.machine(machine.get()) .requiringZeroAndReturningStdout() .summary("group-" + CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_HYPHEN, type)) .environmentVariables(serializer.serialize(env)); Task<String> taskI = DynamicTasks.submit(task.newTask(), target); if (highlight) { highlightAction("Run at "+machine.get().getAddress().getHostAddress(), taskI);
Integer checkExists = DynamicTasks.queue(SshEffectorTasks.ssh("ls \""+brooklynGlobalPropertiesRemotePath+"\"").allowingNonZeroExitCode()).get(); boolean doUpload = true; if (checkExists==0) { DynamicTasks.queueIfPossible( SshEffectorTasks.ssh(cmd).summary("Bespoke BrooklynNode customization script") .requiringExitCodeZero() ) .orSubmitAndBlock(getEntity());
"cd ${BASE_DIR}", "echo BASE_DIR_RESULT':'`pwd`:BASE_DIR_RESULT") .environmentVariable("BASE_DIR", base) .requiringExitCodeZero() .summary("initializing on-box base dir "+base).newTask(); DynamicTasks.queueIfPossible(baseTask).orSubmitAsync(entity); resolvedBase = Strings.getFragmentBetween(baseTask.block().getStdout(), "BASE_DIR_RESULT:", ":BASE_DIR_RESULT");
if (DynamicTasks.queue(SshEffectorTasks.ssh(sudoAsUser("postgres", "ls "+getInstallDir())).allowingNonZeroExitCode() .summary("check postgres user can access install dir")).asTask().getUnchecked()!=0) { log.info("Postgres install dir "+getInstallDir()+" for "+getEntity()+" is not accessible to user 'postgres'; " + "using "+altInstallDir+" instead"); String newRunDir = Urls.mergePaths(altTarget, "apps", getEntity().getApplication().getId(), getEntity().getId()); if (DynamicTasks.queue(SshEffectorTasks.ssh("ls "+altInstallDir+"/pg_ctl").allowingNonZeroExitCode() .summary("check whether "+altInstallDir+" is set up")).asTask().getUnchecked()==0) { "ln -s "+altInstallDir+" "+getInstallDir(), "mkdir -p " + newRunDir, "chown -R postgres:postgres "+altTarget).runAsRoot().requiringExitCodeZero() .summary("move install dir from user to postgres owned space"));
@Override public void customize() { DynamicTasks.queue(SshEffectorTasks.ssh(sudoAsUser("postgres", "/etc/init.d/postgresql stop")).allowingNonZeroExitCode()).get();
"ssh-keygen -t rsa -N '' -f $PRIVATE_KEY -C " + dumpId + " > /dev/null", "cat $PRIVATE_KEY.pub") .environmentVariable("RUN_DIR", sourceRunDir) .machine(sourceMachine) .summary("generate private key for slave access") .requiringZeroAndReturningStdout()) .asTask(); dest.getAttribute(MySqlNode.RUN_DIR), dumpId)) .environmentVariable("RUN_DIR", sourceRunDir) .machine(sourceMachine) .summary("copy database dump to slave") .newTask(); "cd $RUN_DIR", "rm " + privateKeyFile) .environmentVariable("RUN_DIR", sourceRunDir) .machine(sourceMachine) .summary("remove private key")); .machine(destMachine) .summary("remove private key from authorized_keys")).asTask();
tb.add(SshEffectorTasks.ssh(installCmd).summary("renaming cookbook dir").requiringExitCodeZero().newTask());
@Override public void customize() { copyDatabaseConfigScript(); newScript(CUSTOMIZING) .updateTaskAndFailOnNonZeroResultCode() .body.append( "chmod 600 "+getConfigFile(), getBaseDir()+"/scripts/mysql_install_db "+ "--basedir="+getBaseDir()+" --datadir="+getDataDir()+" "+ "--defaults-file="+getConfigFile()) .execute(); // launch, then we will configure it launch(); CountdownTimer timer = Duration.seconds(20).countdownTimer(); boolean hasCreationScript = copyDatabaseCreationScript(); timer.waitForExpiryUnchecked(); DynamicTasks.queue( SshEffectorTasks.ssh( "cd "+getRunDir(), getBaseDir()+"/bin/mysqladmin --defaults-file="+getConfigFile()+" --password= password "+getPassword() ).summary("setting password")); if (hasCreationScript) executeScriptFromInstalledFileAsync("creation-script.sql"); // not sure necessary to stop then subsequently launch, but seems safest // (if skipping, use a flag in launch to indicate we've just launched it) stop(); }
tb.add(SshEffectorTasks.ssh(installCmd).summary("renaming cookbook dir").requiringExitCodeZero().newTask());
.requiringZeroAndReturningStdout() .environmentVariables(serializer.serialize(env));
/** task which returns 0 if pid in the given file is running; * method accepts wildcards so long as they match a single file on the remote end * <p> * returns 1 if no matching file, * 1 if matching file but no matching process, * and 2 if 2+ matching files */ public static SshEffectorTaskFactory<Integer> codePidFromFileRunning(final String pidFile) { return ssh(BashCommands.chain( // this fails, but isn't an error BashCommands.requireTest("-f "+pidFile, "The PID file "+pidFile+" does not exist."), // this fails and logs an error picked up later BashCommands.requireTest("`ls "+pidFile+" | wc -w` -eq 1", "ERROR: there are multiple matching PID files"), // this fails and logs an error picked up later BashCommands.require("cat "+pidFile, "ERROR: the PID file "+pidFile+" cannot be read (permissions?)."), // finally check the process "ps -p `cat "+pidFile+"`")).summary("PID file "+pidFile+" is-running check (exit code)") .allowingNonZeroExitCode() .addCompletionListener(new Function<ProcessTaskWrapper<?>,Void>() { @Override public Void apply(ProcessTaskWrapper<?> input) { if (input.getStderr().contains("ERROR:")) throw new IllegalStateException("Invalid or inaccessible PID filespec: "+pidFile); return null; } }); }
@Override protected String startProcessesAtMachine(Supplier<MachineLocation> machineS) { DynamicTasks.queue( SshEffectorTasks.ssh( "mkdir "+dir(entity), "cd "+dir(entity), BashCommands.downloadToStdout(downloadUrl(entity, isLocalhost(machineS)))+" | tar xvz" ).summary("download mysql").returning(SshTasks.returningStdoutLoggingInfo(log, true))); if (isLinux(machineS)) { DynamicTasks.queue(SshEffectorTasks.ssh(BashCommands.installPackage("libaio1"))); } DynamicTasks.queue( SshEffectorTasks.put(".my.cnf") .contents(String.format("[mysqld]\nbasedir=%s/%s\n", dir(entity), installDir(entity, isLocalhost(machineS)))), SshEffectorTasks.ssh( "cd "+dir(entity)+"/*", "./scripts/mysql_install_db", "./support-files/mysql.server start > out.log 2> err.log < /dev/null" ).summary("setup and run mysql").returning(SshTasks.returningStdoutLoggingInfo(log, true))); return "submitted start"; } @Override
@Override public String execCommandTimeout(String command, Duration timeout) { ProcessTaskWrapper<String> task = SshEffectorTasks.ssh(command) .environmentVariables(((AbstractSoftwareProcessSshDriver) getDriver()).getShellEnvironment()) .configure(SshTool.PROP_ALLOCATE_PTY, true) // TODO configure globally .requiringZeroAndReturningStdout() .machine(getMachine()) .summary(command) .newTask(); try { String result = DynamicTasks.queueIfPossible(task) .executionContext(this) .orSubmitAsync() .asTask() .get(timeout); return result; } catch (TimeoutException te) { throw new IllegalStateException("Timed out running command: " + command); } catch (Exception e) { Integer exitCode = task.getExitCode(); LOG.warn("Command failed, return code {}: {}", exitCode == null ? -1 : exitCode, task.getStderr()); throw Exceptions.propagate(e); } }
@Override public String execCommandTimeout(String command, Duration timeout) { AbstractSoftwareProcessSshDriver driver = (AbstractSoftwareProcessSshDriver) getDriver(); if (driver == null) { throw new NullPointerException("No driver for "+this); } ProcessTaskWrapper<String> task = SshEffectorTasks.ssh(command) .environmentVariables(driver.getShellEnvironment()) .requiringZeroAndReturningStdout() .machine(getMachine()) .summary(command) .newTask(); try { String result = DynamicTasks.queueIfPossible(task) .executionContext(this) .orSubmitAsync() .asTask() .get(timeout); return result; } catch (TimeoutException te) { throw new IllegalStateException("Timed out running command: " + command); } catch (Exception e) { Integer exitCode = task.getExitCode(); LOG.warn("Command failed, return code {}: {}", exitCode == null ? -1 : exitCode, task.getStderr()); throw Exceptions.propagate(e); } }
protected boolean tryStopPid() { Integer pid = entity().getAttribute(Attributes.PID); if (pid==null) { if (entity().getAttribute(Attributes.SERVICE_STATE_ACTUAL)==Lifecycle.RUNNING && getPidFile()==null) log.warn("No PID recorded for "+entity()+" when running, with PID file "+getPidFile()+"; skipping kill in "+Tasks.current()); else if (log.isDebugEnabled()) log.debug("No PID recorded for "+entity()+"; skipping ("+entity().getAttribute(Attributes.SERVICE_STATE_ACTUAL)+" / "+getPidFile()+")"); return false; } // allow non-zero exit as process may have already been killed DynamicTasks.queue(SshEffectorTasks.ssh( "kill "+pid, "sleep 5", BashCommands.ok("kill -9 "+pid)).allowingNonZeroExitCode().runAsRoot()).block(); if (DynamicTasks.queue(SshEffectorTasks.isPidRunning(pid).runAsRoot()).get()) { throw new IllegalStateException("Process for "+entity()+" in "+pid+" still running after kill"); } entity().sensors().set(Attributes.PID, null); return true; }
@Override public int execCommandStatusTimeout(String command, Duration timeout) { ProcessTaskWrapper<Object> task = SshEffectorTasks.ssh(command) .environmentVariables(((AbstractSoftwareProcessSshDriver) getDriver()).getShellEnvironment()) .returning(ScriptReturnType.EXIT_CODE) .allowingNonZeroExitCode() .machine(getMachine()) .summary(command) .newTask(); try { Object result = DynamicTasks.queueIfPossible(task) .executionContext(this) .orSubmitAsync() .asTask() .get(timeout); return (Integer) result; } catch (TimeoutException te) { throw new IllegalStateException("Timed out running command: " + command); } catch (Exception e) { Integer exitCode = task.getExitCode(); LOG.warn("Command failed, return code {}: {}", exitCode == null ? -1 : exitCode, task.getStderr()); throw Exceptions.propagate(e); } }
@Test(groups="Live") public void testPostgresStartsAndStops() throws Exception { ChefLiveTestSupport.installBrooklynChefHostedConfig(app); psql = app.createAndManageChild(PostgreSqlSpecs.specChef()); app.start(ImmutableList.of(targetLocation)); Entities.submit(psql, SshEffectorTasks.ssh("ps aux | grep [p]ostgres").requiringExitCodeZero()); SshMachineLocation targetMachine = EffectorTasks.getSshMachine(psql); psql.stop(); try { // if host is still contactable ensure postgres is not running ProcessTaskWrapper<Integer> t = Entities.submit(app, SshEffectorTasks.ssh("ps aux | grep [p]ostgres").machine(targetMachine).allowingNonZeroExitCode()); t.getTask().blockUntilEnded(Duration.TEN_SECONDS); if (!t.isDone()) Assert.fail("Task not finished yet: "+t.getTask()); Assert.assertNotEquals(t.get(), 0, "Task ended with code "+t.get()+"; output: "+t.getStdout() ); } catch (Exception e) { // host has been killed, that is fine log.info("Machine "+targetMachine+" destroyed on stop (expected - "+e+")"); } }
protected boolean tryStopPid() { Integer pid = entity().getAttribute(Attributes.PID); if (pid==null) { if (entity().getAttribute(Attributes.SERVICE_STATE_ACTUAL)==Lifecycle.RUNNING && getPidFile()==null) log.warn("No PID recorded for "+entity()+" when running, with PID file "+getPidFile()+"; skipping kill in "+Tasks.current()); else if (log.isDebugEnabled()) log.debug("No PID recorded for "+entity()+"; skipping ("+entity().getAttribute(Attributes.SERVICE_STATE_ACTUAL)+" / "+getPidFile()+")"); return false; } // allow non-zero exit as process may have already been killed DynamicTasks.queue(SshEffectorTasks.ssh( "kill "+pid, "sleep 5", BashCommands.ok("kill -9 "+pid)).allowingNonZeroExitCode().runAsRoot()).block(); if (DynamicTasks.queue(SshEffectorTasks.isPidRunning(pid).runAsRoot()).get()) { throw new IllegalStateException("Process for "+entity()+" in "+pid+" still running after kill"); } entity().sensors().set(Attributes.PID, null); return true; }
@Override protected String stopProcessesAtMachine() { // TODO Where is best place to set? // Really should set this with a Feed that checks pid periodically. entity().sensors().set(Attributes.SERVICE_UP, false); Integer pid = entity().getAttribute(Attributes.PID); if (pid==null) { log.info("mysql not running"); return "No pid -- is it running?"; } DynamicTasks.queue(SshEffectorTasks.ssh( "cd "+dir(entity)+"/*", "./support-files/mysql.server stop" ).summary("stop mysql")); return "submitted stop"; } }.attachLifecycleEffectors(entity);