blob: 376577b7b553fbf4bdaf95352802d6e7db7561e1 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.brooklyn.entity.software.base;
import static org.apache.brooklyn.util.JavaGroovyEquivalents.elvis;
import static org.apache.brooklyn.util.JavaGroovyEquivalents.groovyTruth;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.brooklyn.api.entity.EntityLocal;
import org.apache.brooklyn.api.entity.drivers.downloads.DownloadResolver;
import org.apache.brooklyn.config.ConfigKey;
import org.apache.brooklyn.core.BrooklynLogging;
import org.apache.brooklyn.core.effector.EffectorTasks;
import org.apache.brooklyn.core.effector.ssh.SshEffectorTasks;
import org.apache.brooklyn.core.entity.Attributes;
import org.apache.brooklyn.core.entity.BrooklynConfigKeys;
import org.apache.brooklyn.core.entity.Entities;
import org.apache.brooklyn.core.entity.EntityInternal;
import org.apache.brooklyn.core.feed.ConfigToAttributes;
import org.apache.brooklyn.core.mgmt.BrooklynTaskTags;
import org.apache.brooklyn.entity.software.base.lifecycle.NaiveScriptRunner;
import org.apache.brooklyn.entity.software.base.lifecycle.ScriptHelper;
import org.apache.brooklyn.location.ssh.SshMachineLocation;
import org.apache.brooklyn.util.core.internal.ssh.SshTool;
import org.apache.brooklyn.util.core.internal.ssh.sshj.SshjTool;
import org.apache.brooklyn.util.core.json.ShellEnvironmentSerializer;
import org.apache.brooklyn.util.core.mutex.WithMutexes;
import org.apache.brooklyn.util.core.task.DynamicTasks;
import org.apache.brooklyn.util.core.task.Tasks;
import org.apache.brooklyn.util.core.task.system.ProcessTaskWrapper;
import org.apache.brooklyn.util.exceptions.Exceptions;
import org.apache.brooklyn.util.os.Os;
import org.apache.brooklyn.util.ssh.BashCommands;
import org.apache.brooklyn.util.stream.KnownSizeInputStream;
import org.apache.brooklyn.util.stream.Streams;
import org.apache.brooklyn.util.text.StringPredicates;
import org.apache.brooklyn.util.text.Strings;
import org.apache.brooklyn.util.time.Duration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Joiner;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
/**
* An abstract SSH implementation of the {@link AbstractSoftwareProcessDriver}.
*
* This provides conveniences for clients implementing the install/customize/launch/isRunning/stop lifecycle
* over SSH. These conveniences include checking whether software is already installed,
* creating/using a PID file for some operations, and reading ssh-specific config from the entity
* to override/augment ssh flags on the session.
*/
public abstract class AbstractSoftwareProcessSshDriver extends AbstractSoftwareProcessDriver implements NaiveScriptRunner {
public static final Logger log = LoggerFactory.getLogger(AbstractSoftwareProcessSshDriver.class);
public static final Logger logSsh = LoggerFactory.getLogger(BrooklynLogging.SSH_IO);
protected volatile DownloadResolver resolver;
@Override
public void prepare() {
// Check if we should create a download resolver?
String downloadUrl = getEntity().config().get(SoftwareProcess.DOWNLOAD_URL);
if (Strings.isNonEmpty(downloadUrl)) {
resolver = Entities.newDownloader(this);
String formatString = getArchiveNameFormat();
if (Strings.isNonEmpty(formatString)) {
setExpandedInstallDir(Os.mergePaths(getInstallDir(), resolver.getUnpackedDirectoryName(String.format(formatString, getVersion()))));
} else {
setExpandedInstallDir(getInstallDir());
}
}
}
/** include this flag in newScript creation to prevent entity-level flags from being included;
* any SSH-specific flags passed to newScript override flags from the entity,
* and flags from the entity override flags on the location
* (where there aren't conflicts, flags from all three are used however) */
public static final String IGNORE_ENTITY_SSH_FLAGS = SshEffectorTasks.IGNORE_ENTITY_SSH_FLAGS.getName();
public AbstractSoftwareProcessSshDriver(EntityLocal entity, SshMachineLocation machine) {
super(entity, machine);
// FIXME this assumes we own the location, and causes warnings about configuring location after deployment;
// better would be to wrap the ssh-execution-provider to supply these flags
if (getSshFlags()!=null && !getSshFlags().isEmpty())
machine.configure(getSshFlags());
// ensure these are set using the routines below, not a global ConfigToAttributes.apply()
getInstallDir();
getRunDir();
}
/** returns location (tighten type, since we know it is an ssh machine location here) */
@Override
public SshMachineLocation getLocation() {
return (SshMachineLocation) super.getLocation();
}
public SshMachineLocation getMachine() { return getLocation(); }
public String getHostname() { return entity.getAttribute(Attributes.HOSTNAME); }
public String getAddress() { return entity.getAttribute(Attributes.ADDRESS); }
public String getSubnetHostname() { return entity.getAttribute(Attributes.SUBNET_HOSTNAME); }
public String getSubnetAddress() { return entity.getAttribute(Attributes.SUBNET_ADDRESS); }
protected Map<String, Object> getSshFlags() {
return SshEffectorTasks.getSshFlags(getEntity(), getMachine());
}
/**
* @deprecated since 0.10.0 This method will become private in a future release.
*/
@Deprecated
public int execute(String command, String summaryForLogging) {
return execute(ImmutableList.of(command), summaryForLogging);
}
/**
* @deprecated since 0.10.0 This method will become private in a future release.
*/
@Override
@Deprecated
public int execute(List<String> script, String summaryForLogging) {
return execute(Maps.newLinkedHashMap(), script, summaryForLogging);
}
/**
* @deprecated since 0.10.0 This method will become private in a future release.
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
@Deprecated
public int execute(Map flags2, List<String> script, String summaryForLogging) {
// TODO replace with SshEffectorTasks.ssh ?; remove the use of flags
// TODO log the stdin/stdout/stderr upon error
Map flags = Maps.newLinkedHashMap();
if (!flags2.containsKey(IGNORE_ENTITY_SSH_FLAGS)) {
flags.putAll(getSshFlags());
}
flags.putAll(flags2);
Map<String, String> environment = (Map<String, String>) flags.get("env");
if (environment == null) {
// Important only to call getShellEnvironment() if env was not supplied; otherwise it
// could cause us to resolve config (e.g. block for attributeWhenReady) too early.
environment = getShellEnvironment();
}
if (Tasks.current()!=null) {
// attach tags here, as well as in ScriptHelper, because they may have just been read from the driver
if (environment!=null) {
Tasks.addTagDynamically(BrooklynTaskTags.tagForEnvStream(BrooklynTaskTags.STREAM_ENV, environment));
}
if (BrooklynTaskTags.stream(Tasks.current(), BrooklynTaskTags.STREAM_STDIN)==null) {
Tasks.addTagDynamically(BrooklynTaskTags.tagForStreamSoft(BrooklynTaskTags.STREAM_STDIN,
Streams.byteArrayOfString(Strings.join(script, "\n"))));
}
if (BrooklynTaskTags.stream(Tasks.current(), BrooklynTaskTags.STREAM_STDOUT)==null) {
ByteArrayOutputStream stdout = new ByteArrayOutputStream();
Tasks.addTagDynamically(BrooklynTaskTags.tagForStreamSoft(BrooklynTaskTags.STREAM_STDOUT, stdout));
ByteArrayOutputStream stderr = new ByteArrayOutputStream();
Tasks.addTagDynamically(BrooklynTaskTags.tagForStreamSoft(BrooklynTaskTags.STREAM_STDERR, stderr));
flags.put("out", stdout);
flags.put("err", stderr);
}
}
if (!flags.containsKey("logPrefix")) flags.put("logPrefix", ""+entity.getId()+"@"+getLocation().getDisplayName());
return getMachine().execScript(flags, summaryForLogging, script, environment);
}
@Override
public void copyPreInstallResources() {
final WithMutexes mutexSupport = getLocation().mutexes();
String mutexId = "installation lock at host";
mutexSupport.acquireMutex(mutexId, "pre-installation lock at host for files and templates");
try {
super.copyPreInstallResources();
} catch (Exception e) {
log.warn("Error copying pre-install resources", e);
throw Exceptions.propagate(e);
} finally {
mutexSupport.releaseMutex(mutexId);
}
}
@Override
public void copyInstallResources() {
final WithMutexes mutexSupport = getLocation().mutexes();
String mutexId = "installation lock at host";
mutexSupport.acquireMutex(mutexId, "installation lock at host for files and templates");
try {
super.copyInstallResources();
} catch (Exception e) {
log.warn("Error copying install resources", e);
throw Exceptions.propagate(e);
} finally {
mutexSupport.releaseMutex(mutexId);
}
}
@Override
public void copyCustomizeResources() {
final WithMutexes mutexSupport = getLocation().mutexes();
String mutexId = "installation lock at host";
mutexSupport.acquireMutex(mutexId, "installation lock at host for files and templates");
try {
super.copyCustomizeResources();
} catch (Exception e) {
log.warn("Error copying customize resources", e);
throw Exceptions.propagate(e);
} finally {
mutexSupport.releaseMutex(mutexId);
}
}
private void executeSuccessfully(ConfigKey<String> configKey, String label) {
if(Strings.isNonBlank(getEntity().getConfig(configKey))) {
log.debug("Executing {} on entity {}", label, entity.getDisplayName());
int result = execute(ImmutableList.of(getEntity().getConfig(configKey)), label);
if (0 != result) {
log.debug("Executing {} failed with return code {}", label, result);
throw new IllegalStateException("commands for " + configKey.getName() + " failed with return code " + result);
}
}
}
@Override
public void runPreInstallCommand() {
executeSuccessfully(BrooklynConfigKeys.PRE_INSTALL_COMMAND, "running pre-install commands");
}
@Override
public void runPostInstallCommand() {
executeSuccessfully(BrooklynConfigKeys.POST_INSTALL_COMMAND, "running post-install commands");
}
@Override
public void runPreCustomizeCommand() {
executeSuccessfully(BrooklynConfigKeys.PRE_CUSTOMIZE_COMMAND, "running pre-customize commands");
}
@Override
public void runPostCustomizeCommand() {
executeSuccessfully(BrooklynConfigKeys.POST_CUSTOMIZE_COMMAND, "running post-customize commands");
}
@Override
public void runPreLaunchCommand() {
executeSuccessfully(BrooklynConfigKeys.PRE_LAUNCH_COMMAND, "running pre-launch commands");
}
@Override
public void runPostLaunchCommand() {
executeSuccessfully(BrooklynConfigKeys.POST_LAUNCH_COMMAND, "running post-launch commands");
}
/**
* @param sshFlags Extra flags to be used when making an SSH connection to the entity's machine.
* If the map contains the key {@link #IGNORE_ENTITY_SSH_FLAGS} then only the
* given flags are used. Otherwise, the given flags are combined with (and take
* precendence over) the flags returned by {@link #getSshFlags()}.
* @param source URI of file to copy, e.g. file://.., http://.., classpath://..
* @param target Destination on server, relative to {@link #getRunDir()} if not absolute path
* @param createParentDir Whether to create the parent target directory, if it doesn't already exist
* @return The exit code of the SSH command run
*/
@Override
@SuppressWarnings({ "rawtypes", "unchecked" })
public int copyResource(Map<Object,Object> sshFlags, String source, String target, boolean createParentDir) {
// TODO use SshTasks.put instead, better logging
Map flags = Maps.newLinkedHashMap();
if (!sshFlags.containsKey(IGNORE_ENTITY_SSH_FLAGS)) {
flags.putAll(getSshFlags());
}
flags.putAll(sshFlags);
String destination = Os.isAbsolutish(target) ? target : Os.mergePathsUnix(getRunDir(), target);
if (createParentDir) {
// don't use File.separator because it's remote machine's format, rather than local machine's
int lastSlashIndex = destination.lastIndexOf("/");
String parent = (lastSlashIndex > 0) ? destination.substring(0, lastSlashIndex) : null;
if (parent != null) {
getMachine().execCommands("createParentDir", ImmutableList.of("mkdir -p "+parent));
}
}
int result = getMachine().installTo(resource, flags, source, destination);
if (result == 0) {
if (log.isDebugEnabled()) {
log.debug("Copied file for {}: {} to {} - result {}", new Object[] { entity, source, destination, result });
}
}
return result;
}
/**
* Input stream will be closed automatically.
* <p>
* If using {@link SshjTool} usage, consider using {@link KnownSizeInputStream} to avoid having
* to write out stream once to find its size!
*
* @see #copyResource(Map, String, String) for parameter descriptions.
*/
@Override
@SuppressWarnings({ "rawtypes", "unchecked" })
public int copyResource(Map<Object,Object> sshFlags, InputStream source, String target, boolean createParentDir) {
Map flags = Maps.newLinkedHashMap();
if (!sshFlags.containsKey(IGNORE_ENTITY_SSH_FLAGS)) {
flags.putAll(getSshFlags());
}
flags.putAll(sshFlags);
String destination = Os.isAbsolutish(target) ? target : Os.mergePathsUnix(getRunDir(), target);
if (createParentDir) {
// don't use File.separator because it's remote machine's format, rather than local machine's
int lastSlashIndex = destination.lastIndexOf("/");
String parent = (lastSlashIndex > 0) ? destination.substring(0, lastSlashIndex) : null;
if (parent != null) {
getMachine().execCommands("createParentDir", ImmutableList.of("mkdir -p "+parent));
}
}
// TODO SshMachineLocation.copyTo currently doesn't log warn on non-zero or set blocking details
// (because delegated to by installTo, for multiple calls). So do it here for now.
int result;
String prevBlockingDetails = Tasks.setBlockingDetails("copying resource to server at "+destination);
try {
result = getMachine().copyTo(flags, source, destination);
} finally {
Tasks.setBlockingDetails(prevBlockingDetails);
}
if (result == 0) {
log.debug("copying stream complete; {} on {}", new Object[] { destination, getMachine() });
} else {
log.warn("copying stream failed; {} on {}: {}", new Object[] { destination, getMachine(), result });
}
return result;
}
public void checkNoHostnameBug() {
try {
ProcessTaskWrapper<Integer> hostnameTask = DynamicTasks.queue(SshEffectorTasks.ssh("echo FOREMARKER; hostname; echo AFTMARKER")).block();
String stdout = Strings.getFragmentBetween(hostnameTask.getStdout(), "FOREMARKER", "AFTMARKER");
if (hostnameTask.getExitCode() == 0 && Strings.isNonBlank(stdout)) {
String hostname = stdout.trim();
if (hostname.equals("(none)")) {
String newHostname = "br-"+getEntity().getId().toLowerCase();
log.info("Detected no-hostname bug with hostname "+hostname+" for "+getEntity()+"; renaming "+getMachine()+" to hostname "+newHostname);
DynamicTasks.queue(SshEffectorTasks.ssh(BashCommands.setHostname(newHostname, null))).block();
}
} else {
log.debug("Hostname could not be determined for location "+EffectorTasks.findSshMachine()+"; not doing no-hostname bug check");
}
} catch (Exception e) {
Exceptions.propagateIfFatal(e);
log.warn("Error checking/fixing no-hostname bug (continuing): "+e, e);
}
}
public static final String INSTALLING = "installing";
public static final String CUSTOMIZING = "customizing";
public static final String LAUNCHING = "launching";
public static final String CHECK_RUNNING = "check-running";
public static final String STOPPING = "stopping";
public static final String KILLING = "killing";
public static final String RESTARTING = "restarting";
public static final String PID_FILENAME = "pid.txt";
static final String INSTALL_DIR_ENV_VAR = "INSTALL_DIR";
static final String RUN_DIR_ENV_VAR = "RUN_DIR";
/* Flags */
/**
* Use a PID file, created by <em>launching</em>, and reading it for <em>check-running</em>,
* <em>stopping</em> and <em>killing</em>. The value can be <em>true</em> or a path to a file to
* use; either relative to <em>RUN_DIR</em> or an absolute path.
*/
public static final String USE_PID_FILE = "usePidFile";
/**
* Define the process owner if not the same as the brooklyn user. Both <em>stopping</em> and
* <em>killing</em> will sudo to this user before issuing the <code>kill</code> command. Only valid
* if <em>USE_PID_FILE</em> is also set.
*/
public static final String PROCESS_OWNER = "processOwner";
/**
* Marks the script as having a customised setup, to prevent the default headers and footers being
* added to the list of commands.
*/
public static final String NON_STANDARD_LAYOUT = "nonStandardLayout";
/**
* Prevents creation of the <code>$INSTALL_DIR/BROOKLYN</code> marker file after <em>installing</em>
* phase finishes, to allow further installation phases to execute.
*/
public static final String INSTALL_INCOMPLETE = "installIncomplete";
/**
* Enable shell debugging output via <code>set -x</code> prepended to the command header.
*/
public static final String DEBUG = "debug";
/** Permitted flags for {@link #newScript(Map, String)}. */
public static final List<String> VALID_FLAGS =
ImmutableList.of(USE_PID_FILE, PROCESS_OWNER, NON_STANDARD_LAYOUT, INSTALL_INCOMPLETE, DEBUG);
/** @see #newScript(Map, String) */
protected ScriptHelper newScript(String phase) {
return newScript(Maps.<String, Object>newLinkedHashMap(), phase);
}
/**
* Sets up a {@link ScriptHelper} to generate a script that controls the given phase
* (<em>check-running</em>, <em>launching</em> etc.) including default header and
* footer commands.
* <p>
* Supported flags:
* <ul>
* <li><strong>usePidFile</strong> - <em>true</em> or <em>filename</em> to save and retrieve the PID
* <li><strong>processOwner</strong> - <em>username</em> that owns the running process
* <li><strong>nonStandardLayout</strong> - <em>true</em> to omit all default commands
* <li><strong>installIncomplete</strong> - <em>true</em> to prevent marking complete
* <li><strong>debug</strong> - <em>true</em> to enable shell debug output
* </li>
*
* @param flags a {@link Map} of flags to control script generation
* @param phase the phase to create the ScriptHelper for
*
* @see #newScript(String)
* @see #USE_PID_FILE
* @see #PROCESS_OWNER
* @see #NON_STANDARD_LAYOUT
* @see #INSTALL_INCOMPLETE
* @see #DEBUG
*/
protected ScriptHelper newScript(Map<String, ?> flags, String phase) {
if (!Entities.isManaged(getEntity()))
throw new IllegalStateException(getEntity()+" is no longer managed; cannot create script to run here ("+phase+")");
if (!Iterables.all(flags.keySet(), StringPredicates.equalToAny(VALID_FLAGS))) {
throw new IllegalArgumentException("Invalid flags passed: " + flags);
}
ScriptHelper s = new ScriptHelper(this, phase+" "+elvis(entity,this));
if (!groovyTruth(flags.get(NON_STANDARD_LAYOUT))) {
if (groovyTruth(flags.get(DEBUG))) {
s.header.prepend("set -x");
}
if (INSTALLING.equals(phase)) {
// mutexId should be global because otherwise package managers will contend with each other
final String mutexId = "installation lock at host";
s.useMutex(getLocation().mutexes(), mutexId, "installing "+elvis(entity,this));
s.header.append(
"export "+INSTALL_DIR_ENV_VAR+"=\""+getInstallDir()+"\"",
"mkdir -p $"+INSTALL_DIR_ENV_VAR,
"cd $"+INSTALL_DIR_ENV_VAR,
"test -f BROOKLYN && exit 0"
);
if (!groovyTruth(flags.get(INSTALL_INCOMPLETE))) {
s.footer.append("date > $INSTALL_DIR/BROOKLYN");
}
// don't set vars during install phase, prevent dependency resolution
s.environmentVariablesReset();
}
if (ImmutableSet.of(CUSTOMIZING, LAUNCHING, CHECK_RUNNING, STOPPING, KILLING, RESTARTING).contains(phase)) {
s.header.append(
"export "+INSTALL_DIR_ENV_VAR+"=\""+getInstallDir()+"\"",
"export "+RUN_DIR_ENV_VAR+"=\""+getRunDir()+"\"",
"mkdir -p $"+RUN_DIR_ENV_VAR,
"cd $"+RUN_DIR_ENV_VAR
);
}
}
if (ImmutableSet.of(LAUNCHING, RESTARTING).contains(phase)) {
s.failIfBodyEmpty();
}
if (ImmutableSet.of(STOPPING, KILLING).contains(phase)) {
// stopping and killing allowed to have empty body if pid file set
if (!groovyTruth(flags.get(USE_PID_FILE)))
s.failIfBodyEmpty();
}
if (ImmutableSet.of(INSTALLING, LAUNCHING).contains(phase)) {
s.updateTaskAndFailOnNonZeroResultCode();
}
if (phase.equalsIgnoreCase(CHECK_RUNNING)) {
s.setInessential();
s.setTransient();
s.setFlag(SshTool.PROP_CONNECT_TIMEOUT, Duration.TEN_SECONDS.toMilliseconds());
s.setFlag(SshTool.PROP_SESSION_TIMEOUT, Duration.THIRTY_SECONDS.toMilliseconds());
s.setFlag(SshTool.PROP_SSH_TRIES, 1);
}
if (groovyTruth(flags.get(USE_PID_FILE))) {
Object usePidFile = flags.get(USE_PID_FILE);
String pidFile = (usePidFile instanceof CharSequence ? usePidFile : Os.mergePathsUnix(getRunDir(), PID_FILENAME)).toString();
String processOwner = (String) flags.get(PROCESS_OWNER);
if (LAUNCHING.equals(phase)) {
entity.sensors().set(SoftwareProcess.PID_FILE, pidFile);
s.footer.prepend("echo $! > "+pidFile);
} else if (CHECK_RUNNING.equals(phase)) {
// old method, for supplied service, or entity.id
// "ps aux | grep ${service} | grep \$(cat ${pidFile}) > /dev/null"
// new way, preferred?
if (processOwner != null) {
s.body.append(
BashCommands.sudoAsUser(processOwner, "test -f "+pidFile) + " || exit 1",
"ps -p $(" + BashCommands.sudoAsUser(processOwner, "cat "+pidFile) + ")"
);
} else {
s.body.append(
"test -f "+pidFile+" || exit 1",
"ps -p `cat "+pidFile+"`"
);
}
// no pid, not running; 1 is not running
s.requireResultCode(Predicates.or(Predicates.equalTo(0), Predicates.equalTo(1)));
} else if (STOPPING.equals(phase)) {
String stopCommand = Joiner.on('\n').join("PID=$(cat "+pidFile + ")",
"test -n \"$PID\" || exit 0",
"SIGTERM_USED=\"\"",
"for i in $(seq 1 16); do",
" if ps -p $PID > /dev/null ; then",
" kill $PID",
" echo Attempted to stop PID $PID by sending SIGTERM.",
" else",
" echo Process $PID stopped successfully.",
" SIGTERM_USED=\"true\"",
" break",
" fi",
" sleep 1",
"done",
"if test -z $SIGTERM_USED; then",
" kill -9 $PID",
" echo Sent SIGKILL to $PID",
"fi",
"rm -f " + pidFile);
if (processOwner != null) {
s.body.append(BashCommands.sudoAsUser(processOwner, stopCommand));
} else {
s.body.append(stopCommand);
}
} else if (KILLING.equals(phase)) {
if (processOwner != null) {
s.body.append(
"export PID=$(" + BashCommands.sudoAsUser(processOwner, "cat "+pidFile) + ")",
"test -n \"$PID\" || exit 0",
BashCommands.sudoAsUser(processOwner, "kill -9 $PID"),
BashCommands.sudoAsUser(processOwner, "rm -f "+pidFile)
);
} else {
s.body.append(
"export PID=$(cat "+pidFile+")",
"test -n \"$PID\" || exit 0",
"kill -9 $PID",
"rm -f "+pidFile
);
}
} else if (RESTARTING.equals(phase)) {
if (processOwner != null) {
s.footer.prepend(
BashCommands.sudoAsUser(processOwner, "test -f "+pidFile) + " || exit 1",
"ps -p $(" + BashCommands.sudoAsUser(processOwner, "cat "+pidFile) + ") || exit 1"
);
} else {
s.footer.prepend(
"test -f "+pidFile+" || exit 1",
"ps -p $(cat "+pidFile+") || exit 1"
);
}
// no pid, not running; no process; can't restart, 1 is not running
} else {
log.warn(USE_PID_FILE + ": script option not valid for " + s.summary);
}
}
return s;
}
@Override
protected void createDirectory(String directoryName, String summaryForLogging) {
DynamicTasks.queue(SshEffectorTasks.ssh("mkdir -p " + directoryName).summary(summaryForLogging)
.requiringExitCodeZero()).get();
}
public Set<Integer> getPortsUsed() {
Set<Integer> result = Sets.newLinkedHashSet();
result.add(22);
return result;
}
@Override
public void setup() { }
}