diff --git a/antcontrib/src/main/java/org/jclouds/tools/ant/taskdefs/sshjava/SSHJava.java b/antcontrib/src/main/java/org/jclouds/tools/ant/taskdefs/sshjava/SSHJava.java index 9b0695a288..28c450f573 100644 --- a/antcontrib/src/main/java/org/jclouds/tools/ant/taskdefs/sshjava/SSHJava.java +++ b/antcontrib/src/main/java/org/jclouds/tools/ant/taskdefs/sshjava/SSHJava.java @@ -47,7 +47,7 @@ import org.apache.tools.ant.types.Environment; import org.apache.tools.ant.types.FileSet; import org.apache.tools.ant.types.Path; import org.apache.tools.ant.types.Environment.Variable; -import org.jclouds.scriptbuilder.InitBuilder; +import org.jclouds.scriptbuilder.InitScript; import org.jclouds.scriptbuilder.domain.OsFamily; import org.jclouds.scriptbuilder.domain.ShellToken; import org.jclouds.scriptbuilder.domain.Statement; @@ -237,13 +237,13 @@ public class SSHJava extends Java { private int sshexecRedirectStreams(Statement statement) throws IOException { exec.setStreamHandler(redirector.createHandler()); log("starting java as:\n" + statement.render(osFamily), Project.MSG_VERBOSE); - int rc; + int exitStatus; try { - rc = sshexec(statement.render(osFamily)); + exitStatus = sshexec(statement.render(osFamily)); } finally { redirector.complete(); } - return rc; + return exitStatus; } private void mkdirAndCopyTo(String destination, Iterable sets) { @@ -373,8 +373,8 @@ public class SSHJava extends Java { Joiner.on(' ').join(commandLine.getJavaCommand().getArguments())); } - InitBuilder testInitBuilder = new InitBuilder(id, basedir, basedir, envVariables, - ImmutableList. of(Statements.interpret( commandBuilder.toString()))); + InitScript testInitBuilder = InitScript.builder().name(id).home(basedir).exportVariables(envVariables) + .run(Statements.interpret( commandBuilder.toString())).build(); return testInitBuilder.render(osFamily); } diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/EBSBootEC2ClientLiveTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/EBSBootEC2ClientLiveTest.java index 04819b55a1..077cf7953b 100644 --- a/apis/ec2/src/test/java/org/jclouds/ec2/EBSBootEC2ClientLiveTest.java +++ b/apis/ec2/src/test/java/org/jclouds/ec2/EBSBootEC2ClientLiveTest.java @@ -40,6 +40,8 @@ import org.jclouds.domain.LoginCredentials; import org.jclouds.ec2.domain.Attachment; import org.jclouds.ec2.domain.BlockDevice; import org.jclouds.ec2.domain.Image; +import org.jclouds.ec2.domain.Image.Architecture; +import org.jclouds.ec2.domain.Image.ImageType; import org.jclouds.ec2.domain.InstanceState; import org.jclouds.ec2.domain.InstanceType; import org.jclouds.ec2.domain.IpProtocol; @@ -49,8 +51,6 @@ import org.jclouds.ec2.domain.RootDeviceType; import org.jclouds.ec2.domain.RunningInstance; import org.jclouds.ec2.domain.Snapshot; import org.jclouds.ec2.domain.Volume; -import org.jclouds.ec2.domain.Image.Architecture; -import org.jclouds.ec2.domain.Image.ImageType; import org.jclouds.ec2.domain.Volume.InstanceInitiatedShutdownBehavior; import org.jclouds.ec2.predicates.InstanceStateRunning; import org.jclouds.ec2.predicates.InstanceStateStopped; @@ -65,9 +65,8 @@ import org.jclouds.net.IPSocket; import org.jclouds.predicates.RetryablePredicate; import org.jclouds.predicates.SocketOpen; import org.jclouds.rest.RestContextFactory; -import org.jclouds.scriptbuilder.InitBuilder; +import org.jclouds.scriptbuilder.InitScript; import org.jclouds.scriptbuilder.domain.OsFamily; -import org.jclouds.scriptbuilder.domain.Statement; import org.jclouds.scriptbuilder.domain.Statements; import org.jclouds.ssh.SshClient; import org.jclouds.ssh.SshException; @@ -77,7 +76,6 @@ import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; import com.google.common.base.Predicate; -import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; @@ -256,12 +254,12 @@ public class EBSBootEC2ClientLiveTest extends BaseVersionedServiceLiveTest { @BeforeTest void makeScript() { - mkEbsBoot = new InitBuilder( - "mkebsboot",// name of the script - "/tmp",// working directory - "/tmp/logs",// location of stdout.log and stderr.log - ImmutableMap.of("imageDir", "/mnt/tmp", "ebsDevice", "/dev/sdh", "ebsMountPoint", "/mnt/ebs"), - ImmutableList. of(Statements + mkEbsBoot = InitScript.builder() + .name("mkebsboot") + .home("/tmp") + .logDir("/tmp/logs") + .exportVariables(ImmutableMap.of("imageDir", "/mnt/tmp", "ebsDevice", "/dev/sdh", "ebsMountPoint", "/mnt/ebs")) + .run(Statements .interpret( "echo creating a filesystem and mounting the ebs volume", "{md} {varl}IMAGE_DIR{varr} {varl}EBS_MOUNT_POINT{varr}", @@ -275,7 +273,7 @@ public class EBSBootEC2ClientLiveTest extends BaseVersionedServiceLiveTest { "echo copying the local working copy to the ebs mount", "{cd} {varl}IMAGE_DIR{varr}", "tar -cSf - * | tar xf - -C {varl}EBS_MOUNT_POINT{varr}", "echo size of ebs", "du -sk {varl}EBS_MOUNT_POINT{varr}", "echo size of source", "du -sk {varl}IMAGE_DIR{varr}", - "rm -rf {varl}IMAGE_DIR{varr}/*", "umount {varl}EBS_MOUNT_POINT{varr}", "echo " + SCRIPT_END))) + "rm -rf {varl}IMAGE_DIR{varr}/*", "umount {varl}EBS_MOUNT_POINT{varr}", "echo " + SCRIPT_END)).build() .render(OsFamily.UNIX); } diff --git a/compute/src/main/java/org/jclouds/compute/RunScriptData.java b/compute/src/main/java/org/jclouds/compute/RunScriptData.java index 8424804bf6..16f3601b29 100644 --- a/compute/src/main/java/org/jclouds/compute/RunScriptData.java +++ b/compute/src/main/java/org/jclouds/compute/RunScriptData.java @@ -28,7 +28,7 @@ import java.io.IOException; import java.net.URI; import org.jclouds.compute.domain.OperatingSystem; -import org.jclouds.scriptbuilder.InitBuilder; +import org.jclouds.scriptbuilder.InitScript; import org.jclouds.scriptbuilder.domain.Statement; import org.jclouds.scriptbuilder.domain.StatementList; import org.jclouds.scriptbuilder.statements.java.InstallJDK; @@ -71,15 +71,13 @@ public class RunScriptData { } // NOTE do not name this the same as your login user, or the init process may kill you! - public static InitBuilder startJBoss(String configuration) { - return new InitBuilder( - "jboss", - JBOSS_HOME, - JBOSS_HOME, - ImmutableMap.of("jbossHome", JBOSS_HOME), - ImmutableList.of(appendFile(JBOSS_HOME + "/standalone/configuration/standalone-custom.xml", Splitter.on('\n').split(configuration))), - ImmutableList - . of(interpret(new StringBuilder().append("java ").append(' ') + public static InitScript startJBoss(String configuration) { + return InitScript.builder() + .name("jboss") + .home(JBOSS_HOME) + .exportVariables(ImmutableMap.of("jbossHome", JBOSS_HOME)) + .init(appendFile(JBOSS_HOME + "/standalone/configuration/standalone-custom.xml", Splitter.on('\n').split(configuration))) + .run(interpret(new StringBuilder().append("java ").append(' ') .append("-server -Xms128m -Xmx128m -XX:MaxPermSize=128m -Djava.net.preferIPv4Stack=true -XX:+UseFastAccessorMethods -XX:+TieredCompilation -Xverify:none -Dorg.jboss.resolver.warning=true -Dsun.rmi.dgc.client.gcInterval=3600000 -Dsun.rmi.dgc.server.gcInterval=3600000").append(' ') .append("-Djboss.modules.system.pkgs=org.jboss.byteman").append(' ') .append("-Dorg.jboss.boot.log.file=$JBOSS_HOME/standalone/log/boot.log").append(' ') @@ -91,7 +89,7 @@ public class RunScriptData { .append("org.jboss.as.standalone").append(' ') .append("-Djboss.home.dir=$JBOSS_HOME").append(' ') .append("--server-config=standalone-custom.xml") - .toString()))); + .toString())).build(); } // TODO make this a cli option diff --git a/compute/src/main/java/org/jclouds/compute/callables/BlockUntilInitScriptStatusIsZeroThenReturnOutput.java b/compute/src/main/java/org/jclouds/compute/callables/BlockUntilInitScriptStatusIsZeroThenReturnOutput.java index 8ebb35238b..a6f07fbc96 100644 --- a/compute/src/main/java/org/jclouds/compute/callables/BlockUntilInitScriptStatusIsZeroThenReturnOutput.java +++ b/compute/src/main/java/org/jclouds/compute/callables/BlockUntilInitScriptStatusIsZeroThenReturnOutput.java @@ -21,6 +21,7 @@ package org.jclouds.compute.callables; import static com.google.common.base.Preconditions.checkNotNull; import java.util.Date; +import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; @@ -30,22 +31,28 @@ import javax.annotation.Resource; import org.jclouds.Constants; import org.jclouds.compute.domain.ExecResponse; +import org.jclouds.compute.events.StatementOnNodeCompletion; +import org.jclouds.compute.events.StatementOnNodeFailure; import org.jclouds.compute.predicates.ScriptStatusReturnsZero; import org.jclouds.compute.reference.ComputeServiceConstants; import org.jclouds.logging.Logger; import org.jclouds.predicates.RetryablePredicate; -import org.jclouds.scriptbuilder.InitBuilder; +import org.jclouds.scriptbuilder.InitScript; import org.jclouds.ssh.SshClient; +import com.google.common.base.Objects; import com.google.common.base.Predicate; import com.google.common.base.Throwables; +import com.google.common.eventbus.EventBus; +import com.google.common.primitives.Ints; import com.google.common.util.concurrent.AbstractFuture; import com.google.inject.Inject; import com.google.inject.assistedinject.Assisted; import com.google.inject.name.Named; /** - * A future that works in tandem with a task that was invoked by {@link InitBuilder} + * A future that works in tandem with a task that was invoked by + * {@link InitScript} * * @author Adrian Cole */ @@ -60,23 +67,31 @@ public class BlockUntilInitScriptStatusIsZeroThenReturnOutput extends AbstractFu protected Logger logger = Logger.NULL; private final ExecutorService userThreads; + private final EventBus eventBus; private final SudoAwareInitManager commandRunner; - private final RetryablePredicate notRunningAnymore; - private boolean shouldCancel; + public SudoAwareInitManager getCommandRunner() { + return commandRunner; + } + + private final RetryablePredicate notRunningAnymore; @Inject public BlockUntilInitScriptStatusIsZeroThenReturnOutput( - @Named(Constants.PROPERTY_USER_THREADS) ExecutorService userThreads, - ComputeServiceConstants.InitStatusProperties properties, - final ScriptStatusReturnsZero stateRunning, @Assisted final SudoAwareInitManager commandRunner) { - - long retryMaxWait = TimeUnit.DAYS.toMillis(365); // arbitrarily high value, but Long.MAX_VALUE doesn't work! + @Named(Constants.PROPERTY_USER_THREADS) ExecutorService userThreads, EventBus eventBus, + ComputeServiceConstants.InitStatusProperties properties, final ScriptStatusReturnsZero stateRunning, + @Assisted final SudoAwareInitManager commandRunner) { + + long retryMaxWait = TimeUnit.DAYS.toMillis(365); // arbitrarily high + // value, but + // Long.MAX_VALUE doesn't + // work! long retryInitialPeriod = properties.initStatusInitialPeriod; long retryMaxPeriod = properties.initStatusMaxPeriod; - + this.commandRunner = checkNotNull(commandRunner, "commandRunner"); this.userThreads = checkNotNull(userThreads, "userThreads"); + this.eventBus = checkNotNull(eventBus, "eventBus"); this.notRunningAnymore = new RetryablePredicate(new Predicate() { @Override @@ -85,13 +100,13 @@ public class BlockUntilInitScriptStatusIsZeroThenReturnOutput extends AbstractFu } }, retryMaxWait, retryInitialPeriod, retryMaxPeriod, TimeUnit.MILLISECONDS) { /** - * make sure we stop the retry loop if someone cancelled the future, this keeps threads - * from being consumed on dead tasks + * make sure we stop the retry loop if someone cancelled the future, + * this keeps threads from being consumed on dead tasks */ @Override protected boolean atOrAfter(Date end) { - if (shouldCancel) - Throwables.propagate(new TimeoutException("cancelled")); + if (isCancelled()) + Throwables.propagate(new CancellationException("cancelled")); return super.atOrAfter(end); } }; @@ -105,20 +120,28 @@ public class BlockUntilInitScriptStatusIsZeroThenReturnOutput extends AbstractFu } /** - * Submits a thread that will either set the result of the future or the exception that took - * place + * Submits a thread that will either set the result of the future or the + * exception that took place */ public BlockUntilInitScriptStatusIsZeroThenReturnOutput init() { userThreads.submit(new Runnable() { @Override public void run() { try { - boolean complete = notRunningAnymore.apply("status"); - String stdout = commandRunner.runAction("tail").getOutput(); - String stderr = commandRunner.runAction("tailerr").getOutput(); - // TODO make ScriptBuilder save exit status on nuhup - logger.debug("<< complete(%s) status(%s)", commandRunner.getStatement().getInstanceName(), complete); - set(new ExecResponse(stdout, stderr, complete && !shouldCancel ? 0 : -1)); + notRunningAnymore.apply("status"); + String stdout = commandRunner.runAction("stdout").getOutput(); + String stderr = commandRunner.runAction("stderr").getOutput(); + Integer exitStatus = Ints.tryParse(commandRunner.runAction("exitstatus").getOutput().trim()); + ExecResponse exec = new ExecResponse(stdout, stderr, exitStatus == null ? -1 : exitStatus); + if (exitStatus == null) { + Integer pid = Ints.tryParse(commandRunner.runAction("status").getOutput().trim()); + throw new ScriptStillRunningException(String.format("%s still running: pid(%s), last status: %s", + BlockUntilInitScriptStatusIsZeroThenReturnOutput.this, pid, exec), + BlockUntilInitScriptStatusIsZeroThenReturnOutput.this); + } + logger.debug("<< complete(%s) status(%s)", commandRunner.getStatement().getInstanceName(), exitStatus); + set(exec); + } catch (CancellationException e) { } catch (Exception e) { setException(e); } @@ -127,72 +150,54 @@ public class BlockUntilInitScriptStatusIsZeroThenReturnOutput extends AbstractFu return this; } + @Override + protected boolean set(ExecResponse value) { + eventBus.post(new StatementOnNodeCompletion(getCommandRunner().getStatement(), getCommandRunner().getNode(), + value)); + return super.set(value); + } + @Override protected void interruptTask() { logger.debug("<< cancelled(%s)", commandRunner.getStatement().getInstanceName()); - commandRunner.refreshAndRunAction("stop"); - shouldCancel = true; + ExecResponse returnVal = commandRunner.refreshAndRunAction("stop"); + CancellationException e = new CancellationException(String.format( + "cancelled %s on node: %s; stop command had exit status: %s", getCommandRunner().getStatement() + .getInstanceName(), getCommandRunner().getNode().getId(), returnVal)); + eventBus.post(new StatementOnNodeFailure(getCommandRunner().getStatement(), getCommandRunner().getNode(), e)); super.interruptTask(); } @Override public String toString() { - return String.format("running task[%s]", commandRunner); + return Objects.toStringHelper(this).add("commandRunner", commandRunner).toString(); } @Override public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((commandRunner == null) ? 0 : commandRunner.hashCode()); - result = prime * result + ((logger == null) ? 0 : logger.hashCode()); - result = prime * result + ((notRunningAnymore == null) ? 0 : notRunningAnymore.hashCode()); - result = prime * result + (shouldCancel ? 1231 : 1237); - result = prime * result + ((userThreads == null) ? 0 : userThreads.hashCode()); - return result; + return Objects.hashCode(commandRunner); } @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) + public boolean equals(Object o) { + if (o == null) return false; - if (getClass() != obj.getClass()) + if (!o.getClass().equals(getClass())) return false; - BlockUntilInitScriptStatusIsZeroThenReturnOutput other = (BlockUntilInitScriptStatusIsZeroThenReturnOutput) obj; - if (commandRunner == null) { - if (other.commandRunner != null) - return false; - } else if (!commandRunner.equals(other.commandRunner)) - return false; - if (logger == null) { - if (other.logger != null) - return false; - } else if (!logger.equals(other.logger)) - return false; - if (notRunningAnymore == null) { - if (other.notRunningAnymore != null) - return false; - } else if (!notRunningAnymore.equals(other.notRunningAnymore)) - return false; - if (shouldCancel != other.shouldCancel) - return false; - if (userThreads == null) { - if (other.userThreads != null) - return false; - } else if (!userThreads.equals(other.userThreads)) - return false; - return true; + BlockUntilInitScriptStatusIsZeroThenReturnOutput that = BlockUntilInitScriptStatusIsZeroThenReturnOutput.class + .cast(o); + return Objects.equal(this.commandRunner, that.commandRunner); } @Override public ExecResponse get(long timeout, TimeUnit unit) throws InterruptedException, TimeoutException, - ExecutionException { + ExecutionException { try { return super.get(timeout, unit); } catch (TimeoutException e) { - throw new ScriptStillRunningException(timeout, unit, this); + ScriptStillRunningException exception = new ScriptStillRunningException(timeout, unit, this); + exception.initCause(e); + throw exception; } } diff --git a/compute/src/main/java/org/jclouds/compute/callables/InitScriptConfigurationForTasks.java b/compute/src/main/java/org/jclouds/compute/callables/InitScriptConfigurationForTasks.java index ef6eab8804..0394a50b29 100644 --- a/compute/src/main/java/org/jclouds/compute/callables/InitScriptConfigurationForTasks.java +++ b/compute/src/main/java/org/jclouds/compute/callables/InitScriptConfigurationForTasks.java @@ -26,7 +26,7 @@ import java.util.concurrent.atomic.AtomicInteger; import javax.inject.Named; import javax.inject.Singleton; -import org.jclouds.scriptbuilder.InitBuilder; +import org.jclouds.scriptbuilder.InitScript; import com.google.common.base.Supplier; import com.google.inject.Inject; @@ -104,8 +104,8 @@ public class InitScriptConfigurationForTasks { * * @return the naming convention of init scripts. ex. {@code /tmp/init-%s}, noting logs are under * the basedir/%s where %s is the taskName - * @see InitBuilder#getHomeDir - * @see InitBuilder#getLogDir + * @see InitScript#getHomeDir + * @see InitScript#getLogDir */ public String getInitScriptPattern() { return initScriptPattern; diff --git a/compute/src/main/java/org/jclouds/compute/callables/RunScriptOnNodeAsInitScriptUsingSsh.java b/compute/src/main/java/org/jclouds/compute/callables/RunScriptOnNodeAsInitScriptUsingSsh.java index d2a52cfca0..a3e263af23 100644 --- a/compute/src/main/java/org/jclouds/compute/callables/RunScriptOnNodeAsInitScriptUsingSsh.java +++ b/compute/src/main/java/org/jclouds/compute/callables/RunScriptOnNodeAsInitScriptUsingSsh.java @@ -21,19 +21,15 @@ package org.jclouds.compute.callables; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; -import java.util.Collections; - -import javax.annotation.Resource; -import javax.inject.Named; - import org.jclouds.compute.domain.ExecResponse; import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.compute.domain.NodeMetadataBuilder; +import org.jclouds.compute.events.InitScriptOnNodeSubmission; +import org.jclouds.compute.events.StatementOnNodeCompletion; +import org.jclouds.compute.events.StatementOnNodeFailure; import org.jclouds.compute.options.RunScriptOptions; -import org.jclouds.compute.reference.ComputeServiceConstants; import org.jclouds.domain.LoginCredentials; -import org.jclouds.logging.Logger; -import org.jclouds.scriptbuilder.InitBuilder; +import org.jclouds.scriptbuilder.InitScript; import org.jclouds.scriptbuilder.domain.AdminAccessVisitor; import org.jclouds.scriptbuilder.domain.AppendFile; import org.jclouds.scriptbuilder.domain.OsFamily; @@ -45,6 +41,8 @@ import org.jclouds.ssh.SshException; import com.google.common.base.Function; import com.google.common.base.Splitter; +import com.google.common.base.Throwables; +import com.google.common.eventbus.EventBus; import com.google.inject.assistedinject.Assisted; import com.google.inject.assistedinject.AssistedInject; @@ -53,28 +51,18 @@ import com.google.inject.assistedinject.AssistedInject; * @author Adrian Cole */ public class RunScriptOnNodeAsInitScriptUsingSsh extends SudoAwareInitManager implements RunScriptOnNode { - @Resource - @Named(ComputeServiceConstants.COMPUTE_LOGGER) - protected Logger logger = Logger.NULL; - protected final String initFile; - - /** - * @return the absolute path to the file on disk relating to this task. - */ - public String getInitFile() { - return initFile; - } + protected final EventBus eventBus; @AssistedInject - public RunScriptOnNodeAsInitScriptUsingSsh(Function sshFactory, + public RunScriptOnNodeAsInitScriptUsingSsh(Function sshFactory, EventBus eventBus, InitScriptConfigurationForTasks initScriptConfiguration, @Assisted NodeMetadata node, @Assisted Statement script, @Assisted RunScriptOptions options) { super(sshFactory, options.shouldRunAsRoot(), checkNotNull(node, "node"), - checkNotNull(script, "script") instanceof InitBuilder ? InitBuilder.class.cast(script) + initScriptConfiguration, checkNotNull(script, "script") instanceof InitScript ? InitScript.class.cast(script) : createInitScript(checkNotNull(initScriptConfiguration, "initScriptConfiguration"), options .getTaskName(), script)); - this.initFile = String.format(initScriptConfiguration.getInitScriptPattern(), init.getInstanceName()); + this.eventBus = checkNotNull(eventBus, "eventBus"); } @Override @@ -88,19 +76,20 @@ public class RunScriptOnNodeAsInitScriptUsingSsh extends SudoAwareInitManager im checkState(ssh != null, "please call init() before invoking call"); try { ssh.connect(); - return doCall(); + ExecResponse returnVal = doCall(); + eventBus.post(new StatementOnNodeCompletion(init, node, returnVal)); + return returnVal; } finally { if (ssh != null) ssh.disconnect(); } } - public static InitBuilder createInitScript(InitScriptConfigurationForTasks config, String name, Statement script) { + public static InitScript createInitScript(InitScriptConfigurationForTasks config, String name, Statement script) { if (name == null) { name = "jclouds-script-" + config.getAnonymousTaskSuffixSupplier().get(); } - return new InitBuilder(name, config.getBasedir() + "/" + name, config.getBasedir() + "/" + name, Collections - . emptyMap(), Collections.singleton(script)); + return InitScript.builder().name(name).home(config.getBasedir() + "/" + name).run(script).build(); } protected void refreshSshIfNewAdminCredentialsConfigured(AdminAccess input) { @@ -115,34 +104,41 @@ public class RunScriptOnNodeAsInitScriptUsingSsh extends SudoAwareInitManager im } protected ExecResponse doCall() { + eventBus.post(new InitScriptOnNodeSubmission(init, node)); try { - ssh.put(initFile, init.render(OsFamily.UNIX)); - } catch (SshException e) { - // If there's a problem with the sftp configuration, we can try via ssh exec - if (logger.isTraceEnabled()) - logger.warn(e, "<< (%s) problem using sftp [%s], attempting via sshexec", ssh.toString(), e.getMessage()); - else - logger.warn("<< (%s) problem using sftp [%s], attempting via sshexec", ssh.toString(), e.getMessage()); - ssh.disconnect(); - ssh.connect(); - ssh.exec("rm " + initFile); - ssh.exec(Statements.appendFile(initFile, Splitter.on('\n').split(init.render(OsFamily.UNIX)), - AppendFile.MARKER + "_" + init.getInstanceName()).render(OsFamily.UNIX)); - } - - ssh.exec("chmod 755 " + initFile); - setupLinkToInitFile(); - - runAction("init"); - init.getInitStatement().accept(new AdminAccessVisitor() { - - @Override - public void visit(AdminAccess input) { - refreshSshIfNewAdminCredentialsConfigured(input); + try { + ssh.put(initFile, init.render(OsFamily.UNIX)); + } catch (SshException e) { + // If there's a problem with the sftp configuration, we can try via + // ssh exec + if (logger.isTraceEnabled()) + logger.warn(e, "<< (%s) problem using sftp [%s], attempting via sshexec", ssh.toString(), e.getMessage()); + else + logger.warn("<< (%s) problem using sftp [%s], attempting via sshexec", ssh.toString(), e.getMessage()); + ssh.disconnect(); + ssh.connect(); + ssh.exec("rm " + initFile); + ssh.exec(Statements.appendFile(initFile, Splitter.on('\n').split(init.render(OsFamily.UNIX)), + AppendFile.DELIMETER + "_" + init.getInstanceName()).render(OsFamily.UNIX)); } - }); - return runAction("start"); + ssh.exec("chmod 755 " + initFile); + setupLinkToInitFile(); + + runAction("init"); + init.getInitStatement().accept(new AdminAccessVisitor() { + + @Override + public void visit(AdminAccess input) { + refreshSshIfNewAdminCredentialsConfigured(input); + } + + }); + return runAction("start"); + } catch (Throwable e) { + eventBus.post(new StatementOnNodeFailure(init, node, e)); + throw Throwables.propagate(e); + } } protected void setupLinkToInitFile() { diff --git a/compute/src/main/java/org/jclouds/compute/callables/RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilComplete.java b/compute/src/main/java/org/jclouds/compute/callables/RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilComplete.java index d95899ff33..84fa837bd6 100644 --- a/compute/src/main/java/org/jclouds/compute/callables/RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilComplete.java +++ b/compute/src/main/java/org/jclouds/compute/callables/RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilComplete.java @@ -19,7 +19,6 @@ package org.jclouds.compute.callables; import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Preconditions.checkState; import java.util.concurrent.TimeUnit; @@ -27,6 +26,7 @@ import javax.inject.Inject; import org.jclouds.compute.domain.ExecResponse; import org.jclouds.compute.domain.NodeMetadata; +import org.jclouds.compute.events.StatementOnNodeFailure; import org.jclouds.compute.options.RunScriptOptions; import org.jclouds.compute.reference.ComputeServiceConstants.Timeouts; import org.jclouds.scriptbuilder.domain.Statement; @@ -34,6 +34,7 @@ import org.jclouds.ssh.SshClient; import com.google.common.base.Function; import com.google.common.base.Throwables; +import com.google.common.eventbus.EventBus; import com.google.inject.assistedinject.Assisted; /** @@ -46,10 +47,11 @@ public class RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilComplete extends Ru @Inject public RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilComplete( - BlockUntilInitScriptStatusIsZeroThenReturnOutput.Factory statusFactory, Timeouts timeouts, - Function sshFactory, InitScriptConfigurationForTasks initScriptConfiguration, - @Assisted NodeMetadata node, @Assisted Statement script, @Assisted RunScriptOptions options) { - super(sshFactory, initScriptConfiguration, node, script, options); + BlockUntilInitScriptStatusIsZeroThenReturnOutput.Factory statusFactory, Timeouts timeouts, + Function sshFactory, EventBus eventBus, + InitScriptConfigurationForTasks initScriptConfiguration, @Assisted NodeMetadata node, + @Assisted Statement script, @Assisted RunScriptOptions options) { + super(sshFactory, eventBus, initScriptConfiguration, node, script, options); this.statusFactory = checkNotNull(statusFactory, "statusFactory"); this.timeouts = checkNotNull(timeouts, "timeouts"); } @@ -58,16 +60,21 @@ public class RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilComplete extends Ru public ExecResponse doCall() { try { return future().get(timeouts.scriptComplete, TimeUnit.MILLISECONDS); - } catch (Exception e) { - Throwables.propagate(e); - return null; + } catch (Throwable e) { + eventBus.post(new StatementOnNodeFailure(init, node, e)); + throw Throwables.propagate(e); } } public BlockUntilInitScriptStatusIsZeroThenReturnOutput future() { ExecResponse returnVal = super.doCall(); - checkState(returnVal.getExitCode() == 0, String.format("task: %s had non-zero exit status: %s", init - .getInstanceName(), returnVal)); + if (returnVal.getExitStatus() != 0) { + IllegalStateException e = new IllegalStateException(String.format( + "instance: %s on node: %s had non-zero exit status: %s", init.getInstanceName(), getNode().getId(), + returnVal)); + eventBus.post(new StatementOnNodeFailure(init, node, e)); + throw e; + } return statusFactory.create(this).init(); } diff --git a/compute/src/main/java/org/jclouds/compute/callables/RunScriptOnNodeUsingSsh.java b/compute/src/main/java/org/jclouds/compute/callables/RunScriptOnNodeUsingSsh.java index f45b108353..b72d325b70 100644 --- a/compute/src/main/java/org/jclouds/compute/callables/RunScriptOnNodeUsingSsh.java +++ b/compute/src/main/java/org/jclouds/compute/callables/RunScriptOnNodeUsingSsh.java @@ -26,6 +26,9 @@ import javax.inject.Named; import org.jclouds.compute.domain.ExecResponse; import org.jclouds.compute.domain.NodeMetadata; +import org.jclouds.compute.events.StatementOnNodeFailure; +import org.jclouds.compute.events.StatementOnNodeCompletion; +import org.jclouds.compute.events.StatementOnNodeSubmission; import org.jclouds.compute.options.RunScriptOptions; import org.jclouds.compute.reference.ComputeServiceConstants; import org.jclouds.logging.Logger; @@ -36,6 +39,8 @@ import org.jclouds.ssh.SshClient; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Function; import com.google.common.base.Objects; +import com.google.common.base.Throwables; +import com.google.common.eventbus.EventBus; import com.google.inject.assistedinject.Assisted; import com.google.inject.assistedinject.AssistedInject; @@ -51,6 +56,7 @@ public class RunScriptOnNodeUsingSsh implements RunScriptOnNode { protected Logger logger = Logger.NULL; protected final Function sshFactory; + protected final EventBus eventBus; protected final NodeMetadata node; protected final Statement statement; protected final boolean runAsRoot; @@ -58,9 +64,10 @@ public class RunScriptOnNodeUsingSsh implements RunScriptOnNode { protected SshClient ssh; @AssistedInject - public RunScriptOnNodeUsingSsh(Function sshFactory, @Assisted NodeMetadata node, - @Assisted Statement statement, @Assisted RunScriptOptions options) { + public RunScriptOnNodeUsingSsh(Function sshFactory, EventBus eventBus, + @Assisted NodeMetadata node, @Assisted Statement statement, @Assisted RunScriptOptions options) { this.sshFactory = checkNotNull(sshFactory, "sshFactory"); + this.eventBus = checkNotNull(eventBus, "eventBus"); this.node = checkNotNull(node, "node"); this.statement = checkNotNull(statement, "statement"); this.runAsRoot = options.shouldRunAsRoot(); @@ -72,13 +79,20 @@ public class RunScriptOnNodeUsingSsh implements RunScriptOnNode { try { ssh.connect(); ExecResponse returnVal; + eventBus.post(new StatementOnNodeSubmission(statement, node)); String command = (runAsRoot) ? execAsRoot(statement.render(OsFamily.UNIX)) : execScriptAsDefaultUser(statement .render(OsFamily.UNIX)); - returnVal = runCommand(command); + try { + returnVal = runCommand(command); + } catch (Throwable e) { + eventBus.post(new StatementOnNodeFailure(statement, node, e)); + throw Throwables.propagate(e); + } + eventBus.post(new StatementOnNodeCompletion(statement, node, returnVal)); if (logger.isTraceEnabled()) logger.trace("<< %s[%s]", statement, returnVal); else - logger.debug("<< %s(%d)", statement, returnVal.getExitCode()); + logger.debug("<< %s(%d)", statement, returnVal.getExitStatus()); return returnVal; } finally { if (ssh != null) diff --git a/compute/src/main/java/org/jclouds/compute/callables/SudoAwareInitManager.java b/compute/src/main/java/org/jclouds/compute/callables/SudoAwareInitManager.java index 8dbe9cdcbd..e94df32e21 100644 --- a/compute/src/main/java/org/jclouds/compute/callables/SudoAwareInitManager.java +++ b/compute/src/main/java/org/jclouds/compute/callables/SudoAwareInitManager.java @@ -28,7 +28,7 @@ import org.jclouds.compute.domain.ExecResponse; import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.compute.reference.ComputeServiceConstants; import org.jclouds.logging.Logger; -import org.jclouds.scriptbuilder.InitBuilder; +import org.jclouds.scriptbuilder.InitScript; import org.jclouds.ssh.SshClient; import com.google.common.annotations.VisibleForTesting; @@ -47,16 +47,25 @@ public class SudoAwareInitManager { protected Logger computeLogger = Logger.NULL; protected Logger logger = Logger.NULL; protected NodeMetadata node; - protected final InitBuilder init; + protected final String initFile; + protected final InitScript init; protected final boolean runAsRoot; protected final Function sshFactory; protected SshClient ssh; + /** + * @return the absolute path to the file on disk relating to this task. + */ + public String getInitFile() { + return initFile; + } + public SudoAwareInitManager(Function sshFactory, boolean runAsRoot, NodeMetadata node, - InitBuilder init) { + InitScriptConfigurationForTasks initScriptConfiguration, InitScript init) { this.sshFactory = checkNotNull(sshFactory, "sshFactory"); this.runAsRoot = runAsRoot; this.node = checkNotNull(node, "node"); + this.initFile = String.format(initScriptConfiguration.getInitScriptPattern(), init.getInstanceName()); this.init = checkNotNull(init, "init"); } @@ -82,41 +91,44 @@ public class SudoAwareInitManager { : execScriptAsDefaultUser(action); returnVal = runCommand(command); if ("status".equals(action)) - logger.trace("<< %s(%d)", action, returnVal.getExitCode()); + logger.trace("<< %s(%d)", action, returnVal.getExitStatus()); else if (computeLogger.isTraceEnabled()) computeLogger.trace("<< %s[%s]", action, returnVal); else - computeLogger.debug("<< %s(%d)", action, returnVal.getExitCode()); + computeLogger.debug("<< %s(%d)", action, returnVal.getExitStatus()); return returnVal; } ExecResponse runCommand(String command) { - String statement = String.format(">> running [%s] as %s@%s", command.replace( + String statement = String.format("[%s] as %s@%s", command.replace( node.getCredentials().getPassword() != null ? node.getCredentials().getPassword() : "XXXXX", "XXXXX"), ssh .getUsername(), ssh.getHostAddress()); if (command.endsWith("status")) - logger.trace(statement); - else - computeLogger.debug(statement); - return ssh.exec(command); + logger.trace(">> running " + statement); + else + computeLogger.debug(">> running " + statement); + ExecResponse returnVal = ssh.exec(command); + if (!command.endsWith("status")) + checkState(returnVal.getExitStatus() == 0, "error running %s; returnVal !=0: %s", statement, returnVal); + return returnVal; } @VisibleForTesting String execScriptAsRoot(String action) { String command; if (node.getCredentials().identity.equals("root")) { - command = "./" + init.getInstanceName() + " " + action; + command = initFile + " " + action; } else if (node.getCredentials().shouldAuthenticateSudo()) { - command = String.format("echo '%s'|sudo -S ./%s %s", node.getCredentials().getPassword(), - init.getInstanceName(), action); + command = String.format("echo '%s'|sudo -S %s %s", node.getCredentials().getPassword(), + initFile, action); } else { - command = "sudo ./" + init.getInstanceName() + " " + action; + command = "sudo " + initFile + " " + action; } return command; } protected String execScriptAsDefaultUser(String action) { - return "./" + init.getInstanceName() + " " + action; + return initFile + " " + action; } public NodeMetadata getNode() { @@ -125,11 +137,11 @@ public class SudoAwareInitManager { @Override public String toString() { - return Objects.toStringHelper(this).add("node", node).add("name", init.getInstanceName()) + return Objects.toStringHelper(this).add("node", node.getId()).add("name", init.getInstanceName()) .add("runAsRoot", runAsRoot).toString(); } - public InitBuilder getStatement() { + public InitScript getStatement() { return init; } } \ No newline at end of file diff --git a/compute/src/main/java/org/jclouds/compute/domain/ExecResponse.java b/compute/src/main/java/org/jclouds/compute/domain/ExecResponse.java index adc9d4cd67..595d4d30ec 100644 --- a/compute/src/main/java/org/jclouds/compute/domain/ExecResponse.java +++ b/compute/src/main/java/org/jclouds/compute/domain/ExecResponse.java @@ -20,19 +20,21 @@ package org.jclouds.compute.domain; import org.jclouds.compute.config.CustomizationResponse; +import com.google.common.base.Objects; + /** * @author Adrian Cole */ public class ExecResponse implements CustomizationResponse { - private final String error; private final String output; - private final int exitCode; + private final String error; + private final int exitStatus; - public ExecResponse(String output, String error, int exitCode) { + public ExecResponse(String output, String error, int exitStatus) { this.output = output; this.error = error; - this.exitCode = exitCode; + this.exitStatus = exitStatus; } public String getError() { @@ -43,47 +45,38 @@ public class ExecResponse implements CustomizationResponse { return output; } - @Override - public String toString() { - return "[output=" + output + ", error=" + error + ", exitCode=" + exitCode + "]"; + /** + * @see #getExitStatus + */ + @Deprecated + public int getExitCode() { + return exitStatus; + } + + public int getExitStatus() { + return exitStatus; } @Override public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((error == null) ? 0 : error.hashCode()); - result = prime * result + exitCode; - result = prime * result + ((output == null) ? 0 : output.hashCode()); - return result; + return Objects.hashCode(output, error, exitStatus); } @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) + public boolean equals(Object o) { + if (o == null) return false; - if (getClass() != obj.getClass()) + if (!o.getClass().equals(getClass())) return false; - ExecResponse other = (ExecResponse) obj; - if (error == null) { - if (other.error != null) - return false; - } else if (!error.equals(other.error)) - return false; - if (exitCode != other.exitCode) - return false; - if (output == null) { - if (other.output != null) - return false; - } else if (!output.equals(other.output)) - return false; - return true; + ExecResponse that = ExecResponse.class.cast(o); + return Objects.equal(this.output, that.output) && Objects.equal(this.error, that.error) + && Objects.equal(this.exitStatus, that.exitStatus); } - public int getExitCode() { - return exitCode; + @Override + public String toString() { + return Objects.toStringHelper("").add("output", output).add("error", error).add("exitStatus", exitStatus) + .toString(); } } \ No newline at end of file diff --git a/compute/src/main/java/org/jclouds/compute/domain/internal/NodeMetadataImpl.java b/compute/src/main/java/org/jclouds/compute/domain/internal/NodeMetadataImpl.java index 4262864686..6574da6613 100644 --- a/compute/src/main/java/org/jclouds/compute/domain/internal/NodeMetadataImpl.java +++ b/compute/src/main/java/org/jclouds/compute/domain/internal/NodeMetadataImpl.java @@ -220,7 +220,6 @@ public class NodeMetadataImpl extends ComputeMetadataImpl implements NodeMetadat result = prime * result + ((imageId == null) ? 0 : imageId.hashCode()); result = prime * result + ((hardware == null) ? 0 : hardware.hashCode()); result = prime * result + ((os == null) ? 0 : os.hashCode()); - result = prime * result + ((credentials == null) ? 0 : credentials.hashCode()); return result; } @@ -270,11 +269,6 @@ public class NodeMetadataImpl extends ComputeMetadataImpl implements NodeMetadat return false; } else if (!os.equals(other.os)) return false; - if (credentials == null) { - if (other.credentials != null) - return false; - } else if (!credentials.equals(other.credentials)) - return false; return true; } diff --git a/compute/src/main/java/org/jclouds/compute/events/InitScriptOnNodeSubmission.java b/compute/src/main/java/org/jclouds/compute/events/InitScriptOnNodeSubmission.java new file mode 100644 index 0000000000..e80c31b0d6 --- /dev/null +++ b/compute/src/main/java/org/jclouds/compute/events/InitScriptOnNodeSubmission.java @@ -0,0 +1,44 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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.jclouds.compute.events; + +import org.jclouds.compute.domain.NodeMetadata; +import org.jclouds.scriptbuilder.InitScript; + +import com.google.common.annotations.Beta; + +/** + * An init script was submitted to a node for exectuion. + *

+ * Note this does not guarantee that there was success, nor that the init script started. + * + * @author Adrian Cole + */ +@Beta +public class InitScriptOnNodeSubmission extends StatementOnNodeSubmission { + + public InitScriptOnNodeSubmission(InitScript statement, NodeMetadata node) { + super(statement, node); + } + + public InitScript getStatement() { + return InitScript.class.cast(statement); + } + +} \ No newline at end of file diff --git a/compute/src/main/java/org/jclouds/compute/events/StatementOnNode.java b/compute/src/main/java/org/jclouds/compute/events/StatementOnNode.java new file mode 100644 index 0000000000..b4964cb86a --- /dev/null +++ b/compute/src/main/java/org/jclouds/compute/events/StatementOnNode.java @@ -0,0 +1,76 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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.jclouds.compute.events; + +import static com.google.common.base.Preconditions.checkNotNull; + +import org.jclouds.compute.domain.NodeMetadata; +import org.jclouds.scriptbuilder.domain.Statement; + +import com.google.common.annotations.Beta; +import com.google.common.base.Objects; +import com.google.common.base.Objects.ToStringHelper; + +/** + + * @author Adrian Cole + */ +@Beta +public class StatementOnNode { + protected final Statement statement; + protected final NodeMetadata node; + + public StatementOnNode(Statement statement, NodeMetadata node) { + this.statement = checkNotNull(statement, "statement"); + this.node = checkNotNull(node, "node"); + } + + public Statement getStatement() { + return statement; + } + + public NodeMetadata getNode() { + return node; + } + + @Override + public int hashCode() { + return Objects.hashCode(statement, node); + } + + @Override + public boolean equals(Object o) { + if (o == null) + return false; + if (!o.getClass().equals(getClass())) + return false; + StatementOnNode that = StatementOnNode.class.cast(o); + return Objects.equal(this.statement, that.statement) && Objects.equal(this.node, that.node); + } + + @Override + public String toString() { + return string().toString(); + } + + protected ToStringHelper string() { + return Objects.toStringHelper(this).add("statement", statement).add("node", node.getId()); + } + +} \ No newline at end of file diff --git a/compute/src/main/java/org/jclouds/compute/events/StatementOnNodeCompletion.java b/compute/src/main/java/org/jclouds/compute/events/StatementOnNodeCompletion.java new file mode 100644 index 0000000000..c058faaa3c --- /dev/null +++ b/compute/src/main/java/org/jclouds/compute/events/StatementOnNodeCompletion.java @@ -0,0 +1,53 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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.jclouds.compute.events; + +import static com.google.common.base.Preconditions.checkNotNull; + +import org.jclouds.compute.domain.ExecResponse; +import org.jclouds.compute.domain.NodeMetadata; +import org.jclouds.scriptbuilder.domain.Statement; + +import com.google.common.annotations.Beta; +import com.google.common.base.Objects.ToStringHelper; + +/** + * A statement that completed execution on a node. + * + * @author Adrian Cole + */ +@Beta +public class StatementOnNodeCompletion extends StatementOnNode { + + private final ExecResponse response; + + public StatementOnNodeCompletion(Statement statement, NodeMetadata node, ExecResponse response) { + super(statement, node); + this.response = checkNotNull(response, "response"); + } + + public ExecResponse getResponse() { + return response; + } + + @Override + protected ToStringHelper string() { + return super.string().add("response", response); + } +} \ No newline at end of file diff --git a/compute/src/main/java/org/jclouds/compute/events/StatementOnNodeFailure.java b/compute/src/main/java/org/jclouds/compute/events/StatementOnNodeFailure.java new file mode 100644 index 0000000000..de29cf426c --- /dev/null +++ b/compute/src/main/java/org/jclouds/compute/events/StatementOnNodeFailure.java @@ -0,0 +1,52 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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.jclouds.compute.events; + +import static com.google.common.base.Preconditions.checkNotNull; + +import org.jclouds.compute.domain.NodeMetadata; +import org.jclouds.scriptbuilder.domain.Statement; + +import com.google.common.annotations.Beta; +import com.google.common.base.Objects.ToStringHelper; + +/** + * A statement that failed execution on a node. + * + * @author Adrian Cole + */ +@Beta +public class StatementOnNodeFailure extends StatementOnNode { + + private final Throwable cause; + + public StatementOnNodeFailure(Statement statement, NodeMetadata node, Throwable cause) { + super(statement, node); + this.cause = checkNotNull(cause, "cause"); + } + + public Throwable getCause() { + return cause; + } + + @Override + protected ToStringHelper string() { + return super.string().add("cause", cause.getMessage()); + } +} \ No newline at end of file diff --git a/compute/src/main/java/org/jclouds/compute/events/StatementOnNodeSubmission.java b/compute/src/main/java/org/jclouds/compute/events/StatementOnNodeSubmission.java new file mode 100644 index 0000000000..add0af9a9d --- /dev/null +++ b/compute/src/main/java/org/jclouds/compute/events/StatementOnNodeSubmission.java @@ -0,0 +1,41 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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.jclouds.compute.events; + +import org.jclouds.compute.domain.NodeMetadata; +import org.jclouds.scriptbuilder.domain.Statement; + +import com.google.common.annotations.Beta; + +/** + * A statement was submitted to a node for exectuion. + *

+ * Note this does not guarantee that there was success, nor that the node + * received the statement. + * + * @author Adrian Cole + */ +@Beta +public class StatementOnNodeSubmission extends StatementOnNode { + + public StatementOnNodeSubmission(Statement statement, NodeMetadata node) { + super(statement, node); + } + +} \ No newline at end of file diff --git a/compute/src/main/java/org/jclouds/compute/functions/TemplateOptionsToStatement.java b/compute/src/main/java/org/jclouds/compute/functions/TemplateOptionsToStatement.java index c38c21ff8d..95e122b042 100644 --- a/compute/src/main/java/org/jclouds/compute/functions/TemplateOptionsToStatement.java +++ b/compute/src/main/java/org/jclouds/compute/functions/TemplateOptionsToStatement.java @@ -25,7 +25,7 @@ import java.util.List; import javax.inject.Singleton; import org.jclouds.compute.options.TemplateOptions; -import org.jclouds.scriptbuilder.InitBuilder; +import org.jclouds.scriptbuilder.InitScript; import org.jclouds.scriptbuilder.domain.Statement; import org.jclouds.scriptbuilder.domain.StatementList; import org.jclouds.scriptbuilder.statements.ssh.AuthorizeRSAPublicKeys; @@ -51,7 +51,7 @@ public class TemplateOptionsToStatement implements Function= 1) { - if (options.getTaskName() == null && !(options.getRunScript() instanceof InitBuilder)) + if (options.getTaskName() == null && !(options.getRunScript() instanceof InitScript)) options.nameTask("bootstrap"); return bootstrap.size() == 1 ? bootstrap.get(0) : new StatementList(bootstrap); } diff --git a/compute/src/main/java/org/jclouds/compute/predicates/ScriptStatusReturnsZero.java b/compute/src/main/java/org/jclouds/compute/predicates/ScriptStatusReturnsZero.java index 43df011d9d..a2245ebeab 100644 --- a/compute/src/main/java/org/jclouds/compute/predicates/ScriptStatusReturnsZero.java +++ b/compute/src/main/java/org/jclouds/compute/predicates/ScriptStatusReturnsZero.java @@ -44,11 +44,11 @@ public class ScriptStatusReturnsZero implements Predicate() { @Override public ExecResponse call() { - return client.runScriptOnNode(nodeId, "./jboss start", runAsRoot(false).wrapInInitScript(false)); + return client.runScriptOnNode(nodeId, "/tmp/init-jboss start", runAsRoot(false).wrapInInitScript(false)); } @Override diff --git a/compute/src/test/java/org/jclouds/compute/StubComputeServiceIntegrationTest.java b/compute/src/test/java/org/jclouds/compute/StubComputeServiceIntegrationTest.java index 3ea2262417..2c42a16a19 100644 --- a/compute/src/test/java/org/jclouds/compute/StubComputeServiceIntegrationTest.java +++ b/compute/src/test/java/org/jclouds/compute/StubComputeServiceIntegrationTest.java @@ -70,6 +70,7 @@ public class StubComputeServiceIntegrationTest extends BaseComputeServiceLiveTes private static final ExecResponse EXEC_GOOD = new ExecResponse("", "", 0); private static final ExecResponse EXEC_BAD = new ExecResponse("", "", 1); + private static final ExecResponse EXEC_RC_GOOD = new ExecResponse("0", "", 0); public StubComputeServiceIntegrationTest() { provider = "stub"; @@ -268,13 +269,15 @@ public class StubComputeServiceIntegrationTest extends BaseComputeServiceLiveTes expect(client.exec("ln -fs /tmp/init-" + scriptName + " " + scriptName)).andReturn(EXEC_GOOD); expect(client.getUsername()).andReturn("root").atLeastOnce(); expect(client.getHostAddress()).andReturn("localhost").atLeastOnce(); - expect(client.exec("./" + scriptName + " init")).andReturn(EXEC_GOOD); - expect(client.exec("./" + scriptName + " start")).andReturn(EXEC_GOOD); - expect(client.exec("./" + scriptName + " status")).andReturn(EXEC_GOOD); + expect(client.exec("/tmp/init-" + scriptName + " init")).andReturn(EXEC_GOOD); + expect(client.exec("/tmp/init-" + scriptName + " start")).andReturn(EXEC_GOOD); + expect(client.exec("/tmp/init-" + scriptName + " status")).andReturn(EXEC_GOOD); // next status says the script is done, since not found. - expect(client.exec("./" + scriptName + " status")).andReturn(EXEC_BAD); - expect(client.exec("./" + scriptName + " tail")).andReturn(EXEC_GOOD); - expect(client.exec("./" + scriptName + " tailerr")).andReturn(EXEC_GOOD); + expect(client.exec("/tmp/init-" + scriptName + " status")).andReturn(EXEC_BAD); + expect(client.exec("/tmp/init-" + scriptName + " stdout")).andReturn(EXEC_GOOD); + expect(client.exec("/tmp/init-" + scriptName + " stderr")).andReturn(EXEC_GOOD); + expect(client.exec("/tmp/init-" + scriptName + " exitstatus")).andReturn(EXEC_RC_GOOD); + // note we have to reconnect here, as we updated the login user. client.disconnect(); @@ -307,23 +310,23 @@ public class StubComputeServiceIntegrationTest extends BaseComputeServiceLiveTes expect(clientNew.exec("ln -fs /tmp/init-" + scriptName + " " + scriptName)).andReturn(EXEC_GOOD); expect(clientNew.getUsername()).andReturn("web").atLeastOnce(); expect(clientNew.getHostAddress()).andReturn("localhost").atLeastOnce(); - expect(clientNew.exec("./" + scriptName + " init")).andReturn(EXEC_GOOD); - expect(clientNew.exec("./" + scriptName + " start")).andReturn(EXEC_GOOD); + expect(clientNew.exec("/tmp/init-" + scriptName + " init")).andReturn(EXEC_GOOD); + expect(clientNew.exec("/tmp/init-" + scriptName + " start")).andReturn(EXEC_GOOD); clientNew.disconnect(); clientNew.connect(); - expect(clientNew.exec("./" + scriptName + " tail\n")).andReturn(EXEC_GOOD); + expect(clientNew.exec("/tmp/init-" + scriptName + " stdout\n")).andReturn(EXEC_GOOD); clientNew.disconnect(); clientNew.connect(); - expect(clientNew.exec("./" + scriptName + " stop\n")).andReturn(EXEC_GOOD); + expect(clientNew.exec("/tmp/init-" + scriptName + " stop\n")).andReturn(EXEC_GOOD); clientNew.disconnect(); clientNew.connect(); - expect(clientNew.exec("./" + scriptName + " start\n")).andReturn(EXEC_GOOD); + expect(clientNew.exec("/tmp/init-" + scriptName + " start\n")).andReturn(EXEC_GOOD); clientNew.disconnect(); clientNew.connect(); - expect(clientNew.exec("./" + scriptName + " tail\n")).andReturn(EXEC_GOOD); + expect(clientNew.exec("/tmp/init-" + scriptName + " stdout\n")).andReturn(EXEC_GOOD); clientNew.disconnect(); } catch (IOException e) { Throwables.propagate(e); @@ -352,13 +355,14 @@ public class StubComputeServiceIntegrationTest extends BaseComputeServiceLiveTes expect(client.exec("ln -fs /tmp/init-" + scriptName + " " + scriptName)).andReturn(EXEC_GOOD); expect(client.getUsername()).andReturn("root").atLeastOnce(); expect(client.getHostAddress()).andReturn(nodeId + "").atLeastOnce(); - expect(client.exec("./" + scriptName + " init")).andReturn(EXEC_GOOD); - expect(client.exec("./" + scriptName + " start")).andReturn(EXEC_GOOD); - expect(client.exec("./" + scriptName + " status")).andReturn(EXEC_GOOD); + expect(client.exec("/tmp/init-" + scriptName + " init")).andReturn(EXEC_GOOD); + expect(client.exec("/tmp/init-" + scriptName + " start")).andReturn(EXEC_GOOD); + expect(client.exec("/tmp/init-" + scriptName + " status")).andReturn(EXEC_GOOD); // next status says the script is done, since not found. - expect(client.exec("./" + scriptName + " status")).andReturn(EXEC_BAD); - expect(client.exec("./" + scriptName + " tail")).andReturn(EXEC_GOOD); - expect(client.exec("./" + scriptName + " tailerr")).andReturn(EXEC_GOOD); + expect(client.exec("/tmp/init-" + scriptName + " status")).andReturn(EXEC_BAD); + expect(client.exec("/tmp/init-" + scriptName + " stdout")).andReturn(EXEC_GOOD); + expect(client.exec("/tmp/init-" + scriptName + " stderr")).andReturn(EXEC_GOOD); + expect(client.exec("/tmp/init-" + scriptName + " exitstatus")).andReturn(EXEC_RC_GOOD); } private void helloAndJava(SshClient client) { diff --git a/compute/src/test/java/org/jclouds/compute/callables/RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilCompleteTest.java b/compute/src/test/java/org/jclouds/compute/callables/RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilCompleteTest.java index 00c5509fec..4e44027680 100644 --- a/compute/src/test/java/org/jclouds/compute/callables/RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilCompleteTest.java +++ b/compute/src/test/java/org/jclouds/compute/callables/RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilCompleteTest.java @@ -41,7 +41,7 @@ import org.jclouds.concurrent.MoreExecutors; import org.jclouds.concurrent.config.ExecutorServiceModule; import org.jclouds.domain.LoginCredentials; import org.jclouds.predicates.RetryablePredicateTest; -import org.jclouds.scriptbuilder.InitBuilder; +import org.jclouds.scriptbuilder.InitScript; import org.jclouds.scriptbuilder.domain.OsFamily; import org.jclouds.scriptbuilder.domain.Statement; import org.jclouds.ssh.SshClient; @@ -50,7 +50,7 @@ import org.testng.annotations.Test; import com.google.common.base.Functions; import com.google.common.base.Stopwatch; import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; +import com.google.common.eventbus.EventBus; import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.assistedinject.FactoryModuleBuilder; @@ -62,6 +62,8 @@ import com.google.inject.name.Names; @Test(groups = "unit", singleThreaded = true, testName = "RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilCompleteTest") public class RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilCompleteTest { + EventBus eventBus = new EventBus(); + BlockUntilInitScriptStatusIsZeroThenReturnOutput.Factory statusFactory = Guice.createInjector( new ExecutorServiceModule(MoreExecutors.sameThreadExecutor(), MoreExecutors.sameThreadExecutor()), new AbstractModule() { @@ -98,7 +100,7 @@ public class RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilCompleteTest { RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilComplete testMe = new RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilComplete( statusFactory, timeouts, Functions.forMap(ImmutableMap.of(node, sshClient)), - InitScriptConfigurationForTasks.create().appendIncrementingNumberToAnonymousTaskNames(), node, command, + eventBus, InitScriptConfigurationForTasks.create().appendIncrementingNumberToAnonymousTaskNames(), node, command, new RunScriptOptions()); testMe.call(); @@ -137,13 +139,13 @@ public class RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilCompleteTest { */ private void runDefaults(IAnswer answerForScriptStatus, int timesForScriptStatus) { Statement command = exec("doFoo"); - NodeMetadata node = new NodeMetadataBuilder().ids("id").state(NodeState.RUNNING).credentials( - new LoginCredentials("tester", "testpassword!", null, false)).build(); + NodeMetadata node = new NodeMetadataBuilder().ids("id").state(NodeState.RUNNING) + .credentials(LoginCredentials.builder().user("tester").password("testpassword!").build()).build(); SshClient sshClient = createMock(SshClient.class); - InitBuilder init = new InitBuilder("jclouds-script-0", "/tmp/jclouds-script-0", "/tmp/jclouds-script-0", - ImmutableMap. of(), ImmutableSet.of(command)); + InitScript init = InitScript.builder().name("jclouds-script-0").home("/tmp/jclouds-script-0").run(command) + .build(); sshClient.connect(); sshClient.put("/tmp/init-jclouds-script-0", init.render(OsFamily.UNIX)); @@ -154,27 +156,28 @@ public class RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilCompleteTest { expect(sshClient.exec("chmod 755 /tmp/init-jclouds-script-0")).andReturn(new ExecResponse("", "", 0)); expect(sshClient.exec("ln -fs /tmp/init-jclouds-script-0 jclouds-script-0")).andReturn( new ExecResponse("", "", 0)); - expect(sshClient.exec("./jclouds-script-0 init")).andReturn(new ExecResponse("", "", 0)); + expect(sshClient.exec("/tmp/init-jclouds-script-0 init")).andReturn(new ExecResponse("", "", 0)); // start script as root via sudo, note that since there's no adminPassword we do a straight // sudo - expect(sshClient.exec("sudo ./jclouds-script-0 start")).andReturn(new ExecResponse("", "", 0)); + expect(sshClient.exec("sudo /tmp/init-jclouds-script-0 start")).andReturn(new ExecResponse("", "", 0)); // signal the command completed if (answerForScriptStatus == null) { - expect(sshClient.exec("./jclouds-script-0 status")).andReturn(new ExecResponse("", "", 1)).times(1); + expect(sshClient.exec("/tmp/init-jclouds-script-0 status")).andReturn(new ExecResponse("", "", 1)).times(1); } else { - expect(sshClient.exec("./jclouds-script-0 status")).andAnswer(answerForScriptStatus).times(timesForScriptStatus); + expect(sshClient.exec("/tmp/init-jclouds-script-0 status")).andAnswer(answerForScriptStatus).times(timesForScriptStatus); } - expect(sshClient.exec("./jclouds-script-0 tail")).andReturn(new ExecResponse("out", "", 0)); - expect(sshClient.exec("./jclouds-script-0 tailerr")).andReturn(new ExecResponse("err", "", 0)); + expect(sshClient.exec("/tmp/init-jclouds-script-0 stdout")).andReturn(new ExecResponse("out", "", 0)); + expect(sshClient.exec("/tmp/init-jclouds-script-0 stderr")).andReturn(new ExecResponse("err", "", 0)); + expect(sshClient.exec("/tmp/init-jclouds-script-0 exitstatus")).andReturn(new ExecResponse("0", "", 0)); sshClient.disconnect(); replay(sshClient); RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilComplete testMe = new RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilComplete( statusFactory, timeouts, Functions.forMap(ImmutableMap.of(node, sshClient)), - InitScriptConfigurationForTasks.create().appendIncrementingNumberToAnonymousTaskNames(), node, command, + eventBus, InitScriptConfigurationForTasks.create().appendIncrementingNumberToAnonymousTaskNames(), node, command, new RunScriptOptions()); assertEquals(testMe.getInitFile(), "/tmp/init-jclouds-script-0"); @@ -195,8 +198,8 @@ public class RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilCompleteTest { SshClient sshClient = createMock(SshClient.class); - InitBuilder init = new InitBuilder("jclouds-script-0", "/tmp/jclouds-script-0", "/tmp/jclouds-script-0", - ImmutableMap. of(), ImmutableSet.of(command)); + InitScript init = InitScript.builder().name("jclouds-script-0").home("/tmp/jclouds-script-0").run(command) + .build(); sshClient.connect(); sshClient.put("/tmp/init-jclouds-script-0", init.render(OsFamily.UNIX)); @@ -207,22 +210,23 @@ public class RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilCompleteTest { expect(sshClient.exec("chmod 755 /tmp/init-jclouds-script-0")).andReturn(new ExecResponse("", "", 0)); expect(sshClient.exec("ln -fs /tmp/init-jclouds-script-0 jclouds-script-0")).andReturn( new ExecResponse("", "", 0)); - expect(sshClient.exec("./jclouds-script-0 init")).andReturn(new ExecResponse("", "", 0)); + expect(sshClient.exec("/tmp/init-jclouds-script-0 init")).andReturn(new ExecResponse("", "", 0)); // since there's an adminPassword we must pass this in - expect(sshClient.exec("echo 'testpassword!'|sudo -S ./jclouds-script-0 start")).andReturn(new ExecResponse("", "", 0)); + expect(sshClient.exec("echo 'testpassword!'|sudo -S /tmp/init-jclouds-script-0 start")).andReturn(new ExecResponse("", "", 0)); // signal the command completed - expect(sshClient.exec("./jclouds-script-0 status")).andReturn(new ExecResponse("", "", 1)); - expect(sshClient.exec("./jclouds-script-0 tail")).andReturn(new ExecResponse("out", "", 0)); - expect(sshClient.exec("./jclouds-script-0 tailerr")).andReturn(new ExecResponse("err", "", 0)); + expect(sshClient.exec("/tmp/init-jclouds-script-0 status")).andReturn(new ExecResponse("", "", 1)); + expect(sshClient.exec("/tmp/init-jclouds-script-0 stdout")).andReturn(new ExecResponse("out", "", 0)); + expect(sshClient.exec("/tmp/init-jclouds-script-0 stderr")).andReturn(new ExecResponse("err", "", 0)); + expect(sshClient.exec("/tmp/init-jclouds-script-0 exitstatus")).andReturn(new ExecResponse("0", "", 0)); sshClient.disconnect(); replay(sshClient); RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilComplete testMe = new RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilComplete( statusFactory, timeouts, Functions.forMap(ImmutableMap.of(node, sshClient)), - InitScriptConfigurationForTasks.create().appendIncrementingNumberToAnonymousTaskNames(), node, command, + eventBus, InitScriptConfigurationForTasks.create().appendIncrementingNumberToAnonymousTaskNames(), node, command, new RunScriptOptions()); assertEquals(testMe.getInitFile(), "/tmp/init-jclouds-script-0"); @@ -243,8 +247,8 @@ public class RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilCompleteTest { SshClient sshClient = createMock(SshClient.class); - InitBuilder init = new InitBuilder("jclouds-script-0", "/tmp/jclouds-script-0", "/tmp/jclouds-script-0", - ImmutableMap. of(), ImmutableSet.of(command)); + InitScript init = InitScript.builder().name("jclouds-script-0").home("/tmp/jclouds-script-0").run(command) + .build(); sshClient.connect(); sshClient.put("/tmp/init-jclouds-script-0", init.render(OsFamily.UNIX)); @@ -255,22 +259,23 @@ public class RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilCompleteTest { expect(sshClient.exec("chmod 755 /tmp/init-jclouds-script-0")).andReturn(new ExecResponse("", "", 0)); expect(sshClient.exec("ln -fs /tmp/init-jclouds-script-0 jclouds-script-0")).andReturn( new ExecResponse("", "", 0)); - expect(sshClient.exec("./jclouds-script-0 init")).andReturn(new ExecResponse("", "", 0)); + expect(sshClient.exec("/tmp/init-jclouds-script-0 init")).andReturn(new ExecResponse("", "", 0)); // kick off as current user - expect(sshClient.exec("./jclouds-script-0 start")).andReturn(new ExecResponse("", "", 0)); + expect(sshClient.exec("/tmp/init-jclouds-script-0 start")).andReturn(new ExecResponse("", "", 0)); // signal the command completed - expect(sshClient.exec("./jclouds-script-0 status")).andReturn(new ExecResponse("", "", 1)); - expect(sshClient.exec("./jclouds-script-0 tail")).andReturn(new ExecResponse("out", "", 0)); - expect(sshClient.exec("./jclouds-script-0 tailerr")).andReturn(new ExecResponse("err", "", 0)); + expect(sshClient.exec("/tmp/init-jclouds-script-0 status")).andReturn(new ExecResponse("", "", 1)); + expect(sshClient.exec("/tmp/init-jclouds-script-0 stdout")).andReturn(new ExecResponse("out", "", 0)); + expect(sshClient.exec("/tmp/init-jclouds-script-0 stderr")).andReturn(new ExecResponse("err", "", 0)); + expect(sshClient.exec("/tmp/init-jclouds-script-0 exitstatus")).andReturn(new ExecResponse("0", "", 0)); sshClient.disconnect(); replay(sshClient); RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilComplete testMe = new RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilComplete( statusFactory, timeouts, Functions.forMap(ImmutableMap.of(node, sshClient)), - InitScriptConfigurationForTasks.create().appendIncrementingNumberToAnonymousTaskNames(), node, command, + eventBus, InitScriptConfigurationForTasks.create().appendIncrementingNumberToAnonymousTaskNames(), node, command, new RunScriptOptions().runAsRoot(false)); assertEquals(testMe.getInitFile(), "/tmp/init-jclouds-script-0"); @@ -284,4 +289,53 @@ public class RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilCompleteTest { verify(sshClient); } + + public void testBadReturnCode() { + Statement command = exec("doFoo"); + NodeMetadata node = new NodeMetadataBuilder().ids("badreturncode").state(NodeState.RUNNING).credentials( + new LoginCredentials("tester", "testpassword!", null, true)).build(); + + SshClient sshClient = createMock(SshClient.class); + + InitScript init = InitScript.builder().name("jclouds-script-0").home("/tmp/jclouds-script-0").run(command) + .build(); + + sshClient.connect(); + sshClient.put("/tmp/init-jclouds-script-0", init.render(OsFamily.UNIX)); + expect(sshClient.getUsername()).andReturn("tester").atLeastOnce(); + expect(sshClient.getHostAddress()).andReturn("somewhere.example.com").atLeastOnce(); + + // setup script as default user + expect(sshClient.exec("chmod 755 /tmp/init-jclouds-script-0")).andReturn(new ExecResponse("", "", 0)); + expect(sshClient.exec("ln -fs /tmp/init-jclouds-script-0 jclouds-script-0")).andReturn( + new ExecResponse("", "", 0)); + expect(sshClient.exec("/tmp/init-jclouds-script-0 init")).andReturn(new ExecResponse("", "", 0)); + + // kick off as current user + expect(sshClient.exec("/tmp/init-jclouds-script-0 start")).andReturn(new ExecResponse("", "", 0)); + + // signal the command completed + expect(sshClient.exec("/tmp/init-jclouds-script-0 status")).andReturn(new ExecResponse("", "", 1)); + expect(sshClient.exec("/tmp/init-jclouds-script-0 stdout")).andReturn(new ExecResponse("out", "", 0)); + expect(sshClient.exec("/tmp/init-jclouds-script-0 stderr")).andReturn(new ExecResponse("err", "", 0)); + expect(sshClient.exec("/tmp/init-jclouds-script-0 exitstatus")).andReturn(new ExecResponse("1", "", 0)); + + sshClient.disconnect(); + replay(sshClient); + + RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilComplete testMe = new RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilComplete( + statusFactory, timeouts, Functions.forMap(ImmutableMap.of(node, sshClient)), + eventBus, InitScriptConfigurationForTasks.create().appendIncrementingNumberToAnonymousTaskNames(), node, command, + new RunScriptOptions().runAsRoot(false)); + + assertEquals(testMe.getInitFile(), "/tmp/init-jclouds-script-0"); + assertEquals(testMe.getNode(), node); + assertEquals(testMe.getStatement(), init); + + testMe.init(); + + assertEquals(testMe.call(), new ExecResponse("out", "err", 1)); + + verify(sshClient); + } } diff --git a/compute/src/test/java/org/jclouds/compute/callables/RunScriptOnNodeAsInitScriptUsingSshTest.java b/compute/src/test/java/org/jclouds/compute/callables/RunScriptOnNodeAsInitScriptUsingSshTest.java index bb74d982d9..3716c746ba 100644 --- a/compute/src/test/java/org/jclouds/compute/callables/RunScriptOnNodeAsInitScriptUsingSshTest.java +++ b/compute/src/test/java/org/jclouds/compute/callables/RunScriptOnNodeAsInitScriptUsingSshTest.java @@ -31,7 +31,7 @@ import org.jclouds.compute.domain.NodeMetadataBuilder; import org.jclouds.compute.domain.NodeState; import org.jclouds.compute.options.RunScriptOptions; import org.jclouds.domain.LoginCredentials; -import org.jclouds.scriptbuilder.InitBuilder; +import org.jclouds.scriptbuilder.InitScript; import org.jclouds.scriptbuilder.domain.OsFamily; import org.jclouds.scriptbuilder.domain.Statement; import org.jclouds.ssh.SshClient; @@ -39,13 +39,14 @@ import org.testng.annotations.Test; import com.google.common.base.Functions; import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; +import com.google.common.eventbus.EventBus; /** * @author Adrian Cole */ @Test(groups = "unit", singleThreaded = true, testName = "RunScriptOnNodeAsInitScriptUsingSshTest") public class RunScriptOnNodeAsInitScriptUsingSshTest { + EventBus eventBus = new EventBus(); @Test(expectedExceptions = IllegalStateException.class) public void testWithoutInitThrowsIllegalStateException() { @@ -58,7 +59,7 @@ public class RunScriptOnNodeAsInitScriptUsingSshTest { replay(sshClient); RunScriptOnNodeAsInitScriptUsingSsh testMe = new RunScriptOnNodeAsInitScriptUsingSsh(Functions - .forMap(ImmutableMap.of(node, sshClient)), InitScriptConfigurationForTasks.create() + .forMap(ImmutableMap.of(node, sshClient)), eventBus, InitScriptConfigurationForTasks.create() .appendIncrementingNumberToAnonymousTaskNames(), node, command, new RunScriptOptions()); testMe.call(); @@ -71,8 +72,8 @@ public class RunScriptOnNodeAsInitScriptUsingSshTest { SshClient sshClient = createMock(SshClient.class); - InitBuilder init = new InitBuilder("jclouds-script-0", "/tmp/jclouds-script-0", "/tmp/jclouds-script-0", - ImmutableMap. of(), ImmutableSet.of(command)); + InitScript init = InitScript.builder().name("jclouds-script-0").home("/tmp/jclouds-script-0").run(command) + .build(); sshClient.connect(); sshClient.put("/tmp/init-jclouds-script-0", init.render(OsFamily.UNIX)); @@ -82,17 +83,17 @@ public class RunScriptOnNodeAsInitScriptUsingSshTest { // setup script as default user expect(sshClient.exec("chmod 755 /tmp/init-jclouds-script-0")).andReturn(new ExecResponse("", "", 0)); expect(sshClient.exec("ln -fs /tmp/init-jclouds-script-0 jclouds-script-0")).andReturn(new ExecResponse("", "", 0)); - expect(sshClient.exec("./jclouds-script-0 init")).andReturn(new ExecResponse("", "", 0)); + expect(sshClient.exec("/tmp/init-jclouds-script-0 init")).andReturn(new ExecResponse("", "", 0)); // start script as root via sudo, note that since there's no adminPassword we do a straight sudo - expect(sshClient.exec("sudo ./jclouds-script-0 start")).andReturn(new ExecResponse("", "", 0)); + expect(sshClient.exec("sudo /tmp/init-jclouds-script-0 start")).andReturn(new ExecResponse("", "", 0)); sshClient.disconnect(); replay(sshClient); RunScriptOnNodeAsInitScriptUsingSsh testMe = new RunScriptOnNodeAsInitScriptUsingSsh(Functions - .forMap(ImmutableMap.of(node, sshClient)), InitScriptConfigurationForTasks.create() + .forMap(ImmutableMap.of(node, sshClient)), eventBus, InitScriptConfigurationForTasks.create() .appendIncrementingNumberToAnonymousTaskNames(), node, command, new RunScriptOptions()); assertEquals(testMe.getInitFile(), "/tmp/init-jclouds-script-0"); @@ -112,8 +113,8 @@ public class RunScriptOnNodeAsInitScriptUsingSshTest { SshClient sshClient = createMock(SshClient.class); - InitBuilder init = new InitBuilder("jclouds-script-0", "/tmp/jclouds-script-0", "/tmp/jclouds-script-0", - ImmutableMap. of(), ImmutableSet.of(command)); + InitScript init = InitScript.builder().name("jclouds-script-0").home("/tmp/jclouds-script-0").run(command) + .build(); sshClient.connect(); sshClient.put("/tmp/init-jclouds-script-0", init.render(OsFamily.UNIX)); @@ -123,17 +124,17 @@ public class RunScriptOnNodeAsInitScriptUsingSshTest { // setup script as default user expect(sshClient.exec("chmod 755 /tmp/init-jclouds-script-0")).andReturn(new ExecResponse("", "", 0)); expect(sshClient.exec("ln -fs /tmp/init-jclouds-script-0 jclouds-script-0")).andReturn(new ExecResponse("", "", 0)); - expect(sshClient.exec("./jclouds-script-0 init")).andReturn(new ExecResponse("", "", 0)); + expect(sshClient.exec("/tmp/init-jclouds-script-0 init")).andReturn(new ExecResponse("", "", 0)); // since there's an adminPassword we must pass this in - expect(sshClient.exec("echo 'notalot'|sudo -S ./jclouds-script-0 start")).andReturn(new ExecResponse("", "", 0)); + expect(sshClient.exec("echo 'notalot'|sudo -S /tmp/init-jclouds-script-0 start")).andReturn(new ExecResponse("", "", 0)); sshClient.disconnect(); replay(sshClient); RunScriptOnNodeAsInitScriptUsingSsh testMe = new RunScriptOnNodeAsInitScriptUsingSsh(Functions - .forMap(ImmutableMap.of(node, sshClient)), InitScriptConfigurationForTasks.create() + .forMap(ImmutableMap.of(node, sshClient)), eventBus, InitScriptConfigurationForTasks.create() .appendIncrementingNumberToAnonymousTaskNames(), node, command, new RunScriptOptions()); assertEquals(testMe.getInitFile(), "/tmp/init-jclouds-script-0"); @@ -154,8 +155,8 @@ public class RunScriptOnNodeAsInitScriptUsingSshTest { SshClient sshClient = createMock(SshClient.class); - InitBuilder init = new InitBuilder("jclouds-script-0", "/tmp/jclouds-script-0", "/tmp/jclouds-script-0", - ImmutableMap. of(), ImmutableSet.of(command)); + InitScript init = InitScript.builder().name("jclouds-script-0").home("/tmp/jclouds-script-0").run(command) + .build(); sshClient.connect(); sshClient.put("/tmp/init-jclouds-script-0", init.render(OsFamily.UNIX)); @@ -165,16 +166,16 @@ public class RunScriptOnNodeAsInitScriptUsingSshTest { // setup script as default user expect(sshClient.exec("chmod 755 /tmp/init-jclouds-script-0")).andReturn(new ExecResponse("", "", 0)); expect(sshClient.exec("ln -fs /tmp/init-jclouds-script-0 jclouds-script-0")).andReturn(new ExecResponse("", "", 0)); - expect(sshClient.exec("./jclouds-script-0 init")).andReturn(new ExecResponse("", "", 0)); + expect(sshClient.exec("/tmp/init-jclouds-script-0 init")).andReturn(new ExecResponse("", "", 0)); // kick off as current user - expect(sshClient.exec("./jclouds-script-0 start")).andReturn(new ExecResponse("", "", 0)); + expect(sshClient.exec("/tmp/init-jclouds-script-0 start")).andReturn(new ExecResponse("", "", 0)); sshClient.disconnect(); replay(sshClient); RunScriptOnNodeAsInitScriptUsingSsh testMe = new RunScriptOnNodeAsInitScriptUsingSsh(Functions - .forMap(ImmutableMap.of(node, sshClient)), InitScriptConfigurationForTasks.create() + .forMap(ImmutableMap.of(node, sshClient)), eventBus, InitScriptConfigurationForTasks.create() .appendIncrementingNumberToAnonymousTaskNames(), node, command, new RunScriptOptions().runAsRoot(false)); assertEquals(testMe.getInitFile(), "/tmp/init-jclouds-script-0"); diff --git a/compute/src/test/java/org/jclouds/compute/callables/RunScriptOnNodeUsingSshTest.java b/compute/src/test/java/org/jclouds/compute/callables/RunScriptOnNodeUsingSshTest.java index 2c117ff8c1..2a6bed2249 100644 --- a/compute/src/test/java/org/jclouds/compute/callables/RunScriptOnNodeUsingSshTest.java +++ b/compute/src/test/java/org/jclouds/compute/callables/RunScriptOnNodeUsingSshTest.java @@ -34,12 +34,15 @@ import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import com.google.common.base.Function; +import com.google.common.eventbus.EventBus; /** * @author Adam Lowe */ @Test(groups = { "unit" }, singleThreaded = true) public class RunScriptOnNodeUsingSshTest { + EventBus eventBus = new EventBus(); + private SshClient sshClient; private NodeMetadata node; private Function sshFactory; @@ -59,7 +62,7 @@ public class RunScriptOnNodeUsingSshTest { } public void simpleTest() { - RunScriptOnNodeUsingSsh testMe = new RunScriptOnNodeUsingSsh(sshFactory, node, exec("echo $USER\necho $USER"), + RunScriptOnNodeUsingSsh testMe = new RunScriptOnNodeUsingSsh(sshFactory, eventBus, node, exec("echo $USER\necho $USER"), wrapInInitScript(false).runAsRoot(false)); testMe.init(); @@ -75,7 +78,7 @@ public class RunScriptOnNodeUsingSshTest { } public void simpleRootTest() { - RunScriptOnNodeUsingSsh testMe = new RunScriptOnNodeUsingSsh(sshFactory, node, exec("echo $USER\necho $USER"), + RunScriptOnNodeUsingSsh testMe = new RunScriptOnNodeUsingSsh(sshFactory, eventBus, node, exec("echo $USER\necho $USER"), wrapInInitScript(false).runAsRoot(true)); testMe.init(); @@ -97,7 +100,7 @@ public class RunScriptOnNodeUsingSshTest { expect(node.getCredentials()).andReturn(new LoginCredentials("tester", "testpassword!", null, true)) .atLeastOnce(); replay(node); - RunScriptOnNodeUsingSsh testMe = new RunScriptOnNodeUsingSsh(sshFactory, node, exec("echo $USER\necho $USER"), + RunScriptOnNodeUsingSsh testMe = new RunScriptOnNodeUsingSsh(sshFactory, eventBus, node, exec("echo $USER\necho $USER"), wrapInInitScript(false).runAsRoot(true)); testMe.init(); @@ -114,7 +117,7 @@ public class RunScriptOnNodeUsingSshTest { } public void testUserAddAsRoot() { - RunScriptOnNodeUsingSsh testMe = new RunScriptOnNodeUsingSsh(sshFactory, node, UserAdd.builder() + RunScriptOnNodeUsingSsh testMe = new RunScriptOnNodeUsingSsh(sshFactory, eventBus, node, UserAdd.builder() .login("testuser").build(), wrapInInitScript(false).runAsRoot(true).overrideLoginPassword("test")); testMe.init(); diff --git a/compute/src/test/resources/initscript_with_java.sh b/compute/src/test/resources/initscript_with_java.sh index 9e0a3b6d40..e22fa6d73b 100644 --- a/compute/src/test/resources/initscript_with_java.sh +++ b/compute/src/test/resources/initscript_with_java.sh @@ -10,11 +10,11 @@ function abort { function default { export INSTANCE_NAME="bootstrap" export INSTANCE_HOME="/tmp/bootstrap" -export LOG_DIR="/tmp/bootstrap" - return 0 +export LOG_DIR="$INSTANCE_HOME" + return $? } function bootstrap { - return 0 + return $? } function findPid { unset FOUND_PID; @@ -62,35 +62,41 @@ init) mkdir -p $INSTANCE_HOME # create runscript header - cat > $INSTANCE_HOME/bootstrap.sh <&2 + cat > $INSTANCE_HOME/bootstrap.sh <<-'END_OF_JCLOUDS_SCRIPT' + #!/bin/bash + set +u + shopt -s xpg_echo + shopt -s expand_aliases + + PROMPT_COMMAND='echo -ne \"\033]0;bootstrap\007\"' + export PATH=/usr/ucb/bin:/bin:/sbin:/usr/bin:/usr/sbin + + export INSTANCE_NAME='bootstrap' +END_OF_JCLOUDS_SCRIPT + cat >> $INSTANCE_HOME/bootstrap.sh <<-END_OF_JCLOUDS_SCRIPT + export INSTANCE_NAME='$INSTANCE_NAME' + export INSTANCE_HOME='$INSTANCE_HOME' + export LOG_DIR='$LOG_DIR' +END_OF_JCLOUDS_SCRIPT + cat >> $INSTANCE_HOME/bootstrap.sh <<-'END_OF_JCLOUDS_SCRIPT' + function abort { + echo "aborting: $@" 1>&2 exit 1 } alias apt-get-install="apt-get install -f -y -qq --force-yes" alias apt-get-upgrade="(apt-get update -qq&&apt-get upgrade -y -qq)" function ensure_cmd_or_install_package_apt(){ - local cmd=\$1 - local pkg=\$2 + local cmd=$1 + local pkg=$2 - hash \$cmd 2>/dev/null || apt-get-install \$pkg || ( apt-get-upgrade && apt-get-install \$pkg ) + hash $cmd 2>/dev/null || apt-get-install $pkg || ( apt-get-upgrade && apt-get-install $pkg ) } function ensure_cmd_or_install_package_yum(){ - local cmd=\$1 - local pkg=\$2 - hash \$cmd 2>/dev/null || yum --nogpgcheck -y ensure \$pkg + local cmd=$1 + local pkg=$2 + hash $cmd 2>/dev/null || yum --nogpgcheck -y ensure $pkg } function ensure_netutils_apt() { @@ -106,7 +112,7 @@ function ensure_netutils_yum() { # most network services require that the hostname is in # the /etc/hosts file, or they won't operate function ensure_hostname_in_hosts() { - egrep -q `hostname` /etc/hosts || awk -v hostname=`hostname` 'END { print \$1" "hostname }' /proc/net/arp >> /etc/hosts + egrep -q `hostname` /etc/hosts || awk -v hostname=`hostname` 'END { print $1" "hostname }' /proc/net/arp >> /etc/hosts } # download locations for many services are at public dns @@ -127,60 +133,63 @@ function setupPublicCurl() { ensure_can_resolve_public_dns return 0 } -END_OF_SCRIPT + +END_OF_JCLOUDS_SCRIPT # add desired commands from the user - cat >> $INSTANCE_HOME/bootstrap.sh <<'END_OF_SCRIPT' -cd $INSTANCE_HOME -rm /etc/sudoers -cat >> /etc/sudoers <<'END_OF_FILE' -root ALL = (ALL) ALL -%wheel ALL = (ALL) NOPASSWD:ALL -END_OF_FILE -chmod 0440 /etc/sudoers -mkdir -p /home/users -groupadd -f wheel -useradd -s /bin/bash -g wheel -m -d /home/users/defaultAdminUsername -p 'crypt(randompassword)' defaultAdminUsername -mkdir -p /home/users/defaultAdminUsername/.ssh -cat >> /home/users/defaultAdminUsername/.ssh/authorized_keys <<'END_OF_FILE' -publicKey -END_OF_FILE -chmod 600 /home/users/defaultAdminUsername/.ssh/authorized_keys -chown -R defaultAdminUsername /home/users/defaultAdminUsername -exec 3<> /etc/ssh/sshd_config && awk -v TEXT="PasswordAuthentication no -PermitRootLogin no -" 'BEGIN {print TEXT}{print}' /etc/ssh/sshd_config >&3 -/etc/init.d/sshd reload||/etc/init.d/ssh reload -awk -v user=^${SUDO_USER:=${USER}}: -v password='crypt(randompassword)' 'BEGIN { FS=OFS=":" } $0 ~ user { $2 = password } 1' /etc/shadow >/etc/shadow.${SUDO_USER:=${USER}} -test -f /etc/shadow.${SUDO_USER:=${USER}} && mv /etc/shadow.${SUDO_USER:=${USER}} /etc/shadow -setupPublicCurl || return 1 -curl -q -s -S -L --connect-timeout 10 --max-time 600 --retry 20 -X GET http://download.oracle.com/otn-pub/java/jdk/7/jdk-7-linux-x64.tar.gz |(mkdir -p /usr/local &&cd /usr/local &&tar -xpzf -) -mv /usr/local/jdk* /usr/local/jdk/ -test -n "$SUDO_USER" && -cat >> /home/$SUDO_USER/.bashrc <<'END_OF_FILE' -export JAVA_HOME=/usr/local/jdk -export PATH=$JAVA_HOME/bin:$PATH -END_OF_FILE -cat >> /etc/bashrc <<'END_OF_FILE' -export JAVA_HOME=/usr/local/jdk -export PATH=$JAVA_HOME/bin:$PATH -END_OF_FILE -cat >> $HOME/.bashrc <<'END_OF_FILE' -export JAVA_HOME=/usr/local/jdk -export PATH=$JAVA_HOME/bin:$PATH -END_OF_FILE -cat >> /etc/skel/.bashrc <<'END_OF_FILE' -export JAVA_HOME=/usr/local/jdk -export PATH=$JAVA_HOME/bin:$PATH -END_OF_FILE -ln -fs /usr/local/jdk/bin/java /usr/bin/java - -END_OF_SCRIPT + cat >> $INSTANCE_HOME/bootstrap.sh <<-'END_OF_JCLOUDS_SCRIPT' + cd $INSTANCE_HOME + rm -f $INSTANCE_HOME/rc + trap 'echo $?>$INSTANCE_HOME/rc' 0 1 2 3 15 + cat > /etc/sudoers <<-'END_OF_JCLOUDS_FILE' + root ALL = (ALL) ALL + %wheel ALL = (ALL) NOPASSWD:ALL + END_OF_JCLOUDS_FILE + chmod 0440 /etc/sudoers + mkdir -p /home/users + groupadd -f wheel + useradd -s /bin/bash -g wheel -m -d /home/users/defaultAdminUsername -p 'crypt(randompassword)' defaultAdminUsername + mkdir -p /home/users/defaultAdminUsername/.ssh + cat >> /home/users/defaultAdminUsername/.ssh/authorized_keys <<-'END_OF_JCLOUDS_FILE' + publicKey + END_OF_JCLOUDS_FILE + chmod 600 /home/users/defaultAdminUsername/.ssh/authorized_keys + chown -R defaultAdminUsername /home/users/defaultAdminUsername + exec 3<> /etc/ssh/sshd_config && awk -v TEXT="PasswordAuthentication no + PermitRootLogin no + " 'BEGIN {print TEXT}{print}' /etc/ssh/sshd_config >&3 + hash service 2>/dev/null && service ssh reload || /etc/init.d/ssh* reload + awk -v user=^${SUDO_USER:=${USER}}: -v password='crypt(randompassword)' 'BEGIN { FS=OFS=":" } $0 ~ user { $2 = password } 1' /etc/shadow >/etc/shadow.${SUDO_USER:=${USER}} + test -f /etc/shadow.${SUDO_USER:=${USER}} && mv /etc/shadow.${SUDO_USER:=${USER}} /etc/shadow + setupPublicCurl || return 1 + curl -q -s -S -L --connect-timeout 10 --max-time 600 --retry 20 -X GET http://download.oracle.com/otn-pub/java/jdk/7/jdk-7-linux-x64.tar.gz |(mkdir -p /usr/local &&cd /usr/local &&tar -xpzf -) + mv /usr/local/jdk* /usr/local/jdk/ + test -n "$SUDO_USER" && + cat >> /home/$SUDO_USER/.bashrc <<-'END_OF_JCLOUDS_FILE' + export JAVA_HOME=/usr/local/jdk + export PATH=$JAVA_HOME/bin:$PATH + END_OF_JCLOUDS_FILE + cat >> /etc/bashrc <<-'END_OF_JCLOUDS_FILE' + export JAVA_HOME=/usr/local/jdk + export PATH=$JAVA_HOME/bin:$PATH + END_OF_JCLOUDS_FILE + cat >> $HOME/.bashrc <<-'END_OF_JCLOUDS_FILE' + export JAVA_HOME=/usr/local/jdk + export PATH=$JAVA_HOME/bin:$PATH + END_OF_JCLOUDS_FILE + cat >> /etc/skel/.bashrc <<-'END_OF_JCLOUDS_FILE' + export JAVA_HOME=/usr/local/jdk + export PATH=$JAVA_HOME/bin:$PATH + END_OF_JCLOUDS_FILE + ln -fs /usr/local/jdk/bin/java /usr/bin/java + +END_OF_JCLOUDS_SCRIPT # add runscript footer - cat >> $INSTANCE_HOME/bootstrap.sh <<'END_OF_SCRIPT' -exit 0 -END_OF_SCRIPT + cat >> $INSTANCE_HOME/bootstrap.sh <<-'END_OF_JCLOUDS_SCRIPT' + exit $? + +END_OF_JCLOUDS_SCRIPT chmod u+x $INSTANCE_HOME/bootstrap.sh ;; @@ -201,6 +210,17 @@ start) default || exit 1 forget $INSTANCE_NAME $INSTANCE_HOME/$INSTANCE_NAME.sh $LOG_DIR || exit 1 ;; +stdout) + default || exit 1 + cat $LOG_DIR/stdout.log + ;; +stderr) + default || exit 1 + cat $LOG_DIR/stderr.log + ;; +exitstatus) + default || exit 1 + [ -f $LOG_DIR/rc ] && cat $LOG_DIR/rc;; tail) default || exit 1 tail $LOG_DIR/stdout.log @@ -214,4 +234,4 @@ run) $INSTANCE_HOME/$INSTANCE_NAME.sh ;; esac -exit 0 +exit $? diff --git a/compute/src/test/resources/initscript_with_jboss.sh b/compute/src/test/resources/initscript_with_jboss.sh index e73a1f177e..36216b850d 100644 --- a/compute/src/test/resources/initscript_with_jboss.sh +++ b/compute/src/test/resources/initscript_with_jboss.sh @@ -10,11 +10,11 @@ function abort { function default { export INSTANCE_NAME="configure-jboss" export INSTANCE_HOME="/tmp/configure-jboss" -export LOG_DIR="/tmp/configure-jboss" - return 0 +export LOG_DIR="$INSTANCE_HOME" + return $? } function configure-jboss { - return 0 + return $? } function findPid { unset FOUND_PID; @@ -62,35 +62,41 @@ init) mkdir -p $INSTANCE_HOME # create runscript header - cat > $INSTANCE_HOME/configure-jboss.sh <&2 + cat > $INSTANCE_HOME/configure-jboss.sh <<-'END_OF_JCLOUDS_SCRIPT' + #!/bin/bash + set +u + shopt -s xpg_echo + shopt -s expand_aliases + + PROMPT_COMMAND='echo -ne \"\033]0;configure-jboss\007\"' + export PATH=/usr/ucb/bin:/bin:/sbin:/usr/bin:/usr/sbin + + export INSTANCE_NAME='configure-jboss' +END_OF_JCLOUDS_SCRIPT + cat >> $INSTANCE_HOME/configure-jboss.sh <<-END_OF_JCLOUDS_SCRIPT + export INSTANCE_NAME='$INSTANCE_NAME' + export INSTANCE_HOME='$INSTANCE_HOME' + export LOG_DIR='$LOG_DIR' +END_OF_JCLOUDS_SCRIPT + cat >> $INSTANCE_HOME/configure-jboss.sh <<-'END_OF_JCLOUDS_SCRIPT' + function abort { + echo "aborting: $@" 1>&2 exit 1 } alias apt-get-install="apt-get install -f -y -qq --force-yes" alias apt-get-upgrade="(apt-get update -qq&&apt-get upgrade -y -qq)" function ensure_cmd_or_install_package_apt(){ - local cmd=\$1 - local pkg=\$2 + local cmd=$1 + local pkg=$2 - hash \$cmd 2>/dev/null || apt-get-install \$pkg || ( apt-get-upgrade && apt-get-install \$pkg ) + hash $cmd 2>/dev/null || apt-get-install $pkg || ( apt-get-upgrade && apt-get-install $pkg ) } function ensure_cmd_or_install_package_yum(){ - local cmd=\$1 - local pkg=\$2 - hash \$cmd 2>/dev/null || yum --nogpgcheck -y ensure \$pkg + local cmd=$1 + local pkg=$2 + hash $cmd 2>/dev/null || yum --nogpgcheck -y ensure $pkg } function ensure_netutils_apt() { @@ -106,7 +112,7 @@ function ensure_netutils_yum() { # most network services require that the hostname is in # the /etc/hosts file, or they won't operate function ensure_hostname_in_hosts() { - egrep -q `hostname` /etc/hosts || awk -v hostname=`hostname` 'END { print \$1" "hostname }' /proc/net/arp >> /etc/hosts + egrep -q `hostname` /etc/hosts || awk -v hostname=`hostname` 'END { print $1" "hostname }' /proc/net/arp >> /etc/hosts } # download locations for many services are at public dns @@ -127,69 +133,72 @@ function setupPublicCurl() { ensure_can_resolve_public_dns return 0 } -END_OF_SCRIPT + +END_OF_JCLOUDS_SCRIPT # add desired commands from the user - cat >> $INSTANCE_HOME/configure-jboss.sh <<'END_OF_SCRIPT' -cd $INSTANCE_HOME -rm /etc/sudoers -cat >> /etc/sudoers <<'END_OF_FILE' -root ALL = (ALL) ALL -%wheel ALL = (ALL) NOPASSWD:ALL -END_OF_FILE -chmod 0440 /etc/sudoers -mkdir -p /home/users -groupadd -f wheel -useradd -s /bin/bash -g wheel -m -d /home/users/web -p 'crypt(randompassword)' web -mkdir -p /home/users/web/.ssh -cat >> /home/users/web/.ssh/authorized_keys <<'END_OF_FILE' -publicKey -END_OF_FILE -chmod 600 /home/users/web/.ssh/authorized_keys -chown -R web /home/users/web -exec 3<> /etc/ssh/sshd_config && awk -v TEXT="PasswordAuthentication no -PermitRootLogin no -" 'BEGIN {print TEXT}{print}' /etc/ssh/sshd_config >&3 -/etc/init.d/sshd reload||/etc/init.d/ssh reload -awk -v user=^${SUDO_USER:=${USER}}: -v password='crypt(randompassword)' 'BEGIN { FS=OFS=":" } $0 ~ user { $2 = password } 1' /etc/shadow >/etc/shadow.${SUDO_USER:=${USER}} -test -f /etc/shadow.${SUDO_USER:=${USER}} && mv /etc/shadow.${SUDO_USER:=${USER}} /etc/shadow -setupPublicCurl || return 1 -curl -q -s -S -L --connect-timeout 10 --max-time 600 --retry 20 -X GET http://download.oracle.com/otn-pub/java/jdk/7/jdk-7-linux-x64.tar.gz |(mkdir -p /usr/local &&cd /usr/local &&tar -xpzf -) -mv /usr/local/jdk* /usr/local/jdk/ -test -n "$SUDO_USER" && -cat >> /home/$SUDO_USER/.bashrc <<'END_OF_FILE' -export JAVA_HOME=/usr/local/jdk -export PATH=$JAVA_HOME/bin:$PATH -END_OF_FILE -cat >> /etc/bashrc <<'END_OF_FILE' -export JAVA_HOME=/usr/local/jdk -export PATH=$JAVA_HOME/bin:$PATH -END_OF_FILE -cat >> $HOME/.bashrc <<'END_OF_FILE' -export JAVA_HOME=/usr/local/jdk -export PATH=$JAVA_HOME/bin:$PATH -END_OF_FILE -cat >> /etc/skel/.bashrc <<'END_OF_FILE' -export JAVA_HOME=/usr/local/jdk -export PATH=$JAVA_HOME/bin:$PATH -END_OF_FILE -ln -fs /usr/local/jdk/bin/java /usr/bin/java -iptables -I INPUT 1 -p tcp --dport 22 -j ACCEPT -iptables -I INPUT 1 -p tcp --dport 8080 -j ACCEPT -iptables-save -curl -q -s -S -L --connect-timeout 10 --max-time 600 --retry 20 -X GET http://download.jboss.org/jbossas/7.0/jboss-as-7.0.2.Final/jboss-as-web-7.0.2.Final.tar.gz |(mkdir -p /usr/local &&cd /usr/local &&tar -xpzf -) -mkdir -p /usr/local/jboss -mv /usr/local/jboss-*/* /usr/local/jboss -(cd /usr/local/jboss/standalone/configuration && sed 's~inet-address value=.*/~any-address/~g' standalone.xml > standalone.xml.new && mv standalone.xml.new standalone.xml) -chmod -R oug+r+w /usr/local/jboss -chown -R web /usr/local/jboss - -END_OF_SCRIPT + cat >> $INSTANCE_HOME/configure-jboss.sh <<-'END_OF_JCLOUDS_SCRIPT' + cd $INSTANCE_HOME + rm -f $INSTANCE_HOME/rc + trap 'echo $?>$INSTANCE_HOME/rc' 0 1 2 3 15 + cat > /etc/sudoers <<-'END_OF_JCLOUDS_FILE' + root ALL = (ALL) ALL + %wheel ALL = (ALL) NOPASSWD:ALL + END_OF_JCLOUDS_FILE + chmod 0440 /etc/sudoers + mkdir -p /home/users + groupadd -f wheel + useradd -s /bin/bash -g wheel -m -d /home/users/web -p 'crypt(randompassword)' web + mkdir -p /home/users/web/.ssh + cat >> /home/users/web/.ssh/authorized_keys <<-'END_OF_JCLOUDS_FILE' + publicKey + END_OF_JCLOUDS_FILE + chmod 600 /home/users/web/.ssh/authorized_keys + chown -R web /home/users/web + exec 3<> /etc/ssh/sshd_config && awk -v TEXT="PasswordAuthentication no + PermitRootLogin no + " 'BEGIN {print TEXT}{print}' /etc/ssh/sshd_config >&3 + hash service 2>/dev/null && service ssh reload || /etc/init.d/ssh* reload + awk -v user=^${SUDO_USER:=${USER}}: -v password='crypt(randompassword)' 'BEGIN { FS=OFS=":" } $0 ~ user { $2 = password } 1' /etc/shadow >/etc/shadow.${SUDO_USER:=${USER}} + test -f /etc/shadow.${SUDO_USER:=${USER}} && mv /etc/shadow.${SUDO_USER:=${USER}} /etc/shadow + setupPublicCurl || return 1 + curl -q -s -S -L --connect-timeout 10 --max-time 600 --retry 20 -X GET http://download.oracle.com/otn-pub/java/jdk/7/jdk-7-linux-x64.tar.gz |(mkdir -p /usr/local &&cd /usr/local &&tar -xpzf -) + mv /usr/local/jdk* /usr/local/jdk/ + test -n "$SUDO_USER" && + cat >> /home/$SUDO_USER/.bashrc <<-'END_OF_JCLOUDS_FILE' + export JAVA_HOME=/usr/local/jdk + export PATH=$JAVA_HOME/bin:$PATH + END_OF_JCLOUDS_FILE + cat >> /etc/bashrc <<-'END_OF_JCLOUDS_FILE' + export JAVA_HOME=/usr/local/jdk + export PATH=$JAVA_HOME/bin:$PATH + END_OF_JCLOUDS_FILE + cat >> $HOME/.bashrc <<-'END_OF_JCLOUDS_FILE' + export JAVA_HOME=/usr/local/jdk + export PATH=$JAVA_HOME/bin:$PATH + END_OF_JCLOUDS_FILE + cat >> /etc/skel/.bashrc <<-'END_OF_JCLOUDS_FILE' + export JAVA_HOME=/usr/local/jdk + export PATH=$JAVA_HOME/bin:$PATH + END_OF_JCLOUDS_FILE + ln -fs /usr/local/jdk/bin/java /usr/bin/java + iptables -I INPUT 1 -p tcp --dport 22 -j ACCEPT + iptables -I INPUT 1 -p tcp --dport 8080 -j ACCEPT + iptables-save + curl -q -s -S -L --connect-timeout 10 --max-time 600 --retry 20 -X GET http://download.jboss.org/jbossas/7.0/jboss-as-7.0.2.Final/jboss-as-web-7.0.2.Final.tar.gz |(mkdir -p /usr/local &&cd /usr/local &&tar -xpzf -) + mkdir -p /usr/local/jboss + mv /usr/local/jboss-*/* /usr/local/jboss + (cd /usr/local/jboss/standalone/configuration && sed 's~inet-address value=.*/~any-address/~g' standalone.xml > standalone.xml.new && mv standalone.xml.new standalone.xml) + chmod -R oug+r+w /usr/local/jboss + chown -R web /usr/local/jboss + +END_OF_JCLOUDS_SCRIPT # add runscript footer - cat >> $INSTANCE_HOME/configure-jboss.sh <<'END_OF_SCRIPT' -exit 0 -END_OF_SCRIPT + cat >> $INSTANCE_HOME/configure-jboss.sh <<-'END_OF_JCLOUDS_SCRIPT' + exit $? + +END_OF_JCLOUDS_SCRIPT chmod u+x $INSTANCE_HOME/configure-jboss.sh ;; @@ -210,6 +219,17 @@ start) default || exit 1 forget $INSTANCE_NAME $INSTANCE_HOME/$INSTANCE_NAME.sh $LOG_DIR || exit 1 ;; +stdout) + default || exit 1 + cat $LOG_DIR/stdout.log + ;; +stderr) + default || exit 1 + cat $LOG_DIR/stderr.log + ;; +exitstatus) + default || exit 1 + [ -f $LOG_DIR/rc ] && cat $LOG_DIR/rc;; tail) default || exit 1 tail $LOG_DIR/stdout.log @@ -223,4 +243,4 @@ run) $INSTANCE_HOME/$INSTANCE_NAME.sh ;; esac -exit 0 +exit $? diff --git a/compute/src/test/resources/runscript.sh b/compute/src/test/resources/runscript.sh index 63f8a73466..927daac349 100644 --- a/compute/src/test/resources/runscript.sh +++ b/compute/src/test/resources/runscript.sh @@ -10,11 +10,11 @@ function abort { function default { export INSTANCE_NAME="runScriptWithCreds" export INSTANCE_HOME="/tmp/runScriptWithCreds" -export LOG_DIR="/tmp/runScriptWithCreds" - return 0 +export LOG_DIR="$INSTANCE_HOME" + return $? } function runScriptWithCreds { - return 0 + return $? } function findPid { unset FOUND_PID; @@ -62,35 +62,41 @@ init) mkdir -p $INSTANCE_HOME # create runscript header - cat > $INSTANCE_HOME/runScriptWithCreds.sh <&2 + cat > $INSTANCE_HOME/runScriptWithCreds.sh <<-'END_OF_JCLOUDS_SCRIPT' + #!/bin/bash + set +u + shopt -s xpg_echo + shopt -s expand_aliases + + PROMPT_COMMAND='echo -ne \"\033]0;runScriptWithCreds\007\"' + export PATH=/usr/ucb/bin:/bin:/sbin:/usr/bin:/usr/sbin + + export INSTANCE_NAME='runScriptWithCreds' +END_OF_JCLOUDS_SCRIPT + cat >> $INSTANCE_HOME/runScriptWithCreds.sh <<-END_OF_JCLOUDS_SCRIPT + export INSTANCE_NAME='$INSTANCE_NAME' + export INSTANCE_HOME='$INSTANCE_HOME' + export LOG_DIR='$LOG_DIR' +END_OF_JCLOUDS_SCRIPT + cat >> $INSTANCE_HOME/runScriptWithCreds.sh <<-'END_OF_JCLOUDS_SCRIPT' + function abort { + echo "aborting: $@" 1>&2 exit 1 } alias apt-get-install="apt-get install -f -y -qq --force-yes" alias apt-get-upgrade="(apt-get update -qq&&apt-get upgrade -y -qq)" function ensure_cmd_or_install_package_apt(){ - local cmd=\$1 - local pkg=\$2 + local cmd=$1 + local pkg=$2 - hash \$cmd 2>/dev/null || apt-get-install \$pkg || ( apt-get-upgrade && apt-get-install \$pkg ) + hash $cmd 2>/dev/null || apt-get-install $pkg || ( apt-get-upgrade && apt-get-install $pkg ) } function ensure_cmd_or_install_package_yum(){ - local cmd=\$1 - local pkg=\$2 - hash \$cmd 2>/dev/null || yum --nogpgcheck -y ensure \$pkg + local cmd=$1 + local pkg=$2 + hash $cmd 2>/dev/null || yum --nogpgcheck -y ensure $pkg } function ensure_netutils_apt() { @@ -106,7 +112,7 @@ function ensure_netutils_yum() { # most network services require that the hostname is in # the /etc/hosts file, or they won't operate function ensure_hostname_in_hosts() { - egrep -q `hostname` /etc/hosts || awk -v hostname=`hostname` 'END { print \$1" "hostname }' /proc/net/arp >> /etc/hosts + egrep -q `hostname` /etc/hosts || awk -v hostname=`hostname` 'END { print $1" "hostname }' /proc/net/arp >> /etc/hosts } # download locations for many services are at public dns @@ -127,39 +133,43 @@ function setupPublicCurl() { ensure_can_resolve_public_dns return 0 } -END_OF_SCRIPT + +END_OF_JCLOUDS_SCRIPT # add desired commands from the user - cat >> $INSTANCE_HOME/runScriptWithCreds.sh <<'END_OF_SCRIPT' -cd $INSTANCE_HOME -setupPublicCurl || exit 1 -curl -q -s -S -L --connect-timeout 10 --max-time 600 --retry 20 -X GET http://download.oracle.com/otn-pub/java/jdk/7/jdk-7-linux-x64.tar.gz |(mkdir -p /usr/local &&cd /usr/local &&tar -xpzf -) -mv /usr/local/jdk* /usr/local/jdk/ -test -n "$SUDO_USER" && -cat >> /home/$SUDO_USER/.bashrc <<'END_OF_FILE' -export JAVA_HOME=/usr/local/jdk -export PATH=$JAVA_HOME/bin:$PATH -END_OF_FILE -cat >> /etc/bashrc <<'END_OF_FILE' -export JAVA_HOME=/usr/local/jdk -export PATH=$JAVA_HOME/bin:$PATH -END_OF_FILE -cat >> $HOME/.bashrc <<'END_OF_FILE' -export JAVA_HOME=/usr/local/jdk -export PATH=$JAVA_HOME/bin:$PATH -END_OF_FILE -cat >> /etc/skel/.bashrc <<'END_OF_FILE' -export JAVA_HOME=/usr/local/jdk -export PATH=$JAVA_HOME/bin:$PATH -END_OF_FILE -ln -fs /usr/local/jdk/bin/java /usr/bin/java - -END_OF_SCRIPT + cat >> $INSTANCE_HOME/runScriptWithCreds.sh <<-'END_OF_JCLOUDS_SCRIPT' + cd $INSTANCE_HOME + rm -f $INSTANCE_HOME/rc + trap 'echo $?>$INSTANCE_HOME/rc' 0 1 2 3 15 + setupPublicCurl || exit 1 + curl -q -s -S -L --connect-timeout 10 --max-time 600 --retry 20 -X GET http://download.oracle.com/otn-pub/java/jdk/7/jdk-7-linux-x64.tar.gz |(mkdir -p /usr/local &&cd /usr/local &&tar -xpzf -) + mv /usr/local/jdk* /usr/local/jdk/ + test -n "$SUDO_USER" && + cat >> /home/$SUDO_USER/.bashrc <<-'END_OF_JCLOUDS_FILE' + export JAVA_HOME=/usr/local/jdk + export PATH=$JAVA_HOME/bin:$PATH + END_OF_JCLOUDS_FILE + cat >> /etc/bashrc <<-'END_OF_JCLOUDS_FILE' + export JAVA_HOME=/usr/local/jdk + export PATH=$JAVA_HOME/bin:$PATH + END_OF_JCLOUDS_FILE + cat >> $HOME/.bashrc <<-'END_OF_JCLOUDS_FILE' + export JAVA_HOME=/usr/local/jdk + export PATH=$JAVA_HOME/bin:$PATH + END_OF_JCLOUDS_FILE + cat >> /etc/skel/.bashrc <<-'END_OF_JCLOUDS_FILE' + export JAVA_HOME=/usr/local/jdk + export PATH=$JAVA_HOME/bin:$PATH + END_OF_JCLOUDS_FILE + ln -fs /usr/local/jdk/bin/java /usr/bin/java + +END_OF_JCLOUDS_SCRIPT # add runscript footer - cat >> $INSTANCE_HOME/runScriptWithCreds.sh <<'END_OF_SCRIPT' -exit 0 -END_OF_SCRIPT + cat >> $INSTANCE_HOME/runScriptWithCreds.sh <<-'END_OF_JCLOUDS_SCRIPT' + exit $? + +END_OF_JCLOUDS_SCRIPT chmod u+x $INSTANCE_HOME/runScriptWithCreds.sh ;; @@ -180,6 +190,17 @@ start) default || exit 1 forget $INSTANCE_NAME $INSTANCE_HOME/$INSTANCE_NAME.sh $LOG_DIR || exit 1 ;; +stdout) + default || exit 1 + cat $LOG_DIR/stdout.log + ;; +stderr) + default || exit 1 + cat $LOG_DIR/stderr.log + ;; +exitstatus) + default || exit 1 + [ -f $LOG_DIR/rc ] && cat $LOG_DIR/rc;; tail) default || exit 1 tail $LOG_DIR/stdout.log @@ -193,4 +214,4 @@ run) $INSTANCE_HOME/$INSTANCE_NAME.sh ;; esac -exit 0 +exit $? diff --git a/compute/src/test/resources/runscript_adminUpdate.sh b/compute/src/test/resources/runscript_adminUpdate.sh index cd448eb461..068aa3457b 100644 --- a/compute/src/test/resources/runscript_adminUpdate.sh +++ b/compute/src/test/resources/runscript_adminUpdate.sh @@ -10,11 +10,11 @@ function abort { function default { export INSTANCE_NAME="adminUpdate" export INSTANCE_HOME="/tmp/adminUpdate" -export LOG_DIR="/tmp/adminUpdate" - return 0 +export LOG_DIR="$INSTANCE_HOME" + return $? } function adminUpdate { - return 0 + return $? } function findPid { unset FOUND_PID; @@ -62,50 +62,56 @@ init) mkdir -p $INSTANCE_HOME # create runscript header - cat > $INSTANCE_HOME/adminUpdate.sh < $INSTANCE_HOME/adminUpdate.sh <<-'END_OF_JCLOUDS_SCRIPT' + #!/bin/bash + set +u + shopt -s xpg_echo + shopt -s expand_aliases + + PROMPT_COMMAND='echo -ne \"\033]0;adminUpdate\007\"' + export PATH=/usr/ucb/bin:/bin:/sbin:/usr/bin:/usr/sbin + + export INSTANCE_NAME='adminUpdate' +END_OF_JCLOUDS_SCRIPT + cat >> $INSTANCE_HOME/adminUpdate.sh <<-END_OF_JCLOUDS_SCRIPT + export INSTANCE_NAME='$INSTANCE_NAME' + export INSTANCE_HOME='$INSTANCE_HOME' + export LOG_DIR='$LOG_DIR' +END_OF_JCLOUDS_SCRIPT # add desired commands from the user - cat >> $INSTANCE_HOME/adminUpdate.sh <<'END_OF_SCRIPT' -cd $INSTANCE_HOME -rm /etc/sudoers -cat >> /etc/sudoers <<'END_OF_FILE' -root ALL = (ALL) ALL -%wheel ALL = (ALL) NOPASSWD:ALL -END_OF_FILE -chmod 0440 /etc/sudoers -mkdir -p /home/users -groupadd -f wheel -useradd -s /bin/bash -g wheel -m -d /home/users/foo -p 'crypt(randompassword)' foo -mkdir -p /home/users/foo/.ssh -cat >> /home/users/foo/.ssh/authorized_keys <<'END_OF_FILE' -publicKey -END_OF_FILE -chmod 600 /home/users/foo/.ssh/authorized_keys -chown -R foo /home/users/foo -exec 3<> /etc/ssh/sshd_config && awk -v TEXT="PasswordAuthentication no -PermitRootLogin no -" 'BEGIN {print TEXT}{print}' /etc/ssh/sshd_config >&3 -/etc/init.d/sshd reload||/etc/init.d/ssh reload -awk -v user=^${SUDO_USER:=${USER}}: -v password='crypt(randompassword)' 'BEGIN { FS=OFS=":" } $0 ~ user { $2 = password } 1' /etc/shadow >/etc/shadow.${SUDO_USER:=${USER}} -test -f /etc/shadow.${SUDO_USER:=${USER}} && mv /etc/shadow.${SUDO_USER:=${USER}} /etc/shadow - -END_OF_SCRIPT + cat >> $INSTANCE_HOME/adminUpdate.sh <<-'END_OF_JCLOUDS_SCRIPT' + cd $INSTANCE_HOME + rm -f $INSTANCE_HOME/rc + trap 'echo $?>$INSTANCE_HOME/rc' 0 1 2 3 15 + cat > /etc/sudoers <<-'END_OF_JCLOUDS_FILE' + root ALL = (ALL) ALL + %wheel ALL = (ALL) NOPASSWD:ALL + END_OF_JCLOUDS_FILE + chmod 0440 /etc/sudoers + mkdir -p /home/users + groupadd -f wheel + useradd -s /bin/bash -g wheel -m -d /home/users/foo -p 'crypt(randompassword)' foo + mkdir -p /home/users/foo/.ssh + cat >> /home/users/foo/.ssh/authorized_keys <<-'END_OF_JCLOUDS_FILE' + publicKey + END_OF_JCLOUDS_FILE + chmod 600 /home/users/foo/.ssh/authorized_keys + chown -R foo /home/users/foo + exec 3<> /etc/ssh/sshd_config && awk -v TEXT="PasswordAuthentication no + PermitRootLogin no + " 'BEGIN {print TEXT}{print}' /etc/ssh/sshd_config >&3 + hash service 2>/dev/null && service ssh reload || /etc/init.d/ssh* reload + awk -v user=^${SUDO_USER:=${USER}}: -v password='crypt(randompassword)' 'BEGIN { FS=OFS=":" } $0 ~ user { $2 = password } 1' /etc/shadow >/etc/shadow.${SUDO_USER:=${USER}} + test -f /etc/shadow.${SUDO_USER:=${USER}} && mv /etc/shadow.${SUDO_USER:=${USER}} /etc/shadow + +END_OF_JCLOUDS_SCRIPT # add runscript footer - cat >> $INSTANCE_HOME/adminUpdate.sh <<'END_OF_SCRIPT' -exit 0 -END_OF_SCRIPT + cat >> $INSTANCE_HOME/adminUpdate.sh <<-'END_OF_JCLOUDS_SCRIPT' + exit $? + +END_OF_JCLOUDS_SCRIPT chmod u+x $INSTANCE_HOME/adminUpdate.sh ;; @@ -126,6 +132,17 @@ start) default || exit 1 forget $INSTANCE_NAME $INSTANCE_HOME/$INSTANCE_NAME.sh $LOG_DIR || exit 1 ;; +stdout) + default || exit 1 + cat $LOG_DIR/stdout.log + ;; +stderr) + default || exit 1 + cat $LOG_DIR/stderr.log + ;; +exitstatus) + default || exit 1 + [ -f $LOG_DIR/rc ] && cat $LOG_DIR/rc;; tail) default || exit 1 tail $LOG_DIR/stdout.log @@ -139,4 +156,4 @@ run) $INSTANCE_HOME/$INSTANCE_NAME.sh ;; esac -exit 0 +exit $? diff --git a/compute/src/test/resources/runscript_jboss.sh b/compute/src/test/resources/runscript_jboss.sh index 508b954785..a919188c91 100644 --- a/compute/src/test/resources/runscript_jboss.sh +++ b/compute/src/test/resources/runscript_jboss.sh @@ -10,12 +10,12 @@ function abort { function default { export INSTANCE_NAME="jboss" export INSTANCE_HOME="/usr/local/jboss" -export LOG_DIR="/usr/local/jboss" - return 0 +export LOG_DIR="$INSTANCE_HOME" + return $? } function jboss { export JBOSS_HOME="/usr/local/jboss" - return 0 + return $? } function findPid { unset FOUND_PID; @@ -60,139 +60,146 @@ case $1 in init) default || exit 1 jboss || exit 1 - cat >> /usr/local/jboss/standalone/configuration/standalone-custom.xml <<'END_OF_FILE' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -END_OF_FILE + cat >> /usr/local/jboss/standalone/configuration/standalone-custom.xml <<-'END_OF_JCLOUDS_FILE' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +END_OF_JCLOUDS_FILE mkdir -p $INSTANCE_HOME # create runscript header - cat > $INSTANCE_HOME/jboss.sh < $INSTANCE_HOME/jboss.sh <<-'END_OF_JCLOUDS_SCRIPT' + #!/bin/bash + set +u + shopt -s xpg_echo + shopt -s expand_aliases + + PROMPT_COMMAND='echo -ne \"\033]0;jboss\007\"' + export PATH=/usr/ucb/bin:/bin:/sbin:/usr/bin:/usr/sbin + + export INSTANCE_NAME='jboss' +END_OF_JCLOUDS_SCRIPT + cat >> $INSTANCE_HOME/jboss.sh <<-END_OF_JCLOUDS_SCRIPT + export JBOSS_HOME='$JBOSS_HOME' + export INSTANCE_NAME='$INSTANCE_NAME' + export INSTANCE_HOME='$INSTANCE_HOME' + export LOG_DIR='$LOG_DIR' +END_OF_JCLOUDS_SCRIPT # add desired commands from the user - cat >> $INSTANCE_HOME/jboss.sh <<'END_OF_SCRIPT' -cd $INSTANCE_HOME -java -server -Xms128m -Xmx128m -XX:MaxPermSize=128m -Djava.net.preferIPv4Stack=true -XX:+UseFastAccessorMethods -XX:+TieredCompilation -Xverify:none -Dorg.jboss.resolver.warning=true -Dsun.rmi.dgc.client.gcInterval=3600000 -Dsun.rmi.dgc.server.gcInterval=3600000 -Djboss.modules.system.pkgs=org.jboss.byteman -Dorg.jboss.boot.log.file=$JBOSS_HOME/standalone/log/boot.log -Dlogging.configuration=file:$JBOSS_HOME/standalone/configuration/logging.properties -jar $JBOSS_HOME/jboss-modules.jar -mp $JBOSS_HOME/modules -logmodule org.jboss.logmanager -jaxpmodule javax.xml.jaxp-provider org.jboss.as.standalone -Djboss.home.dir=$JBOSS_HOME --server-config=standalone-custom.xml -END_OF_SCRIPT + cat >> $INSTANCE_HOME/jboss.sh <<-'END_OF_JCLOUDS_SCRIPT' + cd $INSTANCE_HOME + rm -f $INSTANCE_HOME/rc + trap 'echo $?>$INSTANCE_HOME/rc' 0 1 2 3 15 + java -server -Xms128m -Xmx128m -XX:MaxPermSize=128m -Djava.net.preferIPv4Stack=true -XX:+UseFastAccessorMethods -XX:+TieredCompilation -Xverify:none -Dorg.jboss.resolver.warning=true -Dsun.rmi.dgc.client.gcInterval=3600000 -Dsun.rmi.dgc.server.gcInterval=3600000 -Djboss.modules.system.pkgs=org.jboss.byteman -Dorg.jboss.boot.log.file=$JBOSS_HOME/standalone/log/boot.log -Dlogging.configuration=file:$JBOSS_HOME/standalone/configuration/logging.properties -jar $JBOSS_HOME/jboss-modules.jar -mp $JBOSS_HOME/modules -logmodule org.jboss.logmanager -jaxpmodule javax.xml.jaxp-provider org.jboss.as.standalone -Djboss.home.dir=$JBOSS_HOME --server-config=standalone-custom.xml +END_OF_JCLOUDS_SCRIPT # add runscript footer - cat >> $INSTANCE_HOME/jboss.sh <<'END_OF_SCRIPT' -exit 0 -END_OF_SCRIPT + cat >> $INSTANCE_HOME/jboss.sh <<-'END_OF_JCLOUDS_SCRIPT' + exit $? + +END_OF_JCLOUDS_SCRIPT chmod u+x $INSTANCE_HOME/jboss.sh ;; @@ -213,6 +220,17 @@ start) default || exit 1 forget $INSTANCE_NAME $INSTANCE_HOME/$INSTANCE_NAME.sh $LOG_DIR || exit 1 ;; +stdout) + default || exit 1 + cat $LOG_DIR/stdout.log + ;; +stderr) + default || exit 1 + cat $LOG_DIR/stderr.log + ;; +exitstatus) + default || exit 1 + [ -f $LOG_DIR/rc ] && cat $LOG_DIR/rc;; tail) default || exit 1 tail $LOG_DIR/stdout.log @@ -226,4 +244,4 @@ run) $INSTANCE_HOME/$INSTANCE_NAME.sh ;; esac -exit 0 +exit $? diff --git a/core/src/main/java/org/jclouds/concurrent/config/ExecutorServiceModule.java b/core/src/main/java/org/jclouds/concurrent/config/ExecutorServiceModule.java index 5097696e94..99d8601a4c 100644 --- a/core/src/main/java/org/jclouds/concurrent/config/ExecutorServiceModule.java +++ b/core/src/main/java/org/jclouds/concurrent/config/ExecutorServiceModule.java @@ -45,6 +45,8 @@ import org.jclouds.lifecycle.Closer; import org.jclouds.logging.Logger; import com.google.common.annotations.VisibleForTesting; +import com.google.common.eventbus.AsyncEventBus; +import com.google.common.eventbus.EventBus; import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.google.inject.AbstractModule; import com.google.inject.Provides; @@ -320,7 +322,13 @@ public class ExecutorServiceModule extends AbstractModule { } } - + + @Provides + @Singleton + EventBus provideEventBus(@Named(Constants.PROPERTY_USER_THREADS) ExecutorService userThreads){ + return new AsyncEventBus(userThreads); + } + @Provides @Singleton @Named(Constants.PROPERTY_USER_THREADS) diff --git a/core/src/main/java/org/jclouds/predicates/RetryablePredicate.java b/core/src/main/java/org/jclouds/predicates/RetryablePredicate.java index aa2de5a9d8..79d142b1ef 100644 --- a/core/src/main/java/org/jclouds/predicates/RetryablePredicate.java +++ b/core/src/main/java/org/jclouds/predicates/RetryablePredicate.java @@ -21,6 +21,7 @@ package org.jclouds.predicates; import static org.jclouds.util.Throwables2.getFirstThrowableOfType; import java.util.Date; +import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; @@ -88,6 +89,9 @@ public class RetryablePredicate implements Predicate { } else if (getFirstThrowableOfType(e, IllegalStateException.class) != null) { logger.warn(e, "predicate %s on %s illegal state [%s], returning false", input, predicate, e.getMessage()); return false; + } else if (getFirstThrowableOfType(e, CancellationException.class) != null) { + logger.warn(e, "predicate %s on %s cancelled [%s], returning false", input, predicate, e.getMessage()); + return false; } else if (getFirstThrowableOfType(e, TimeoutException.class) != null) { logger.warn(e, "predicate %s on %s timed out [%s], returning false", input, predicate, e.getMessage()); return false; diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/predicates/GuestAdditionsInstaller.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/predicates/GuestAdditionsInstaller.java index 4e258897f2..7c8d6c8613 100644 --- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/predicates/GuestAdditionsInstaller.java +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/predicates/GuestAdditionsInstaller.java @@ -39,7 +39,7 @@ public class GuestAdditionsInstaller implements Predicate { ListenableFuture execFuture = context.getComputeService().submitScriptOnNode(vmName, new InstallGuestAdditions(vboxVersion), RunScriptOptions.NONE); ExecResponse execResponse = Futures.getUnchecked(execFuture); - return execResponse == null ? false : execResponse.getExitCode() == 0; + return execResponse == null ? false : execResponse.getExitStatus() == 0; } } \ No newline at end of file diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/predicates/SshAvailable.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/predicates/SshAvailable.java index ffede70326..418b7dcec9 100644 --- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/predicates/SshAvailable.java +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/predicates/SshAvailable.java @@ -53,7 +53,7 @@ public class SshAvailable implements Predicate { try { if (context.getComputeService() .runScriptOnNode(nodeId, "id", wrapInInitScript(false).runAsRoot(false)) - .getExitCode() == 0) { + .getExitStatus() == 0) { logger.debug("Got response from ssh daemon running on %s", nodeId); sshDaemonIsRunning = true; } diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/predicates/SshResponds.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/predicates/SshResponds.java index 676f42a169..fe9f8c8917 100644 --- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/predicates/SshResponds.java +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/predicates/SshResponds.java @@ -39,7 +39,7 @@ public class SshResponds implements Predicate { try { client.connect(); - if (client.exec("id").getExitCode() == 0) { + if (client.exec("id").getExitStatus() == 0) { return true; } } catch (SshException e) { diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/statements/InstallGuestAdditions.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/statements/InstallGuestAdditions.java index 8d26a1f18b..6d6e5a7122 100644 --- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/statements/InstallGuestAdditions.java +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/statements/InstallGuestAdditions.java @@ -51,8 +51,7 @@ public class InstallGuestAdditions extends StatementList { @Override public String render(OsFamily family) { - checkNotNull(family, "family"); - if (family == OsFamily.WINDOWS) + if (checkNotNull(family, "family") == OsFamily.WINDOWS) throw new UnsupportedOperationException("windows not yet implemented"); return super.render(family); } diff --git a/labs/virtualbox/src/test/java/org/jclouds/virtualbox/domain/InstallGuestAdditionsTest.java b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/domain/InstallGuestAdditionsTest.java index d989deae58..1fdc34ff28 100644 --- a/labs/virtualbox/src/test/java/org/jclouds/virtualbox/domain/InstallGuestAdditionsTest.java +++ b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/domain/InstallGuestAdditionsTest.java @@ -22,7 +22,7 @@ import static org.testng.Assert.assertEquals; import java.io.IOException; -import org.jclouds.scriptbuilder.InitBuilder; +import org.jclouds.scriptbuilder.InitScript; import org.jclouds.scriptbuilder.domain.OsFamily; import org.jclouds.scriptbuilder.domain.ShellToken; import org.jclouds.scriptbuilder.domain.Statement; @@ -30,7 +30,6 @@ import org.jclouds.virtualbox.statements.InstallGuestAdditions; import org.testng.annotations.Test; import com.google.common.base.Charsets; -import com.google.common.collect.ImmutableSet; import com.google.common.io.CharStreams; import com.google.common.io.Resources; @@ -47,8 +46,8 @@ public class InstallGuestAdditionsTest { @Test public void testUnixInInitScript() throws IOException { - Statement statement = new InitBuilder("install_guest_additions", ImmutableSet. of(), - ImmutableSet. of(new InstallGuestAdditions("4.1.6"))); + Statement statement = InitScript.builder().name("install_guest_additions") + .run(new InstallGuestAdditions("4.1.6")).build(); assertEquals(statement.render(OsFamily.UNIX), CharStreams.toString(Resources.newReaderSupplier( Resources.getResource("test_guest_additions_installer_init." + ShellToken.SH.to(OsFamily.UNIX)), diff --git a/labs/virtualbox/src/test/resources/test_export_ip_address_from_vm_named.sh b/labs/virtualbox/src/test/resources/test_export_ip_address_from_vm_named.sh index ebfd770645..52fd3b904c 100644 --- a/labs/virtualbox/src/test/resources/test_export_ip_address_from_vm_named.sh +++ b/labs/virtualbox/src/test/resources/test_export_ip_address_from_vm_named.sh @@ -26,4 +26,4 @@ function exportIpAddressFromVmNamed { export PATH=/usr/ucb/bin:/bin:/sbin:/usr/bin:/usr/sbin exportIpAddressFromVmNamed $@ || exit 1 echo $FOUND_IP_ADDRESS -exit 0 +exit $? diff --git a/labs/virtualbox/src/test/resources/test_guest_additions_installer_init.sh b/labs/virtualbox/src/test/resources/test_guest_additions_installer_init.sh index a27e2c27ab..47c37dbe14 100644 --- a/labs/virtualbox/src/test/resources/test_guest_additions_installer_init.sh +++ b/labs/virtualbox/src/test/resources/test_guest_additions_installer_init.sh @@ -9,12 +9,12 @@ function abort { } function default { export INSTANCE_NAME="install_guest_additions" -export INSTANCE_HOME="$HOME/instances/install_guest_additions" -export LOG_DIR="$HOME/instances/install_guest_additions" - return 0 +export INSTANCE_HOME="/tmp/$INSTANCE_NAME" +export LOG_DIR="$INSTANCE_HOME" + return $? } function install_guest_additions { - return 0 + return $? } function findPid { unset FOUND_PID; @@ -62,35 +62,41 @@ init) mkdir -p $INSTANCE_HOME # create runscript header - cat > $INSTANCE_HOME/install_guest_additions.sh <&2 + cat > $INSTANCE_HOME/install_guest_additions.sh <<-'END_OF_JCLOUDS_SCRIPT' + #!/bin/bash + set +u + shopt -s xpg_echo + shopt -s expand_aliases + + PROMPT_COMMAND='echo -ne \"\033]0;install_guest_additions\007\"' + export PATH=/usr/ucb/bin:/bin:/sbin:/usr/bin:/usr/sbin + + export INSTANCE_NAME='install_guest_additions' +END_OF_JCLOUDS_SCRIPT + cat >> $INSTANCE_HOME/install_guest_additions.sh <<-END_OF_JCLOUDS_SCRIPT + export INSTANCE_NAME='$INSTANCE_NAME' + export INSTANCE_HOME='$INSTANCE_HOME' + export LOG_DIR='$LOG_DIR' +END_OF_JCLOUDS_SCRIPT + cat >> $INSTANCE_HOME/install_guest_additions.sh <<-'END_OF_JCLOUDS_SCRIPT' + function abort { + echo "aborting: $@" 1>&2 exit 1 } alias apt-get-install="apt-get install -f -y -qq --force-yes" alias apt-get-upgrade="(apt-get update -qq&&apt-get upgrade -y -qq)" function ensure_cmd_or_install_package_apt(){ - local cmd=\$1 - local pkg=\$2 + local cmd=$1 + local pkg=$2 - hash \$cmd 2>/dev/null || apt-get-install \$pkg || ( apt-get-upgrade && apt-get-install \$pkg ) + hash $cmd 2>/dev/null || apt-get-install $pkg || ( apt-get-upgrade && apt-get-install $pkg ) } function ensure_cmd_or_install_package_yum(){ - local cmd=\$1 - local pkg=\$2 - hash \$cmd 2>/dev/null || yum --nogpgcheck -y ensure \$pkg + local cmd=$1 + local pkg=$2 + hash $cmd 2>/dev/null || yum --nogpgcheck -y ensure $pkg } function ensure_netutils_apt() { @@ -106,7 +112,7 @@ function ensure_netutils_yum() { # most network services require that the hostname is in # the /etc/hosts file, or they won't operate function ensure_hostname_in_hosts() { - egrep -q `hostname` /etc/hosts || awk -v hostname=`hostname` 'END { print \$1" "hostname }' /proc/net/arp >> /etc/hosts + egrep -q `hostname` /etc/hosts || awk -v hostname=`hostname` 'END { print $1" "hostname }' /proc/net/arp >> /etc/hosts } # download locations for many services are at public dns @@ -130,31 +136,40 @@ function setupPublicCurl() { function installModuleAssistantIfNeeded { unset OSNAME; local OSNAME=`lsb_release -d -s | cut -d ' ' -f 1`; shift - if [ \$OSNAME = 'Ubuntu' ] + if [ $OSNAME = 'Ubuntu' ] then echo "OS is Ubuntu" apt-get -f -y -qq --force-yes install build-essential module-assistant; m-a prepare -i fi } -END_OF_SCRIPT + +END_OF_JCLOUDS_SCRIPT # add desired commands from the user - cat >> $INSTANCE_HOME/install_guest_additions.sh <<'END_OF_SCRIPT' -cd $INSTANCE_HOME -setupPublicCurl || exit 1 -(mkdir -p /tmp/ && cd /tmp/ && [ ! -f VBoxGuestAdditions_4.1.6.iso ] && curl -q -s -S -L --connect-timeout 10 --max-time 600 --retry 20 -C - -X GET http://download.virtualbox.org/virtualbox/4.1.6/VBoxGuestAdditions_4.1.6.iso >VBoxGuestAdditions_4.1.6.iso) -mount -o loop /tmp/VBoxGuestAdditions_4.1.6.iso /mnt -installModuleAssistantIfNeeded || exit 1 -/mnt/VBoxLinuxAdditions.run -umount /mnt - -END_OF_SCRIPT + cat >> $INSTANCE_HOME/install_guest_additions.sh <<-'END_OF_JCLOUDS_SCRIPT' + cd $INSTANCE_HOME + rm -f $INSTANCE_HOME/rc + trap 'echo $?>$INSTANCE_HOME/rc' 0 1 2 3 15 + setupPublicCurl || exit 1 + + (mkdir -p /tmp/ && cd /tmp/ && [ ! -f VBoxGuestAdditions_4.1.6.iso ] && curl -q -s -S -L --connect-timeout 10 --max-time 600 --retry 20 -C - -X GET http://download.virtualbox.org/virtualbox/4.1.6/VBoxGuestAdditions_4.1.6.iso >VBoxGuestAdditions_4.1.6.iso) + + mount -o loop /tmp/VBoxGuestAdditions_4.1.6.iso /mnt + + installModuleAssistantIfNeeded || exit 1 + + /mnt/VBoxLinuxAdditions.run + + umount /mnt + +END_OF_JCLOUDS_SCRIPT # add runscript footer - cat >> $INSTANCE_HOME/install_guest_additions.sh <<'END_OF_SCRIPT' -exit 0 -END_OF_SCRIPT + cat >> $INSTANCE_HOME/install_guest_additions.sh <<-'END_OF_JCLOUDS_SCRIPT' + exit $? + +END_OF_JCLOUDS_SCRIPT chmod u+x $INSTANCE_HOME/install_guest_additions.sh ;; @@ -175,6 +190,17 @@ start) default || exit 1 forget $INSTANCE_NAME $INSTANCE_HOME/$INSTANCE_NAME.sh $LOG_DIR || exit 1 ;; +stdout) + default || exit 1 + cat $LOG_DIR/stdout.log + ;; +stderr) + default || exit 1 + cat $LOG_DIR/stderr.log + ;; +exitstatus) + default || exit 1 + [ -f $LOG_DIR/rc ] && cat $LOG_DIR/rc;; tail) default || exit 1 tail $LOG_DIR/stdout.log @@ -188,4 +214,4 @@ run) $INSTANCE_HOME/$INSTANCE_NAME.sh ;; esac -exit 0 +exit $? diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/InitBuilder.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/InitBuilder.java index 4d8b01d0ad..74fc896a3f 100644 --- a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/InitBuilder.java +++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/InitBuilder.java @@ -38,8 +38,10 @@ import com.google.common.collect.Iterables; /** * Creates an init script file * + * @see InitScript * @author Adrian Cole */ +@Deprecated public class InitBuilder extends ScriptBuilder { private final String instanceName; @@ -53,8 +55,8 @@ public class InitBuilder extends ScriptBuilder { } public InitBuilder(String instanceName, Iterable initStatements, Iterable statements) { - this(instanceName, String.format("{varl}HOME{varr}{fs}instances{fs}%s", instanceName), String.format( - "{varl}HOME{varr}{fs}instances{fs}%s", instanceName), ImmutableMap. of(), initStatements, + this(instanceName, String.format("{tmp}{fs}{varl}INSTANCE_NAME{varr}", instanceName), String.format( + "{tmp}{fs}{varl}INSTANCE_NAME{varr}", instanceName), ImmutableMap. of(), initStatements, statements); } diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/InitScript.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/InitScript.java new file mode 100644 index 0000000000..439c96f1b6 --- /dev/null +++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/InitScript.java @@ -0,0 +1,329 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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.jclouds.scriptbuilder; + +import static com.google.common.base.Objects.equal; +import static com.google.common.base.Objects.toStringHelper; +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.Iterables.concat; +import static java.lang.String.format; +import static org.jclouds.scriptbuilder.ScriptBuilder.call; +import static org.jclouds.scriptbuilder.ScriptBuilder.findPid; +import static org.jclouds.scriptbuilder.ScriptBuilder.forget; +import static org.jclouds.scriptbuilder.domain.Statements.createRunScript; +import static org.jclouds.scriptbuilder.domain.Statements.interpret; +import static org.jclouds.scriptbuilder.domain.Statements.kill; +import static org.jclouds.scriptbuilder.domain.Statements.newStatementList; +import static org.jclouds.scriptbuilder.domain.Statements.switchArg; + +import java.util.Map; + +import org.jclouds.scriptbuilder.domain.AcceptsStatementVisitor; +import org.jclouds.scriptbuilder.domain.CreateRunScript; +import org.jclouds.scriptbuilder.domain.OsFamily; +import org.jclouds.scriptbuilder.domain.Statement; +import org.jclouds.scriptbuilder.domain.StatementList; +import org.jclouds.scriptbuilder.domain.StatementVisitor; + +import com.google.common.base.Objects; +import com.google.common.collect.ForwardingObject; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; + +/** + * Creates an init script file + * + * @author Adrian Cole + */ +public class InitScript extends ForwardingObject implements Statement, AcceptsStatementVisitor { + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + protected String instanceName; + protected String instanceHome = "{tmp}{fs}{varl}INSTANCE_NAME{varr}"; + protected String logDir = "{varl}INSTANCE_HOME{varr}"; + protected Map exports = ImmutableMap.of(); + protected StatementList init = new StatementList(); + protected StatementList run = new StatementList(); + + /** + * @see InitScript#getInstanceName() + */ + public Builder name(String instanceName) { + this.instanceName = checkNotNull(instanceName, "instanceName"); + return this; + } + + /** + * @see InitScript#getInstanceHome() + */ + public Builder home(String instanceHome) { + this.instanceHome = checkNotNull(instanceHome, "instanceHome"); + return this; + } + + /** + * @see InitScript#getLogDir() + */ + public Builder logDir(String logDir) { + this.logDir = checkNotNull(logDir, "logDir"); + return this; + } + + /** + * @see InitScript#getExportedVariables() + */ + public Builder exportVariables(Map exports) { + this.exports = ImmutableMap.copyOf(checkNotNull(exports, "exports")); + return this; + } + + /** + * @see InitScript#getRun() + */ + public Builder run(Statement run) { + this.run = new StatementList(checkNotNull(run, "run")); + return this; + } + + /** + * @see InitScript#getRun() + */ + public Builder run(Statement... run) { + this.run = new StatementList(checkNotNull(run, "run")); + return this; + } + + /** + * @see InitScript#getRun() + */ + public Builder run(Iterable run) { + this.run = new StatementList(checkNotNull(run, "run")); + return this; + } + + /** + * @see InitScript#getRun() + */ + public Builder run(StatementList run) { + this.run = checkNotNull(run, "run"); + return this; + } + + /** + * @see InitScript#getInit() + */ + public Builder init(Statement init) { + this.init = new StatementList(checkNotNull(init, "init")); + return this; + } + + /** + * @see InitScript#getInit() + */ + public Builder init(Statement... init) { + this.init = new StatementList(checkNotNull(init, "init")); + return this; + } + + /** + * @see InitScript#getInit() + */ + public Builder init(Iterable init) { + this.init = new StatementList(checkNotNull(init, "init")); + return this; + } + + /** + * @see InitScript#getInit() + */ + public Builder init(StatementList init) { + this.init = checkNotNull(init, "init"); + return this; + } + + public InitScript build() { + return new InitScript(instanceName, instanceHome, logDir, exports, init, run); + } + + } + + protected final String instanceName; + protected final String instanceHome; + protected final String logDir; + protected final Map exports; + protected final StatementList init; + protected final StatementList run; + protected final ScriptBuilder delegate; + + protected InitScript(String instanceName, String instanceHome, String logDir, Map exports, + StatementList init, StatementList run) { + this.instanceName = checkNotNull(instanceName, "instanceName"); + this.instanceHome = checkNotNull(instanceHome, "instanceHome"); + this.logDir = checkNotNull(logDir, "logDir"); + this.exports = ImmutableMap. copyOf(checkNotNull(exports, "exports")); + this.init = checkNotNull(init, "init"); + this.run = checkNotNull(run, "run"); + checkArgument(run.delegate().size() > 0, "you must specify at least one statement to run"); + this.delegate = makeInitScriptStatement(instanceName, instanceHome, logDir, exports, init, run); + } + + public static ScriptBuilder makeInitScriptStatement(String instanceName, String instanceHome, String logDir, + Map exports, StatementList init, StatementList run) { + Map defaultExports = ImmutableMap.of("instanceName", instanceName, "instanceHome", instanceHome, + "logDir", logDir); + String exitStatusFile = format("%s/rc", logDir); + run = new StatementList(ImmutableList. builder().add(interpret("rm -f " + exitStatusFile)) + .add(interpret(format("trap 'echo $?>%s' 0 1 2 3 15", exitStatusFile))).addAll(run.delegate()).build()); + + CreateRunScript createRunScript = createRunScript(instanceName, + concat(exports.keySet(), defaultExports.keySet()), "{varl}INSTANCE_HOME{varr}", run); + + return new ScriptBuilder() + .addEnvironmentVariableScope("default", defaultExports) + .addEnvironmentVariableScope(instanceName, exports) + .addStatement( + switchArg( + 1, + new ImmutableMap.Builder() + .put("init", newStatementList(call("default"), call(instanceName), init, createRunScript)) + .put("status", + newStatementList(call("default"), findPid("{varl}INSTANCE_NAME{varr}"), + interpret("echo [{varl}FOUND_PID{varr}]{lf}"))) + .put("stop", + newStatementList(call("default"), findPid("{varl}INSTANCE_NAME{varr}"), kill())) + .put("start", + newStatementList( + call("default"), + forget("{varl}INSTANCE_NAME{varr}", + "{varl}INSTANCE_HOME{varr}{fs}{varl}INSTANCE_NAME{varr}.{sh}", + "{varl}LOG_DIR{varr}"))) + .put("stdout", + newStatementList(call("default"), + interpret("cat {varl}LOG_DIR{varr}{fs}stdout.log{lf}"))) + .put("stderr", + newStatementList(call("default"), + interpret("cat {varl}LOG_DIR{varr}{fs}stderr.log{lf}"))) + .put("exitstatus", + newStatementList(call("default"), + interpret("[ -f $LOG_DIR/rc ] && cat $LOG_DIR/rc"))) + .put("tail", + newStatementList(call("default"), + interpret("tail {varl}LOG_DIR{varr}{fs}stdout.log{lf}"))) + .put("tailerr", + newStatementList(call("default"), + interpret("tail {varl}LOG_DIR{varr}{fs}stderr.log{lf}"))) + .put("run", + newStatementList(call("default"), + interpret("{varl}INSTANCE_HOME{varr}{fs}{varl}INSTANCE_NAME{varr}.{sh}{lf}"))) + .build())); + } + + /** + * + * @return what will be bound to the INSTANCE_NAME variable, and uniquely + * identifies the process + */ + public String getInstanceName() { + return instanceName; + } + + /** + * default {@code /tmp/$INSTANCE_NAME} + *
+ *

note

The parent directory + * should be set with unix sticky-bit or otherwise made available to all + * users. Otherwise, new instances by other users may fail due to not being + * able to create a directory. This is why the default is set to {@code /tmp} + * + * @return what will be bound to the INSTANCE_HOME variable, and represents + * the working directory of the instance + */ + public String getInstanceHome() { + return instanceHome; + } + + /** + * default {@code $INSTANCE_HOME} + * + * @return what will be bound to the LOG_DIR variable, and represents where + * stdout and stderr.logs are written. + */ + public String getLogDir() { + return logDir; + } + + /** + * + * @return statements that will be executed upon the init command + */ + public StatementList getInitStatement() { + return init; + } + + /** + * + * @return statements that will be executed upon the run or start commands + */ + public StatementList getRunStatement() { + return init; + } + + @Override + public int hashCode() { + return Objects.hashCode(instanceName); + } + + @Override + public boolean equals(Object that) { + if (that == null) + return false; + return equal(this.toString(), that.toString()); + } + + @Override + public String toString() { + return toStringHelper(this).add("instanceName", instanceName).toString(); + } + + @Override + public void accept(StatementVisitor visitor) { + delegate().accept(visitor); + } + + @Override + public Iterable functionDependencies(OsFamily family) { + return delegate().functionDependencies(family); + } + + @Override + public String render(OsFamily family) { + return delegate().render(family); + } + + @Override + protected ScriptBuilder delegate() { + return delegate; + } + +} \ No newline at end of file diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/AppendFile.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/AppendFile.java index 3d16d26352..4f674cf5de 100644 --- a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/AppendFile.java +++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/AppendFile.java @@ -23,13 +23,12 @@ import static com.google.common.base.Preconditions.checkState; import static org.jclouds.scriptbuilder.domain.Statements.interpret; import java.util.Collections; -import java.util.List; import java.util.Map; import java.util.Map.Entry; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; -import com.google.common.collect.Lists; import com.google.common.collect.Maps; /** @@ -38,21 +37,66 @@ import com.google.common.collect.Maps; * @author Adrian Cole */ public class AppendFile implements Statement { - public static final String MARKER = "END_OF_FILE"; + public static final String DELIMETER = "END_OF_JCLOUDS_FILE"; - final String path; - final Iterable lines; - final String marker; - - public AppendFile(String path, Iterable lines) { - this(path, lines, MARKER); + public static Builder builder() { + return new Builder(); } - public AppendFile(String path, Iterable lines, String marker) { + public static class Builder { + protected String path; + protected Iterable lines = ImmutableSet.of(); + protected String delimeter = DELIMETER; + protected boolean expandVariables; + + /** + * @see AppendFile#getPath() + */ + public Builder path(String path) { + this.path = path; + return this; + } + + /** + * @see AppendFile#getLines() + */ + public Builder lines(Iterable lines) { + this.lines = ImmutableList.copyOf(lines); + return this; + } + + /** + * @see AppendFile#getDelimeter() + */ + public Builder delimeter(String delimeter) { + this.delimeter = delimeter; + return this; + } + + /** + * @see AppendFile#shouldExpandVariables() + */ + public Builder expandVariables(boolean expandVariables) { + this.expandVariables = expandVariables; + return this; + } + + public AppendFile build() { + return new AppendFile(path, lines, delimeter, expandVariables); + } + } + + protected final String path; + protected final Iterable lines; + protected final String delimeter; + protected final boolean expandVariables; + + protected AppendFile(String path, Iterable lines, String delimeter, boolean expandVariables) { this.path = checkNotNull(path, "path"); this.lines = checkNotNull(lines, "lines"); - this.marker = checkNotNull(marker, "marker"); + this.delimeter = checkNotNull(delimeter, "delimeter"); checkState(Iterables.size(lines) > 0, "you must pass something to execute"); + this.expandVariables = expandVariables; } public static String escapeVarTokens(String toEscape, OsFamily family) { @@ -76,35 +120,45 @@ public class AppendFile implements Statement { @Override public String render(OsFamily family) { - List statements = Lists.newArrayList(); if (family == OsFamily.UNIX) { - StringBuilder builder = new StringBuilder(); - hereFile(path, builder); - statements.add(interpret(builder.toString())); + return interpret(hereFile()).render(family); } else { - for (String line : lines) { - statements.add(appendToFile(line, path, family)); - } + return interpret(appendToWindowsFile()).render(family); } - return new StatementList(statements).render(family); } - protected void hereFile(String path, StringBuilder builder) { - builder.append("cat >> ").append(path).append(" <<'").append(marker).append("'\n"); + protected String appendToWindowsFile() { + StringBuilder builder = new StringBuilder(); for (String line : lines) { - builder.append(line).append("\n"); + builder.append(appendLineToWindowsFile(line, path)); } - builder.append(marker).append("\n"); + return builder.toString(); } - protected Statement appendToFile(String line, String path, OsFamily family) { + protected String hereFile() { + StringBuilder hereFile = startHereFile(); + for (String line : lines) { + hereFile.append('\t').append(line).append("\n"); + } + hereFile.append(delimeter).append("\n"); + return hereFile.toString(); + } + + public StringBuilder startHereFile() { + StringBuilder hereFile = new StringBuilder().append("cat >> ").append(path); + if (expandVariables) + return hereFile.append(" <<-").append(delimeter).append("\n"); + return hereFile.append(" <<-'").append(delimeter).append("'\n"); + } + + protected String appendLineToWindowsFile(String line, String path) { String quote = ""; - if (!ShellToken.VQ.to(family).equals("")) { + if (!ShellToken.VQ.to(OsFamily.WINDOWS).equals("")) { quote = "'"; } else { - line = escapeVarTokens(line, family); + line = escapeVarTokens(line, OsFamily.WINDOWS); } - return interpret(String.format("echo %s%s%s >>%s{lf}", quote, line, quote, path)); + return String.format("echo %s%s%s >>%s{lf}", quote, line, quote, path); } } \ No newline at end of file diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/CreateFile.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/CreateFile.java deleted file mode 100644 index 8636fafa56..0000000000 --- a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/CreateFile.java +++ /dev/null @@ -1,118 +0,0 @@ -/** - * Licensed to jclouds, Inc. (jclouds) under one or more - * contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. jclouds 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.jclouds.scriptbuilder.domain; - -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Preconditions.checkState; -import static org.jclouds.scriptbuilder.domain.Statements.interpret; - -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.regex.Pattern; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Iterables; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; - -/** - * Creates a run script - * - * @author Adrian Cole - */ -public class CreateFile implements Statement { - public static final String MARKER = "END_OF_FILE"; - - final String path; - final Iterable lines; - final String marker; - - public CreateFile(String path, Iterable lines) { - this(path, lines, MARKER); - } - - public CreateFile(String path, Iterable lines, String marker) { - this.path = checkNotNull(path, "path"); - this.lines = checkNotNull(lines, "lines"); - this.marker = checkNotNull(marker, "marker"); - checkState(Iterables.size(lines) > 0, "you must pass something to execute"); - } - - public static String escapeVarTokens(String toEscape, OsFamily family) { - Map inputToEscape = Maps.newHashMap(); - for (ShellToken token : ImmutableList.of(ShellToken.VARL, ShellToken.VARR)) { - if (!token.to(family).equals("")) { - String tokenS = "{" + token.toString().toLowerCase() + "}"; - inputToEscape.put(tokenS, "{escvar}" + tokenS); - } - } - for (Entry entry : inputToEscape.entrySet()) { - toEscape = toEscape.replace(entry.getKey(), entry.getValue()); - } - return toEscape; - } - - @Override - public Iterable functionDependencies(OsFamily family) { - return Collections.emptyList(); - } - - @Override - public String render(OsFamily family) { - List statements = Lists.newArrayList(); - if (family == OsFamily.UNIX) { - StringBuilder builder = new StringBuilder(); - hereFile(path, builder); - statements.add(interpret(builder.toString())); - } else { - for (String line : lines) { - statements.add(appendToFile(line, path, family)); - } - } - return new StatementList(statements).render(family); - } - - private void hereFile(String path, StringBuilder builder) { - builder.append("cat > ").append(path).append(" <<'").append(marker).append("'\n"); - for (String line : lines) { - builder.append(line).append("\n"); - } - builder.append(marker).append("\n"); - } - - private Statement appendToFile(String line, String path, OsFamily family) { - String quote = ""; - if (!ShellToken.VQ.to(family).equals("")) { - quote = "'"; - } else { - line = escapeVarTokens(line, family); - } - return interpret(addSpaceToEnsureWeDontAccidentallyRedirectFd(String.format("echo %s%s%s>>%s{lf}", quote, line, - quote, path))); - } - - public static final Pattern REDIRECT_FD_PATTERN = Pattern.compile(".*[0-2]>>.*"); - - static String addSpaceToEnsureWeDontAccidentallyRedirectFd(String line) { - return REDIRECT_FD_PATTERN.matcher(line).matches() ? line.replace(">>", " >>") : line; - } - -} \ No newline at end of file diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/CreateOrOverwriteFile.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/CreateOrOverwriteFile.java index 701c2977a6..04bb3ab03f 100644 --- a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/CreateOrOverwriteFile.java +++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/CreateOrOverwriteFile.java @@ -18,11 +18,6 @@ */ package org.jclouds.scriptbuilder.domain; -import static org.jclouds.scriptbuilder.domain.Statements.interpret; - -import java.util.List; - -import com.google.common.collect.Lists; /** * Creates a run script @@ -30,38 +25,54 @@ import com.google.common.collect.Lists; * @author Adrian Cole */ public class CreateOrOverwriteFile extends AppendFile { - - public CreateOrOverwriteFile(String path, Iterable lines) { - super(path, lines); + public static Builder builder() { + return new Builder(); } - public CreateOrOverwriteFile(String path, Iterable lines, String marker) { - super(path, lines, marker); + public static class Builder extends AppendFile.Builder { + + @Override + public Builder path(String path) { + return Builder.class.cast(super.path(path)); + } + + @Override + public Builder lines(Iterable lines) { + return Builder.class.cast(super.lines(lines)); + } + + @Override + public Builder delimeter(String delimeter) { + return Builder.class.cast(super.delimeter(delimeter)); + } + + @Override + public Builder expandVariables(boolean expandVariables) { + return Builder.class.cast(super.expandVariables(expandVariables)); + } + + @Override + public CreateOrOverwriteFile build() { + return new CreateOrOverwriteFile(path, lines, delimeter, expandVariables); + } + + } + + protected CreateOrOverwriteFile(String path, Iterable lines, String delimeter, boolean expandVariables) { + super(path, lines, delimeter, expandVariables); } @Override - public String render(OsFamily family) { - List statements = Lists.newArrayList(); - if (family == OsFamily.UNIX) { - StringBuilder builder = new StringBuilder(); - hereFile(path, builder); - statements.add(interpret(builder.toString())); - } else { - // Windows: - statements.add(interpret(String.format("copy /y CON %s{lf}", path))); // This clears the file - for (String line : lines) { - statements.add(appendToFile(line, path, family)); - } - } - return new StatementList(statements).render(family); + protected String appendToWindowsFile() { + return String.format("copy /y CON %s{lf}", path) + super.appendToWindowsFile(); } - protected void hereFile(String path, StringBuilder builder) { - builder.append("cat > ").append(path).append(" <<'").append(marker).append("'\n"); - for (String line : lines) { - builder.append(line).append("\n"); - } - builder.append(marker).append("\n"); + @Override + public StringBuilder startHereFile() { + StringBuilder hereFile = new StringBuilder().append("cat > ").append(path); + if (expandVariables) + return hereFile.append(" <<-").append(delimeter).append("\n"); + return hereFile.append(" <<-'").append(delimeter).append("'\n"); } } \ No newline at end of file diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/CreateRunScript.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/CreateRunScript.java index 0a3674a118..ad680797e4 100644 --- a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/CreateRunScript.java +++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/CreateRunScript.java @@ -22,23 +22,27 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Predicates.instanceOf; import static com.google.common.collect.Iterables.any; import static com.google.common.collect.Lists.newArrayList; -import static com.google.common.collect.Maps.newHashMap; +import static java.lang.String.format; +import static org.jclouds.scriptbuilder.domain.Statements.appendFile; +import static org.jclouds.scriptbuilder.domain.Statements.createOrOverwriteFile; +import static org.jclouds.scriptbuilder.domain.Statements.exec; import static org.jclouds.scriptbuilder.domain.Statements.interpret; import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.Map.Entry; -import java.util.regex.Pattern; import org.jclouds.scriptbuilder.ExitInsteadOfReturn; import org.jclouds.scriptbuilder.ScriptBuilder; import org.jclouds.scriptbuilder.util.Utils; import com.google.common.base.CaseFormat; +import com.google.common.base.Function; import com.google.common.base.Splitter; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableList.Builder; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Iterables; /** * Creates a run script @@ -46,7 +50,7 @@ import com.google.common.collect.ImmutableMap; * @author Adrian Cole */ public class CreateRunScript extends StatementList { - public final static String MARKER = "END_OF_SCRIPT"; + public final static String DELIMETER = "END_OF_JCLOUDS_SCRIPT"; final String instanceName; final Iterable exports; final String pwd; @@ -58,44 +62,17 @@ public class CreateRunScript extends StatementList { this.pwd = checkNotNull(pwd, "pwd").replaceAll("[/\\\\]", "{fs}"); } - public static class AddTitleToFile implements Statement { - final String title; - final String file; - - public AddTitleToFile(String title, String file) { - this.title = checkNotNull(title, "title"); - this.file = checkNotNull(file, "file"); - } - - public static final Map OS_TO_TITLE_PATTERN = ImmutableMap.of(OsFamily.UNIX, - "echo \"PROMPT_COMMAND='echo -ne \\\"\\033]0;{title}\\007\\\"'\">>{file}\n", OsFamily.WINDOWS, - "echo title {title}>>{file}\r\n"); - - @Override - public Iterable functionDependencies(OsFamily family) { - return Collections.emptyList(); - } - - @Override - public String render(OsFamily family) { - return addSpaceToEnsureWeDontAccidentallyRedirectFd(Utils.replaceTokens(OS_TO_TITLE_PATTERN.get(family), - ImmutableMap.of("title", title, "file", file))); - } - } - - public static class AddExportToFile implements Statement { + public static class AddExport implements Statement { final String export; final String value; - final String file; - public AddExportToFile(String export, String value, String file) { + public AddExport(String export, String value) { this.export = checkNotNull(CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, export), "export"); this.value = checkNotNull(value, "value"); - this.file = checkNotNull(file, "file"); } public static final Map OS_TO_EXPORT_PATTERN = ImmutableMap.of(OsFamily.UNIX, - "echo \"export {export}='{value}'\">>{file}\n", OsFamily.WINDOWS, "echo set {export}={value}>>{file}\r\n"); + "export {export}='{value}'\n", OsFamily.WINDOWS, "set {export}={value}\r\n"); @Override public Iterable functionDependencies(OsFamily family) { @@ -104,137 +81,91 @@ public class CreateRunScript extends StatementList { @Override public String render(OsFamily family) { - return addSpaceToEnsureWeDontAccidentallyRedirectFd(Utils.replaceTokens(OS_TO_EXPORT_PATTERN.get(family), - ImmutableMap.of("export", export, "value", value, "file", file))); + return Utils + .replaceTokens(OS_TO_EXPORT_PATTERN.get(family), ImmutableMap.of("export", export, "value", value)); } } - public static String escapeVarTokens(String toEscape, OsFamily family) { - Map inputToEscape = newHashMap(); - for (ShellToken token : ImmutableList.of(ShellToken.VARL, ShellToken.VARR)) { - if (!token.to(family).equals("")) { - String tokenS = "{" + token.toString().toLowerCase() + "}"; - inputToEscape.put(tokenS, "{escvar}" + tokenS); - } - } - for (Entry entry : inputToEscape.entrySet()) { - toEscape = toEscape.replace(entry.getKey(), entry.getValue()); - } - return toEscape; - } - @Override public Iterable functionDependencies(OsFamily family) { return Collections.emptyList(); } - public static final Map OS_TO_CHMOD_PATTERN = ImmutableMap.of(OsFamily.UNIX, "chmod u+x {file}\n", - OsFamily.WINDOWS, ""); - @Override public String render(OsFamily family) { + if (checkNotNull(family, "family") == OsFamily.WINDOWS) + throw new UnsupportedOperationException("windows not yet implemented"); List statements = newArrayList(); - Map tokenMap = ShellToken.tokenValueMap(family); + final Map tokenMap = ShellToken.tokenValueMap(family); String runScript = Utils.replaceTokens(pwd + "{fs}" + instanceName + ".{sh}", tokenMap); statements.add(interpret(String.format("{md} %s{lf}", pwd))); - if (family == OsFamily.UNIX) { - StringBuilder builder = new StringBuilder(); - builder.append("\n"); - addUnixRunScriptHeader(family, runScript, builder); - builder.append("\n"); - addUnixRunScript(runScript, builder); - builder.append("\n"); - addUnixRunScriptFooter(family, runScript, builder); - builder.append("\n"); - statements.add(interpret(builder.toString())); - } else { - statements.add(interpret(String.format("{rm} %s 2{closeFd}{lf}", runScript))); - for (String line : Splitter.on(ShellToken.LF.to(family)).split(ShellToken.BEGIN_SCRIPT.to(family))) { - if (!line.equals("")) - statements.add(appendToFile(line, runScript, family)); - } - statements.add(new AddTitleToFile(instanceName, runScript)); - statements.add(appendToFile(Utils.writeZeroPath(family).replace(ShellToken.LF.to(family), ""), runScript, - family)); - statements.add(new AddExportToFile("instanceName", instanceName, runScript)); - for (String export : exports) { - statements.add(new AddExportToFile(export, Utils.replaceTokens( - "{varl}" + CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, export) + "{varr}", tokenMap), - runScript)); - } - statements.add(appendToFile("{cd} " + pwd, runScript, family)); - statements.addAll(statements); - for (String line : Splitter.on(ShellToken.LF.to(family)).split(ShellToken.END_SCRIPT.to(family))) { - if (!line.equals("")) - statements.add(appendToFile(line, runScript, family)); - } - } - statements - .add(interpret(Utils.replaceTokens(OS_TO_CHMOD_PATTERN.get(family), ImmutableMap.of("file", runScript)))); + StringBuilder builder = new StringBuilder(); + builder.append("\n"); + addUnixRunScriptHeader(runScript, builder); + builder.append("\n"); + addUnixRunScript(runScript, builder); + builder.append("\n"); + addUnixRunScriptFooter(runScript, builder); + builder.append("\n"); + statements.add(interpret(builder.toString())); + statements.add(exec("chmod u+x " + runScript)); return new StatementList(statements).render(family); } - private void addUnixRunScriptFooter(OsFamily family, String runScript, StringBuilder builder) { + private void addUnixRunScriptFooter(String runScript, StringBuilder builder) { builder.append("# add runscript footer\n"); - builder.append("cat >> ").append(runScript).append(" <<'").append(MARKER).append("'\n"); - builder.append(ShellToken.END_SCRIPT.to(family)); - builder.append(MARKER).append("\n"); + Iterable endScript = Splitter.on(ShellToken.LF.to(OsFamily.UNIX)).split( + ShellToken.END_SCRIPT.to(OsFamily.UNIX)); + builder.append(appendFile(runScript, endScript, DELIMETER).render(OsFamily.UNIX)); } private void addUnixRunScript(String runScript, StringBuilder builder) { builder.append("# add desired commands from the user\n"); - builder.append("cat >> ").append(runScript).append(" <<'").append(MARKER).append("'\n"); - builder.append("cd ").append(pwd).append("\n"); + Builder userCommands = ImmutableList.builder(); + userCommands.add("cd " + pwd); for (Statement statement : statements) { if (statement instanceof Call || (statement instanceof StatementList && any(StatementList.class.cast(statement).delegate(), instanceOf(Call.class)))) { statement = new ExitInsteadOfReturn(statement); } - builder.append(statement.render(OsFamily.UNIX)).append("\n"); + userCommands.addAll(Splitter.on('\n').split(statement.render(OsFamily.UNIX))); } - builder.append(MARKER).append("\n"); + builder.append(appendFile(runScript, userCommands.build(), DELIMETER).render(OsFamily.UNIX)); } - private void addUnixRunScriptHeader(OsFamily family, String runScript, StringBuilder builder) { + private void addUnixRunScriptHeader(String runScript, StringBuilder builder) { builder.append("# create runscript header\n"); - builder.append("cat > ").append(runScript).append(" <<").append(MARKER).append("\n"); - builder.append(ShellToken.BEGIN_SCRIPT.to(family)); - builder.append("PROMPT_COMMAND='echo -ne \"\\033]0;").append(instanceName).append("\\007\"'\n"); - builder.append(Utils.writeZeroPath(family)); - builder.append("export INSTANCE_NAME='").append(instanceName).append("'\n"); - for (String export : exports) { - String variableNameInUpper = CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, export); - builder.append("export ").append(variableNameInUpper).append("='$").append(variableNameInUpper).append("'\n"); - } + + Builder beginningOfFile = ImmutableList. builder(); + beginningOfFile.addAll(Splitter.on(ShellToken.LF.to(OsFamily.UNIX)).split( + ShellToken.BEGIN_SCRIPT.to(OsFamily.UNIX))); + beginningOfFile.add(format("PROMPT_COMMAND='echo -ne \\\"\\033]0;%s\\007\\\"'", instanceName)); + beginningOfFile.add(Utils.writeZeroPath(OsFamily.UNIX)); + beginningOfFile.add(format("export INSTANCE_NAME='%s'", instanceName)); + builder.append(createOrOverwriteFile(runScript, beginningOfFile.build(), DELIMETER).render(OsFamily.UNIX)); + + // expanding variables here. + builder.append(AppendFile.builder().path(runScript).delimeter(DELIMETER).expandVariables(true) + .lines(Iterables.transform(exports, new Function() { + + @Override + public String apply(String export) { + String variableNameInUpper = CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, export); + return new StringBuilder().append("export ").append(variableNameInUpper).append("='$") + .append(variableNameInUpper).append("'").toString(); + } + })).build().render(OsFamily.UNIX)); Map functionsToWrite = ScriptBuilder.resolveFunctionDependenciesForStatements( - ImmutableMap. of("abort", Utils.writeFunctionFromResource("abort", family)), statements, - family); + ImmutableMap. of("abort", Utils.writeFunctionFromResource("abort", OsFamily.UNIX)), + statements, OsFamily.UNIX); + // if there are more functions than simply abort if (functionsToWrite.size() > 1) { - StringBuilder inNeedOfEscaping = new StringBuilder(); - ScriptBuilder.writeFunctions(functionsToWrite, family, inNeedOfEscaping); - builder.append(inNeedOfEscaping.toString().replace("$", "\\$")); + StringBuilder functions = new StringBuilder(); + ScriptBuilder.writeFunctions(functionsToWrite, OsFamily.UNIX, functions); + builder.append(appendFile(runScript, functions.toString(), DELIMETER).render(OsFamily.UNIX)); } - builder.append(MARKER).append("\n"); } - - private Statement appendToFile(String line, String runScript, OsFamily family) { - String quote = ""; - if (!ShellToken.VQ.to(family).equals("")) { - quote = "'"; - } else { - line = escapeVarTokens(line, family); - } - return interpret(addSpaceToEnsureWeDontAccidentallyRedirectFd(String.format("echo %s%s%s>>%s{lf}", quote, line, - quote, runScript))); - } - - public static final Pattern REDIRECT_FD_PATTERN = Pattern.compile(".*[0-2]>>.*"); - - static String addSpaceToEnsureWeDontAccidentallyRedirectFd(String line) { - return REDIRECT_FD_PATTERN.matcher(line).matches() ? line.replace(">>", " >>") : line; - } - } \ No newline at end of file diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/ShellToken.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/ShellToken.java index 7176981696..bf0d4d91e8 100644 --- a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/ShellToken.java +++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/ShellToken.java @@ -103,7 +103,7 @@ public enum ShellToken { case WINDOWS: return " exit /b 0\r\n"; case UNIX: - return " return 0\n}\n"; + return " return $?\n}\n"; } case ESCVAR: switch (family) { @@ -173,7 +173,7 @@ public enum ShellToken { case WINDOWS: return "exit /b 0\r\n"; case UNIX: - return "exit 0\n"; + return "exit $?\n"; } case EXPORT: switch (family) { diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/Statements.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/Statements.java index a226e63491..76c6bf6ed1 100644 --- a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/Statements.java +++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/Statements.java @@ -23,6 +23,7 @@ import java.util.Map; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMultimap; +import com.google.common.collect.ImmutableSet; import com.google.common.collect.Multimap; /** @@ -64,24 +65,28 @@ public class Statements { return new Call(function, args); } + public static Statement appendFile(String path, String line, String delimeter) { + return AppendFile.builder().path(path).lines(ImmutableSet.of(line)).delimeter(delimeter).build(); + } + public static Statement appendFile(String path, Iterable lines) { - return new AppendFile(path, lines); - } + return AppendFile.builder().path(path).lines(lines).build(); + } - public static Statement appendFile(String path, Iterable lines, String marker) { - return new AppendFile(path, lines, marker); - } + public static Statement appendFile(String path, Iterable lines, String delimeter) { + return AppendFile.builder().path(path).lines(lines).delimeter(delimeter).build(); + } - public static Statement createOrOverwriteFile(String path, Iterable lines) { - return new CreateOrOverwriteFile(path, lines); - } + public static Statement createOrOverwriteFile(String path, Iterable lines) { + return CreateOrOverwriteFile.builder().path(path).lines(lines).build(); + } - public static Statement createOrOverwriteFile(String path, Iterable lines, String marker) { - return new CreateOrOverwriteFile(path, lines, marker); - } + public static Statement createOrOverwriteFile(String path, Iterable lines, String delimeter) { + return CreateOrOverwriteFile.builder().path(path).lines(lines).delimeter(delimeter).build(); + } public static CreateRunScript createRunScript(String instanceName, Iterable exports, String pwd, - Iterable statements) {// TODO: convert so + Iterable statements) {// TODO: convert so // that // createRunScript // can take from a @@ -101,7 +106,8 @@ public class Statements { /** * - * Runs the script in a way that it can be matched later with {@link #findPid} + * Runs the script in a way that it can be matched later with + * {@link #findPid} * * @param instanceName * - what to match the process on @@ -119,7 +125,8 @@ public class Statements { } /** - * Kills the pid and subprocesses related to the variable {@code FOUND_PID} if set. + * Kills the pid and subprocesses related to the variable {@code FOUND_PID} + * if set. * * @see #findPid */ @@ -128,7 +135,8 @@ public class Statements { } /** - * statement can have multiple newlines, note you should use {@code lf} to be portable + * statement can have multiple newlines, note you should use {@code lf} to be + * portable * * @see ShellToken */ @@ -155,14 +163,14 @@ public class Statements { * @param directory */ public static Statement extractTargzIntoDirectory(String method, URI endpoint, Multimap headers, - String directory) { + String directory) { return new PipeHttpResponseToTarxpzfIntoDirectory(method, endpoint, headers, directory); } - + public static Statement extractTargzIntoDirectory(URI targz, String directory) { - return extractTargzIntoDirectory("GET", targz, ImmutableMultimap.of(), directory); + return extractTargzIntoDirectory("GET", targz, ImmutableMultimap. of(), directory); } - + /** * unzip the data received from the request parameters. * @@ -175,7 +183,7 @@ public class Statements { * @param directory */ public static Statement extractZipIntoDirectory(String method, URI endpoint, Multimap headers, - String directory) { + String directory) { return new UnzipHttpResponseIntoDirectory(method, endpoint, headers, directory); } diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/SwitchArg.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/SwitchArg.java index 0b3154d38f..289ecc7243 100644 --- a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/SwitchArg.java +++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/SwitchArg.java @@ -94,11 +94,11 @@ public class SwitchArg implements Statement, AcceptsStatementVisitor { if (shouldIndent) actionBuilder.append(INDENT); actionBuilder.append(line).append(ShellToken.LF.to(family)); - if (line.indexOf(CreateRunScript.MARKER) != -1) { + if (line.indexOf(CreateRunScript.DELIMETER) != -1) { inRunScript = inRunScript ? false : true; } - if (line.indexOf(AppendFile.MARKER) != -1) { + if (line.indexOf(AppendFile.DELIMETER) != -1) { inCreateFile = inCreateFile ? false : true; } shouldIndent = !inCreateFile && !inRunScript; diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/login/AdminAccess.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/login/AdminAccess.java index c7c0bd4233..f222bc8b0b 100644 --- a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/login/AdminAccess.java +++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/login/AdminAccess.java @@ -19,27 +19,30 @@ package org.jclouds.scriptbuilder.statements.login; import static com.google.common.base.Charsets.UTF_8; +import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import java.io.File; import java.io.IOException; import java.util.Map; -import org.jclouds.javax.annotation.Nullable; - import org.jclouds.crypto.Sha512Crypt; import org.jclouds.domain.Credentials; +import org.jclouds.javax.annotation.Nullable; import org.jclouds.scriptbuilder.domain.OsFamily; import org.jclouds.scriptbuilder.domain.Statement; import org.jclouds.scriptbuilder.domain.StatementList; import org.jclouds.scriptbuilder.statements.ssh.SshStatements; +import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Function; -import com.google.common.base.Preconditions; +import com.google.common.base.Predicates; import com.google.common.base.Supplier; import com.google.common.base.Throwables; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; import com.google.common.io.Files; import com.google.inject.ImplementedBy; @@ -288,7 +291,8 @@ public class AdminAccess implements Statement { } } - private Config config; + @VisibleForTesting + Config config; protected AdminAccess(Config in) { this.config = checkNotNull(in, "in"); @@ -344,14 +348,19 @@ public class AdminAccess implements Statement { checkNotNull(family, "family"); if (family == OsFamily.WINDOWS) throw new UnsupportedOperationException("windows not yet implemented"); + checkArgument(!"root".equals(config.getAdminUsername()), "cannot create admin user 'root'; " + + "ensure jclouds is not running as root, or specify an explicit non-root username in AdminAccess"); + if (Iterables.any( + Lists.newArrayList(config.getAdminUsername(), config.getAdminPassword(), config.getAdminPublicKey(), + config.getAdminPrivateKey(), config.getLoginPassword()), Predicates.isNull())) + init(new DefaultConfiguration()); + checkNotNull(config.getAdminUsername(), "adminUsername"); - Preconditions.checkArgument(!"root".equals(config.getAdminUsername()), "cannot create admin user 'root'; " + - "ensure jclouds is not running as root, or specify an explicit non-root username in AdminAccess"); checkNotNull(config.getAdminPassword(), "adminPassword"); checkNotNull(config.getAdminPublicKey(), "adminPublicKey"); checkNotNull(config.getAdminPrivateKey(), "adminPrivateKey"); checkNotNull(config.getLoginPassword(), "loginPassword"); - + ImmutableList.Builder statements = ImmutableList. builder(); UserAdd.Builder userBuilder = UserAdd.builder(); userBuilder.login(config.getAdminUsername()); diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/login/Sudoers.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/login/Sudoers.java index fa61ba4763..6e2bab2ecf 100644 --- a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/login/Sudoers.java +++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/login/Sudoers.java @@ -19,7 +19,7 @@ package org.jclouds.scriptbuilder.statements.login; import static com.google.common.base.Preconditions.checkNotNull; -import static org.jclouds.scriptbuilder.domain.Statements.appendFile; +import static org.jclouds.scriptbuilder.domain.Statements.createOrOverwriteFile; import static org.jclouds.scriptbuilder.domain.Statements.exec; import org.jclouds.scriptbuilder.domain.OsFamily; @@ -45,8 +45,7 @@ public class Sudoers implements Statement { if (family == OsFamily.WINDOWS) throw new UnsupportedOperationException("windows not yet implemented"); Builder statements = ImmutableList. builder(); - statements.add(exec("{rm} " + sudoers)); - statements.add(appendFile(sudoers, ImmutableSet.of("root ALL = (ALL) ALL", "%wheel ALL = (ALL) NOPASSWD:ALL"))); + statements.add(createOrOverwriteFile(sudoers, ImmutableSet.of("root ALL = (ALL) ALL", "%wheel ALL = (ALL) NOPASSWD:ALL"))); statements.add(exec("chmod 0440 " + sudoers)); return new StatementList(statements.build()).render(family); } diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/ssh/SshdConfig.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/ssh/SshdConfig.java index f151e88b57..2264f89f91 100644 --- a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/ssh/SshdConfig.java +++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/ssh/SshdConfig.java @@ -49,7 +49,7 @@ public class SshdConfig implements Statement { Statement prependSshdConfig = exec(String.format( "exec 3<> %1$s && awk -v TEXT=\"%2$s\n\" 'BEGIN {print TEXT}{print}' %1$s >&3", sshdConfig, linesToPrepend)); - Statement reloadSshdConfig = exec("/etc/init.d/sshd reload||/etc/init.d/ssh reload"); + Statement reloadSshdConfig = exec("hash service 2>/dev/null && service ssh reload || /etc/init.d/ssh* reload"); return newStatementList(prependSshdConfig, reloadSshdConfig).render(family); } diff --git a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/InitBuilderTest.java b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/InitBuilderTest.java index 0c022c9a28..0981d6b72b 100644 --- a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/InitBuilderTest.java +++ b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/InitBuilderTest.java @@ -49,12 +49,9 @@ public class InitBuilderTest { appendFile("{tmp}{fs}{uid}{fs}scripttest{fs}temp.txt", ImmutableList. of("hello world")), exec("find /"))); - @Test + @Test(expectedExceptions = UnsupportedOperationException.class) public void testBuildSimpleWindows() throws MalformedURLException, IOException { - assertEquals( - testInitBuilder.render(OsFamily.WINDOWS), - CharStreams.toString(Resources.newReaderSupplier( - Resources.getResource("test_init." + ShellToken.SH.to(OsFamily.WINDOWS)), Charsets.UTF_8))); + testInitBuilder.render(OsFamily.WINDOWS); } @Test diff --git a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/InitScriptTest.java b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/InitScriptTest.java new file mode 100644 index 0000000000..51e407b5a2 --- /dev/null +++ b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/InitScriptTest.java @@ -0,0 +1,105 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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.jclouds.scriptbuilder; + +import static org.jclouds.scriptbuilder.domain.Statements.appendFile; +import static org.jclouds.scriptbuilder.domain.Statements.call; +import static org.jclouds.scriptbuilder.domain.Statements.exec; +import static org.jclouds.scriptbuilder.domain.Statements.interpret; +import static org.testng.Assert.assertEquals; + +import java.io.IOException; +import java.net.MalformedURLException; + +import org.jclouds.scriptbuilder.domain.OsFamily; +import org.jclouds.scriptbuilder.domain.ShellToken; +import org.testng.annotations.Test; + +import com.google.common.base.Charsets; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.io.CharStreams; +import com.google.common.io.Resources; + +/** + * Tests possible uses of InitScript + * + * @author Adrian Cole + */ +public class InitScriptTest { + InitScript testInitScript = InitScript + .builder() + .name("mkebsboot") + .home("/mnt/tmp") + .exportVariables(ImmutableMap.of("tmpDir", "/mnt/tmp")) + .run(appendFile("{tmp}{fs}{uid}{fs}scripttest{fs}temp.txt", ImmutableList. of("hello world")), + exec("find /")).build(); + + public void testBuildSimpleWindows() throws MalformedURLException, IOException { + assertEquals( + testInitScript.render(OsFamily.WINDOWS), + CharStreams.toString(Resources.newReaderSupplier( + Resources.getResource("test_init." + ShellToken.SH.to(OsFamily.WINDOWS)), Charsets.UTF_8))); + } + + public void testBuildSimpleUNIX() throws MalformedURLException, IOException { + assertEquals( + testInitScript.render(OsFamily.UNIX), + CharStreams.toString(Resources.newReaderSupplier( + Resources.getResource("test_init." + ShellToken.SH.to(OsFamily.UNIX)), Charsets.UTF_8))); + } + + public void testBuildEBS() throws MalformedURLException, IOException { + assertEquals(InitScript + .builder() + .name("mkebsboot") + .home("tmp") + .logDir("/tmp/logs") + .exportVariables(ImmutableMap.of("imageDir", "/mnt/tmp", "ebsDevice", "/dev/sdh", "ebsMountPoint", "/mnt/ebs")) + .run(interpret( + "echo creating a filesystem and mounting the ebs volume", + "{md} {varl}IMAGE_DIR{varr} {varl}EBS_MOUNT_POINT{varr}", + "rm -rf {varl}IMAGE_DIR{varr}/*", + "yes| mkfs -t ext3 {varl}EBS_DEVICE{varr} 2>&-", + "mount {varl}EBS_DEVICE{varr} {varl}EBS_MOUNT_POINT{varr}", + "echo making a local working copy of the boot disk", + "rsync -ax --exclude /ubuntu/.bash_history --exclude /home/*/.bash_history --exclude /etc/ssh/ssh_host_* --exclude /etc/ssh/moduli --exclude /etc/udev/rules.d/*persistent-net.rules --exclude /var/lib/ec2/* --exclude=/mnt/* --exclude=/proc/* --exclude=/tmp/* --exclude=/dev/log / {varl}IMAGE_DIR{varr}", + "echo preparing the local working copy", + "touch {varl}IMAGE_DIR{varr}/etc/init.d/ec2-init-user-data", + "echo copying the local working copy to the ebs mount", "{cd} {varl}IMAGE_DIR{varr}", + "tar -cSf - * | tar xf - -C {varl}EBS_MOUNT_POINT{varr}", "echo size of ebs", + "du -sk {varl}EBS_MOUNT_POINT{varr}", "echo size of source", + "du -sk {varl}IMAGE_DIR{varr}", "rm -rf {varl}IMAGE_DIR{varr}/*", + "umount {varl}EBS_MOUNT_POINT{varr}", "echo ----COMPLETE----")).build().render(OsFamily.UNIX), + CharStreams.toString(Resources.newReaderSupplier( + Resources.getResource("test_ebs." + ShellToken.SH.to(OsFamily.UNIX)), Charsets.UTF_8))); + } + + InitScript testCallInRun = InitScript.builder().name("testcall").init(exec("echo hello")) + .run(call("sourceEnvFile", "foo"), exec("find /")).build(); + + @Test + public void testCallInRunUNIX() throws MalformedURLException, IOException { + assertEquals( + testCallInRun.render(OsFamily.UNIX), + CharStreams.toString(Resources.newReaderSupplier( + Resources.getResource("test_init_script." + ShellToken.SH.to(OsFamily.UNIX)), Charsets.UTF_8))); + } + +} diff --git a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/domain/CreateRunScriptTest.java b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/domain/CreateRunScriptTest.java index 021ec55ed7..f984e974be 100644 --- a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/domain/CreateRunScriptTest.java +++ b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/domain/CreateRunScriptTest.java @@ -53,16 +53,9 @@ public class CreateRunScriptTest { .getResource("test_runrun." + ShellToken.SH.to(OsFamily.UNIX)), Charsets.UTF_8))); } - public void testWINDOWS() throws IOException { - assertEquals(statement.render(OsFamily.WINDOWS), CharStreams.toString(Resources.newReaderSupplier(Resources - .getResource("test_runrun." + ShellToken.SH.to(OsFamily.WINDOWS)), Charsets.UTF_8))); - } - - public void testRedirectGuard() { - assertEquals(CreateRunScript.addSpaceToEnsureWeDontAccidentallyRedirectFd("foo>>"), "foo>>"); - assertEquals(CreateRunScript.addSpaceToEnsureWeDontAccidentallyRedirectFd("foo0>>"), "foo0 >>"); - assertEquals(CreateRunScript.addSpaceToEnsureWeDontAccidentallyRedirectFd("foo1>>"), "foo1 >>"); - assertEquals(CreateRunScript.addSpaceToEnsureWeDontAccidentallyRedirectFd("foo2>>"), "foo2 >>"); + @Test(expectedExceptions = UnsupportedOperationException.class) + public void testWINDOWSUnimplemented() throws IOException { + statement.render(OsFamily.WINDOWS); } } diff --git a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/domain/ShellTokenTest.java b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/domain/ShellTokenTest.java index a5edfd2756..5c7c6cbf6a 100644 --- a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/domain/ShellTokenTest.java +++ b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/domain/ShellTokenTest.java @@ -40,9 +40,9 @@ public class ShellTokenTest { "args", "$@").put("varl", "$").put("return", "return").put("exit", "exit").put( "varr", "").put("libraryPathVariable", "LD_LIBRARY_PATH").put("beginScript", "#!/bin/bash\nset +u\nshopt -s xpg_echo\nshopt -s expand_aliases\n").put( - "endScript", "exit 0\n").put("vq", "\"").put("beginFunctions", "").put( + "endScript", "exit $?\n").put("vq", "\"").put("beginFunctions", "").put( "endFunctions", "").put("fncl", "function ").put("fncr", " {\n").put("fnce", - " return 0\n}\n").put("export", "export").put("rm", "rm").put("cd", "cd").put( + " return $?\n}\n").put("export", "export").put("rm", "rm").put("cd", "cd").put( "tmp", "/tmp").put("uid", "$USER").put("root", "/").put("closeFd", ">&-").put("md", "mkdir -p").put("escvar", "\\").build(); assertEquals(ShellToken.tokenValueMap(OsFamily.UNIX), expected); diff --git a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/domain/SwitchArgTest.java b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/domain/SwitchArgTest.java index e7407640dc..fdde97d12c 100644 --- a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/domain/SwitchArgTest.java +++ b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/domain/SwitchArgTest.java @@ -39,7 +39,17 @@ public class SwitchArgTest { assertEquals(new SwitchArg(1, ImmutableMap.of("0", newStatementList(appendFile( "{tmp}{fs}{uid}{fs}scripttest{fs}temp.txt", Collections.singleton("hello world")), interpret("echo hello zero{lf}")), "1", interpret("echo hello one{lf}"))).render(OsFamily.UNIX), - "case $1 in\n0)\n cat >> /tmp/$USER/scripttest/temp.txt <<'END_OF_FILE'\nhello world\nEND_OF_FILE\n echo hello zero\n ;;\n1)\n echo hello one\n ;;\nesac\n"); + "case $1 in\n"+ + "0)\n"+ + " cat >> /tmp/$USER/scripttest/temp.txt <<-'END_OF_JCLOUDS_FILE'\n"+ + "\thello world\n"+ + "END_OF_JCLOUDS_FILE\n"+ + " echo hello zero\n"+ + " ;;\n"+ + "1)\n"+ + " echo hello one\n"+ + " ;;\n"+ + "esac\n"); } public void testSwitchArgWindows() { diff --git a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/functions/CredentialsFromAdminAccessTest.java b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/functions/CredentialsFromAdminAccessTest.java index 3088d6622f..e2dc7f91a4 100644 --- a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/functions/CredentialsFromAdminAccessTest.java +++ b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/functions/CredentialsFromAdminAccessTest.java @@ -18,20 +18,19 @@ */ package org.jclouds.scriptbuilder.functions; +import static org.easymock.EasyMock.createMock; import static org.easymock.EasyMock.expect; -import static org.easymock.classextension.EasyMock.createMock; -import static org.easymock.classextension.EasyMock.replay; -import static org.easymock.classextension.EasyMock.verify; +import static org.easymock.EasyMock.replay; +import static org.easymock.EasyMock.verify; import static org.testng.Assert.assertEquals; import org.jclouds.domain.Credentials; -import org.jclouds.scriptbuilder.InitBuilder; +import org.jclouds.scriptbuilder.InitScript; import org.jclouds.scriptbuilder.domain.Statement; import org.jclouds.scriptbuilder.domain.Statements; import org.jclouds.scriptbuilder.statements.login.AdminAccess; import org.testng.annotations.Test; -import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; /** @@ -97,8 +96,8 @@ public class CredentialsFromAdminAccessTest { replay(statement); replay(creds); - InitBuilder testInitBuilder = new InitBuilder("mkebsboot", "/mnt/tmp", "/mnt/tmp", ImmutableMap.of("tmpDir", - "/mnt/tmp"), ImmutableList. of(statement)); + InitScript testInitBuilder = InitScript.builder().name("mkebsboot").home("/mnt/tmp") + .exportVariables(ImmutableMap.of("tmpDir", "/mnt/tmp")).run(statement).build(); assertEquals(CredentialsFromAdminAccess.INSTANCE.apply(testInitBuilder), creds); diff --git a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/functions/InitAdminAccessTest.java b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/functions/InitAdminAccessTest.java index 5a007d95a8..f57e1aea6a 100644 --- a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/functions/InitAdminAccessTest.java +++ b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/functions/InitAdminAccessTest.java @@ -18,18 +18,16 @@ */ package org.jclouds.scriptbuilder.functions; +import static org.easymock.EasyMock.createMock; import static org.easymock.EasyMock.expect; -import static org.easymock.classextension.EasyMock.createMock; -import static org.easymock.classextension.EasyMock.replay; -import static org.easymock.classextension.EasyMock.verify; +import static org.easymock.EasyMock.replay; +import static org.easymock.EasyMock.verify; -import org.jclouds.scriptbuilder.InitBuilder; -import org.jclouds.scriptbuilder.domain.Statement; +import org.jclouds.scriptbuilder.InitScript; import org.jclouds.scriptbuilder.domain.Statements; import org.jclouds.scriptbuilder.statements.login.AdminAccess; import org.testng.annotations.Test; -import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; /** @@ -100,9 +98,9 @@ public class InitAdminAccessTest { replay(configuration); replay(statement); replay(newStatement); - - InitBuilder testInitBuilder = new InitBuilder("mkebsboot", "/mnt/tmp", "/mnt/tmp", ImmutableMap.of("tmpDir", - "/mnt/tmp"), ImmutableList. of(statement)); + + InitScript testInitBuilder = InitScript.builder().name("mkebsboot").home("/mnt/tmp") + .exportVariables(ImmutableMap.of("tmpDir", "/mnt/tmp")).run(statement).build(); InitAdminAccess initAdminAccess = new InitAdminAccess(configuration); diff --git a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/java/InstallJDKTest.java b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/java/InstallJDKTest.java index 8351d74a72..4730ffbc6a 100644 --- a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/java/InstallJDKTest.java +++ b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/java/InstallJDKTest.java @@ -23,14 +23,13 @@ import static org.testng.Assert.assertEquals; import java.io.IOException; import java.net.URI; -import org.jclouds.scriptbuilder.InitBuilder; +import org.jclouds.scriptbuilder.InitScript; import org.jclouds.scriptbuilder.domain.OsFamily; import org.jclouds.scriptbuilder.domain.ShellToken; import org.jclouds.scriptbuilder.domain.Statement; import org.testng.annotations.Test; import com.google.common.base.Charsets; -import com.google.common.collect.ImmutableSet; import com.google.common.io.CharStreams; import com.google.common.io.Resources; @@ -48,11 +47,10 @@ public class InstallJDKTest { } public void testInstallJDKUNIXInScriptBuilderSourcesSetupPublicCurl() throws IOException { - assertEquals( - new InitBuilder("install_jdk", ImmutableSet. of(), ImmutableSet. of(InstallJDK - .fromURL())).render(OsFamily.UNIX), + assertEquals(InitScript.builder().name("install_jdk").run(InstallJDK.fromURL()).build().render(OsFamily.UNIX), CharStreams.toString(Resources.newReaderSupplier( - Resources.getResource("test_install_jdk_scriptbuilder." + ShellToken.SH.to(OsFamily.UNIX)), Charsets.UTF_8))); + Resources.getResource("test_install_jdk_scriptbuilder." + ShellToken.SH.to(OsFamily.UNIX)), + Charsets.UTF_8))); } public void testInstallJDKUNIXWithURL() throws IOException { diff --git a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/login/AdminAccessTest.java b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/login/AdminAccessTest.java index c3c599293b..0d389fec19 100644 --- a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/login/AdminAccessTest.java +++ b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/login/AdminAccessTest.java @@ -19,6 +19,7 @@ package org.jclouds.scriptbuilder.statements.login; import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; import java.io.IOException; @@ -76,9 +77,9 @@ public class AdminAccessTest { public void testCreateWheelWindowsNotSupported() { AdminAccess.standard().init(TestConfiguration.INSTANCE).render(OsFamily.WINDOWS); } - - @Test(expectedExceptions=IllegalArgumentException.class) - //for issue 682 + + @Test(expectedExceptions = IllegalArgumentException.class) + // for issue 682 public void testRootNotAllowed() throws IOException { TestConfiguration.INSTANCE.reset(); try { @@ -88,4 +89,34 @@ public class AdminAccessTest { } } + @Test(expectedExceptions = NullPointerException.class) + public void testFamilyRequiredAllowed() throws IOException { + AdminAccess.standard().render(null); + } + + public void testWhenUninitializedLazyInitWithDefaultConfiguration() throws IOException { + AdminAccess access = AdminAccess.standard(); + // before rendered, holder is empty + assertEquals(access.config.getAdminUsername(), null); + assertEquals(access.config.getAdminPassword(), null); + assertEquals(access.config.getAdminPublicKey(), null); + assertEquals(access.config.getAdminPrivateKey(), null); + assertEquals(access.config.getLoginPassword(), null); + access.render(OsFamily.UNIX); + // DefaultConfiguration + try { + assertEquals(access.config.getAdminUsername(), System.getProperty("user.name")); + assertNotNull(access.config.getAdminPassword()); + assertNotNull(access.config.getAdminPublicKey()); + assertNotNull(access.config.getAdminPrivateKey()); + assertNotNull(access.config.getLoginPassword()); + } catch (AssertionError e) { + throw e; + } catch (Throwable e) { + // we are catching throwables here, in case the test runner doesn't + // have ssh keys setup + } + + } + } diff --git a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/login/SudoStatementsTest.java b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/login/SudoStatementsTest.java index 3364297b18..afbf0d4a91 100644 --- a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/login/SudoStatementsTest.java +++ b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/login/SudoStatementsTest.java @@ -32,7 +32,11 @@ public class SudoStatementsTest { public void testCreateWheelUNIX() { assertEquals( SudoStatements.createWheel().render(OsFamily.UNIX), - "rm /etc/sudoers\ncat >> /etc/sudoers <<'END_OF_FILE'\nroot ALL = (ALL) ALL\n%wheel ALL = (ALL) NOPASSWD:ALL\nEND_OF_FILE\nchmod 0440 /etc/sudoers\n"); + "cat > /etc/sudoers <<-'END_OF_JCLOUDS_FILE'\n"+ + "\troot ALL = (ALL) ALL\n"+ + "\t%wheel ALL = (ALL) NOPASSWD:ALL\n"+ + "END_OF_JCLOUDS_FILE\n"+ + "chmod 0440 /etc/sudoers\n"); } @Test(expectedExceptions = UnsupportedOperationException.class) diff --git a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/login/UserAddTest.java b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/login/UserAddTest.java index ac570a1eb9..e9d6bf3ecc 100644 --- a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/login/UserAddTest.java +++ b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/login/UserAddTest.java @@ -61,13 +61,13 @@ public class UserAddTest { public void testWithSshAuthorizedKeyUNIX() { assertEquals( UserAdd.builder().login("me").authorizeRSAPublicKey("rsapublickey").build().render(OsFamily.UNIX), - "mkdir -p /home/users\nuseradd -s /bin/bash -m -d /home/users/me me\nmkdir -p /home/users/me/.ssh\ncat >> /home/users/me/.ssh/authorized_keys <<'END_OF_FILE'\nrsapublickey\nEND_OF_FILE\nchmod 600 /home/users/me/.ssh/authorized_keys\nchown -R me /home/users/me\n"); + "mkdir -p /home/users\nuseradd -s /bin/bash -m -d /home/users/me me\nmkdir -p /home/users/me/.ssh\ncat >> /home/users/me/.ssh/authorized_keys <<-'END_OF_JCLOUDS_FILE'\n\trsapublickey\nEND_OF_JCLOUDS_FILE\nchmod 600 /home/users/me/.ssh/authorized_keys\nchown -R me /home/users/me\n"); } public void testWithSshInstalledKeyUNIX() { assertEquals( UserAdd.builder().login("me").installRSAPrivateKey("rsaprivate").build().render(OsFamily.UNIX), - "mkdir -p /home/users\nuseradd -s /bin/bash -m -d /home/users/me me\nmkdir -p /home/users/me/.ssh\nrm /home/users/me/.ssh/id_rsa\ncat >> /home/users/me/.ssh/id_rsa <<'END_OF_FILE'\nrsaprivate\nEND_OF_FILE\nchmod 600 /home/users/me/.ssh/id_rsa\nchown -R me /home/users/me\n"); + "mkdir -p /home/users\nuseradd -s /bin/bash -m -d /home/users/me me\nmkdir -p /home/users/me/.ssh\nrm /home/users/me/.ssh/id_rsa\ncat >> /home/users/me/.ssh/id_rsa <<-'END_OF_JCLOUDS_FILE'\n\trsaprivate\nEND_OF_JCLOUDS_FILE\nchmod 600 /home/users/me/.ssh/id_rsa\nchown -R me /home/users/me\n"); } @Test(expectedExceptions = UnsupportedOperationException.class) diff --git a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/ssh/AuthorizeRSAPublicKeyTest.java b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/ssh/AuthorizeRSAPublicKeyTest.java index 4c17a66dd1..4a772301db 100644 --- a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/ssh/AuthorizeRSAPublicKeyTest.java +++ b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/ssh/AuthorizeRSAPublicKeyTest.java @@ -34,26 +34,46 @@ public class AuthorizeRSAPublicKeyTest { public void testAuthorizeRSAPublicKeyUNIXCurrentUser() { assertEquals( new AuthorizeRSAPublicKeys(ImmutableSet.of("ssh-dss AAAAB")).render(OsFamily.UNIX), - "mkdir -p ~/.ssh\ncat >> ~/.ssh/authorized_keys <<'END_OF_FILE'\nssh-dss AAAAB\nEND_OF_FILE\nchmod 600 ~/.ssh/authorized_keys\n"); + "mkdir -p ~/.ssh\n"+ + "cat >> ~/.ssh/authorized_keys <<-'END_OF_JCLOUDS_FILE'\n"+ + "\tssh-dss AAAAB\n"+ + "END_OF_JCLOUDS_FILE\n"+ + "chmod 600 ~/.ssh/authorized_keys\n"); } public void testAuthorizeRSAPublicKeyUNIXCurrentUserWith2Keys() { assertEquals( new AuthorizeRSAPublicKeys(ImmutableSet.of("ssh-dss AAAAB", "ssh-dss CCCCD")).render(OsFamily.UNIX), - "mkdir -p ~/.ssh\ncat >> ~/.ssh/authorized_keys <<'END_OF_FILE'\nssh-dss AAAAB\n\nssh-dss CCCCD\nEND_OF_FILE\nchmod 600 ~/.ssh/authorized_keys\n"); + "mkdir -p ~/.ssh\n"+ + "cat >> ~/.ssh/authorized_keys <<-'END_OF_JCLOUDS_FILE'\n"+ + "\tssh-dss AAAAB\n"+ + "\t\n"+ + "\tssh-dss CCCCD\n"+ + "END_OF_JCLOUDS_FILE\n"+ + "chmod 600 ~/.ssh/authorized_keys\n"); } public void testAuthorizeRSAPublicKeyUNIXSpecifiedDir() { assertEquals( new AuthorizeRSAPublicKeys("/home/me/.ssh", ImmutableSet.of("ssh-dss AAAAB")).render(OsFamily.UNIX), - "mkdir -p /home/me/.ssh\ncat >> /home/me/.ssh/authorized_keys <<'END_OF_FILE'\nssh-dss AAAAB\nEND_OF_FILE\nchmod 600 /home/me/.ssh/authorized_keys\n"); + "mkdir -p /home/me/.ssh\n"+ + "cat >> /home/me/.ssh/authorized_keys <<-'END_OF_JCLOUDS_FILE'\n"+ + "\tssh-dss AAAAB\n"+ + "END_OF_JCLOUDS_FILE\n"+ + "chmod 600 /home/me/.ssh/authorized_keys\n"); } public void testAuthorizeRSAPublicKeyUNIXSpecifiedDirWith2Keys() { assertEquals( new AuthorizeRSAPublicKeys("/home/me/.ssh", ImmutableSet.of("ssh-dss AAAAB", "ssh-dss CCCCD")) .render(OsFamily.UNIX), - "mkdir -p /home/me/.ssh\ncat >> /home/me/.ssh/authorized_keys <<'END_OF_FILE'\nssh-dss AAAAB\n\nssh-dss CCCCD\nEND_OF_FILE\nchmod 600 /home/me/.ssh/authorized_keys\n"); + "mkdir -p /home/me/.ssh\n"+ + "cat >> /home/me/.ssh/authorized_keys <<-'END_OF_JCLOUDS_FILE'\n"+ + "\tssh-dss AAAAB\n"+ + "\t\n"+ + "\tssh-dss CCCCD\n"+ + "END_OF_JCLOUDS_FILE\n"+ + "chmod 600 /home/me/.ssh/authorized_keys\n"); } @Test(expectedExceptions = UnsupportedOperationException.class) diff --git a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/ssh/InstallRSAPrivateKeyTest.java b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/ssh/InstallRSAPrivateKeyTest.java index 2e9f3b2990..3fd7809097 100644 --- a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/ssh/InstallRSAPrivateKeyTest.java +++ b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/ssh/InstallRSAPrivateKeyTest.java @@ -33,15 +33,28 @@ public class InstallRSAPrivateKeyTest { assertEquals( new InstallRSAPrivateKey("-----BEGIN RSA PRIVATE KEY-----\n-----END RSA PRIVATE KEY-----\n") .render(OsFamily.UNIX), - "mkdir -p ~/.ssh\nrm ~/.ssh/id_rsa\ncat >> ~/.ssh/id_rsa <<'END_OF_FILE'\n-----BEGIN RSA PRIVATE KEY-----\n-----END RSA PRIVATE KEY-----\n\nEND_OF_FILE\nchmod 600 ~/.ssh/id_rsa\n"); + "mkdir -p ~/.ssh\n"+ + "rm ~/.ssh/id_rsa\n"+ + "cat >> ~/.ssh/id_rsa <<-'END_OF_JCLOUDS_FILE'\n"+ + "\t-----BEGIN RSA PRIVATE KEY-----\n"+ + "\t-----END RSA PRIVATE KEY-----\n"+ + "\t\n"+ + "END_OF_JCLOUDS_FILE\n"+ + "chmod 600 ~/.ssh/id_rsa\n"); } public void testInstallRSAPrivateKeyUNIXSpecifiedHome() { assertEquals( new InstallRSAPrivateKey("/home/me/.ssh", "-----BEGIN RSA PRIVATE KEY-----\n-----END RSA PRIVATE KEY-----\n") .render(OsFamily.UNIX), - "mkdir -p /home/me/.ssh\nrm /home/me/.ssh/id_rsa\ncat >> /home/me/.ssh/id_rsa <<'END_OF_FILE'\n-----BEGIN RSA PRIVATE KEY-----\n-----END RSA PRIVATE KEY-----\n\nEND_OF_FILE\nchmod 600 /home/me/.ssh/id_rsa\n"); - } + "mkdir -p /home/me/.ssh\n"+ + "rm /home/me/.ssh/id_rsa\n"+ + "cat >> /home/me/.ssh/id_rsa <<-'END_OF_JCLOUDS_FILE'\n"+ + "\t-----BEGIN RSA PRIVATE KEY-----\n"+ + "\t-----END RSA PRIVATE KEY-----\n"+ + "\t\n"+ + "END_OF_JCLOUDS_FILE\n"+ + "chmod 600 /home/me/.ssh/id_rsa\n"); } @Test(expectedExceptions = UnsupportedOperationException.class) public void testInstallRSAPrivateKeyWINDOWS() { diff --git a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/ssh/SshStatementsTest.java b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/ssh/SshStatementsTest.java index 9459ba51bc..bf9a628518 100644 --- a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/ssh/SshStatementsTest.java +++ b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/ssh/SshStatementsTest.java @@ -38,7 +38,7 @@ public class SshStatementsTest { .append("PasswordAuthentication no").append("\n")// .append("PermitRootLogin no").append("\n")// .append("\" 'BEGIN {print TEXT}{print}' /etc/ssh/sshd_config >&3").append("\n")// - .append("/etc/init.d/sshd reload||/etc/init.d/ssh reload").append("\n").toString()); + .append("hash service 2>/dev/null && service ssh reload || /etc/init.d/ssh* reload").append("\n").toString()); } public void testSshdConfigUNIX() { @@ -46,7 +46,7 @@ public class SshStatementsTest { new StringBuilder().append("exec 3<> /etc/ssh/sshd_config && awk -v TEXT=\"")// .append("AddressFamily inet6").append("\n")// .append("\" 'BEGIN {print TEXT}{print}' /etc/ssh/sshd_config >&3").append("\n")// - .append("/etc/init.d/sshd reload||/etc/init.d/ssh reload").append("\n").toString()); + .append("hash service 2>/dev/null && service ssh reload || /etc/init.d/ssh* reload").append("\n").toString()); } } diff --git a/scriptbuilder/src/test/resources/client_rb_append.sh b/scriptbuilder/src/test/resources/client_rb_append.sh index f0522fbfa9..96711ddf63 100644 --- a/scriptbuilder/src/test/resources/client_rb_append.sh +++ b/scriptbuilder/src/test/resources/client_rb_append.sh @@ -1,5 +1,5 @@ -cat >> /etc/chef/client.rb <<'END_OF_FILE' -log_level :info -log_location STDOUT -chef_server_url "http://localhost:4000" -END_OF_FILE +cat >> /etc/chef/client.rb <<-'END_OF_JCLOUDS_FILE' + log_level :info + log_location STDOUT + chef_server_url "http://localhost:4000" +END_OF_JCLOUDS_FILE diff --git a/scriptbuilder/src/test/resources/client_rb_overwrite.sh b/scriptbuilder/src/test/resources/client_rb_overwrite.sh index cca52f5638..7f2b94956a 100644 --- a/scriptbuilder/src/test/resources/client_rb_overwrite.sh +++ b/scriptbuilder/src/test/resources/client_rb_overwrite.sh @@ -1,5 +1,5 @@ -cat > /etc/chef/client.rb <<'END_OF_FILE' -log_level :info -log_location STDOUT -chef_server_url "http://localhost:4000" -END_OF_FILE +cat > /etc/chef/client.rb <<-'END_OF_JCLOUDS_FILE' + log_level :info + log_location STDOUT + chef_server_url "http://localhost:4000" +END_OF_JCLOUDS_FILE diff --git a/scriptbuilder/src/test/resources/test_adminaccess_flipped.sh b/scriptbuilder/src/test/resources/test_adminaccess_flipped.sh index 0c10ee9582..d065eff6a2 100644 --- a/scriptbuilder/src/test/resources/test_adminaccess_flipped.sh +++ b/scriptbuilder/src/test/resources/test_adminaccess_flipped.sh @@ -16,6 +16,6 @@ chown -R defaultAdminUsername /home/users/defaultAdminUsername exec 3<> /etc/ssh/sshd_config && awk -v TEXT="PasswordAuthentication no PermitRootLogin no " 'BEGIN {print TEXT}{print}' /etc/ssh/sshd_config >&3 -/etc/init.d/sshd reload||/etc/init.d/ssh reload +hash service 2>/dev/null && service ssh reload || /etc/init.d/ssh* reload awk -v user=^${SUDO_USER:=${USER}}: -v password='crypt(1)' 'BEGIN { FS=OFS=":" } $0 ~ user { $2 = password } 1' /etc/shadow >/etc/shadow.${SUDO_USER:=${USER}} test -f /etc/shadow.${SUDO_USER:=${USER}} && mv /etc/shadow.${SUDO_USER:=${USER}} /etc/shadow diff --git a/scriptbuilder/src/test/resources/test_adminaccess_params.sh b/scriptbuilder/src/test/resources/test_adminaccess_params.sh index 3908f6299a..6f1e1d0589 100644 --- a/scriptbuilder/src/test/resources/test_adminaccess_params.sh +++ b/scriptbuilder/src/test/resources/test_adminaccess_params.sh @@ -1,21 +1,20 @@ -rm /etc/sudoers -cat >> /etc/sudoers <<'END_OF_FILE' -root ALL = (ALL) ALL -%wheel ALL = (ALL) NOPASSWD:ALL -END_OF_FILE +cat > /etc/sudoers <<-'END_OF_JCLOUDS_FILE' + root ALL = (ALL) ALL + %wheel ALL = (ALL) NOPASSWD:ALL +END_OF_JCLOUDS_FILE chmod 0440 /etc/sudoers mkdir -p /home/users groupadd -f wheel useradd -s /bin/bash -g wheel -m -d /home/users/foo -p 'crypt(bar)' foo mkdir -p /home/users/foo/.ssh -cat >> /home/users/foo/.ssh/authorized_keys <<'END_OF_FILE' -fooPublicKey -END_OF_FILE +cat >> /home/users/foo/.ssh/authorized_keys <<-'END_OF_JCLOUDS_FILE' + fooPublicKey +END_OF_JCLOUDS_FILE chmod 600 /home/users/foo/.ssh/authorized_keys chown -R foo /home/users/foo exec 3<> /etc/ssh/sshd_config && awk -v TEXT="PasswordAuthentication no PermitRootLogin no " 'BEGIN {print TEXT}{print}' /etc/ssh/sshd_config >&3 -/etc/init.d/sshd reload||/etc/init.d/ssh reload +hash service 2>/dev/null && service ssh reload || /etc/init.d/ssh* reload awk -v user=^${SUDO_USER:=${USER}}: -v password='crypt(0)' 'BEGIN { FS=OFS=":" } $0 ~ user { $2 = password } 1' /etc/shadow >/etc/shadow.${SUDO_USER:=${USER}} test -f /etc/shadow.${SUDO_USER:=${USER}} && mv /etc/shadow.${SUDO_USER:=${USER}} /etc/shadow diff --git a/scriptbuilder/src/test/resources/test_adminaccess_plainuser.sh b/scriptbuilder/src/test/resources/test_adminaccess_plainuser.sh index d91f18e1ef..68325f9e01 100644 --- a/scriptbuilder/src/test/resources/test_adminaccess_plainuser.sh +++ b/scriptbuilder/src/test/resources/test_adminaccess_plainuser.sh @@ -1,14 +1,14 @@ mkdir -p /home/users useradd -s /bin/bash -m -d /home/users/defaultAdminUsername -p 'crypt(0)' defaultAdminUsername mkdir -p /home/users/defaultAdminUsername/.ssh -cat >> /home/users/defaultAdminUsername/.ssh/authorized_keys <<'END_OF_FILE' -publicKey -END_OF_FILE +cat >> /home/users/defaultAdminUsername/.ssh/authorized_keys <<-'END_OF_JCLOUDS_FILE' + publicKey +END_OF_JCLOUDS_FILE chmod 600 /home/users/defaultAdminUsername/.ssh/authorized_keys mkdir -p /home/users/defaultAdminUsername/.ssh rm /home/users/defaultAdminUsername/.ssh/id_rsa -cat >> /home/users/defaultAdminUsername/.ssh/id_rsa <<'END_OF_FILE' -privateKey -END_OF_FILE +cat >> /home/users/defaultAdminUsername/.ssh/id_rsa <<-'END_OF_JCLOUDS_FILE' + privateKey +END_OF_JCLOUDS_FILE chmod 600 /home/users/defaultAdminUsername/.ssh/id_rsa chown -R defaultAdminUsername /home/users/defaultAdminUsername diff --git a/scriptbuilder/src/test/resources/test_adminaccess_standard.sh b/scriptbuilder/src/test/resources/test_adminaccess_standard.sh index 3da6b1c70c..28d184988a 100644 --- a/scriptbuilder/src/test/resources/test_adminaccess_standard.sh +++ b/scriptbuilder/src/test/resources/test_adminaccess_standard.sh @@ -1,21 +1,20 @@ -rm /etc/sudoers -cat >> /etc/sudoers <<'END_OF_FILE' -root ALL = (ALL) ALL -%wheel ALL = (ALL) NOPASSWD:ALL -END_OF_FILE +cat > /etc/sudoers <<-'END_OF_JCLOUDS_FILE' + root ALL = (ALL) ALL + %wheel ALL = (ALL) NOPASSWD:ALL +END_OF_JCLOUDS_FILE chmod 0440 /etc/sudoers mkdir -p /home/users groupadd -f wheel useradd -s /bin/bash -g wheel -m -d /home/users/defaultAdminUsername -p 'crypt(0)' defaultAdminUsername mkdir -p /home/users/defaultAdminUsername/.ssh -cat >> /home/users/defaultAdminUsername/.ssh/authorized_keys <<'END_OF_FILE' -publicKey -END_OF_FILE +cat >> /home/users/defaultAdminUsername/.ssh/authorized_keys <<-'END_OF_JCLOUDS_FILE' + publicKey +END_OF_JCLOUDS_FILE chmod 600 /home/users/defaultAdminUsername/.ssh/authorized_keys chown -R defaultAdminUsername /home/users/defaultAdminUsername exec 3<> /etc/ssh/sshd_config && awk -v TEXT="PasswordAuthentication no PermitRootLogin no " 'BEGIN {print TEXT}{print}' /etc/ssh/sshd_config >&3 -/etc/init.d/sshd reload||/etc/init.d/ssh reload +hash service 2>/dev/null && service ssh reload || /etc/init.d/ssh* reload awk -v user=^${SUDO_USER:=${USER}}: -v password='crypt(1)' 'BEGIN { FS=OFS=":" } $0 ~ user { $2 = password } 1' /etc/shadow >/etc/shadow.${SUDO_USER:=${USER}} test -f /etc/shadow.${SUDO_USER:=${USER}} && mv /etc/shadow.${SUDO_USER:=${USER}} /etc/shadow diff --git a/scriptbuilder/src/test/resources/test_ebs.sh b/scriptbuilder/src/test/resources/test_ebs.sh index d9dd5b7e3e..be055d73c6 100644 --- a/scriptbuilder/src/test/resources/test_ebs.sh +++ b/scriptbuilder/src/test/resources/test_ebs.sh @@ -11,13 +11,13 @@ function default { export INSTANCE_NAME="mkebsboot" export INSTANCE_HOME="/tmp" export LOG_DIR="/tmp/logs" - return 0 + return $? } function mkebsboot { export IMAGE_DIR="/mnt/tmp" export EBS_DEVICE="/dev/sdh" export EBS_MOUNT_POINT="/mnt/ebs" - return 0 + return $? } function findPid { unset FOUND_PID; @@ -65,50 +65,55 @@ init) mkdir -p $INSTANCE_HOME # create runscript header - cat > $INSTANCE_HOME/mkebsboot.sh < $INSTANCE_HOME/mkebsboot.sh <<-'END_OF_JCLOUDS_SCRIPT' + #!/bin/bash + set +u + shopt -s xpg_echo + shopt -s expand_aliases + + PROMPT_COMMAND='echo -ne \"\033]0;mkebsboot\007\"' + export PATH=/usr/ucb/bin:/bin:/sbin:/usr/bin:/usr/sbin + + export INSTANCE_NAME='mkebsboot' +END_OF_JCLOUDS_SCRIPT + cat >> $INSTANCE_HOME/mkebsboot.sh <<-END_OF_JCLOUDS_SCRIPT + export IMAGE_DIR='$IMAGE_DIR' + export EBS_DEVICE='$EBS_DEVICE' + export EBS_MOUNT_POINT='$EBS_MOUNT_POINT' + export INSTANCE_NAME='$INSTANCE_NAME' + export INSTANCE_HOME='$INSTANCE_HOME' + export LOG_DIR='$LOG_DIR' +END_OF_JCLOUDS_SCRIPT # add desired commands from the user - cat >> $INSTANCE_HOME/mkebsboot.sh <<'END_OF_SCRIPT' -cd $INSTANCE_HOME -echo creating a filesystem and mounting the ebs volume -mkdir -p $IMAGE_DIR $EBS_MOUNT_POINT -rm -rf $IMAGE_DIR/* -yes| mkfs -t ext3 $EBS_DEVICE 2>&- -mount $EBS_DEVICE $EBS_MOUNT_POINT -echo making a local working copy of the boot disk -rsync -ax --exclude /ubuntu/.bash_history --exclude /home/*/.bash_history --exclude /etc/ssh/ssh_host_* --exclude /etc/ssh/moduli --exclude /etc/udev/rules.d/*persistent-net.rules --exclude /var/lib/ec2/* --exclude=/mnt/* --exclude=/proc/* --exclude=/tmp/* --exclude=/dev/log / $IMAGE_DIR -echo preparing the local working copy -touch $IMAGE_DIR/etc/init.d/ec2-init-user-data -echo copying the local working copy to the ebs mount -cd $IMAGE_DIR -tar -cSf - * | tar xf - -C $EBS_MOUNT_POINT -echo size of ebs -du -sk $EBS_MOUNT_POINT -echo size of source -du -sk $IMAGE_DIR -rm -rf $IMAGE_DIR/* -umount $EBS_MOUNT_POINT -echo ----COMPLETE---- -END_OF_SCRIPT + cat >> $INSTANCE_HOME/mkebsboot.sh <<-'END_OF_JCLOUDS_SCRIPT' + cd $INSTANCE_HOME + echo creating a filesystem and mounting the ebs volume + mkdir -p $IMAGE_DIR $EBS_MOUNT_POINT + rm -rf $IMAGE_DIR/* + yes| mkfs -t ext3 $EBS_DEVICE 2>&- + mount $EBS_DEVICE $EBS_MOUNT_POINT + echo making a local working copy of the boot disk + rsync -ax --exclude /ubuntu/.bash_history --exclude /home/*/.bash_history --exclude /etc/ssh/ssh_host_* --exclude /etc/ssh/moduli --exclude /etc/udev/rules.d/*persistent-net.rules --exclude /var/lib/ec2/* --exclude=/mnt/* --exclude=/proc/* --exclude=/tmp/* --exclude=/dev/log / $IMAGE_DIR + echo preparing the local working copy + touch $IMAGE_DIR/etc/init.d/ec2-init-user-data + echo copying the local working copy to the ebs mount + cd $IMAGE_DIR + tar -cSf - * | tar xf - -C $EBS_MOUNT_POINT + echo size of ebs + du -sk $EBS_MOUNT_POINT + echo size of source + du -sk $IMAGE_DIR + rm -rf $IMAGE_DIR/* + umount $EBS_MOUNT_POINT + echo ----COMPLETE---- +END_OF_JCLOUDS_SCRIPT # add runscript footer - cat >> $INSTANCE_HOME/mkebsboot.sh <<'END_OF_SCRIPT' -exit 0 -END_OF_SCRIPT + cat >> $INSTANCE_HOME/mkebsboot.sh <<-'END_OF_JCLOUDS_SCRIPT' + exit $? + +END_OF_JCLOUDS_SCRIPT chmod u+x $INSTANCE_HOME/mkebsboot.sh ;; @@ -142,4 +147,4 @@ run) $INSTANCE_HOME/$INSTANCE_NAME.sh ;; esac -exit 0 +exit $? diff --git a/scriptbuilder/src/test/resources/test_find_pid.sh b/scriptbuilder/src/test/resources/test_find_pid.sh index fc29b66050..30206c2cce 100644 --- a/scriptbuilder/src/test/resources/test_find_pid.sh +++ b/scriptbuilder/src/test/resources/test_find_pid.sh @@ -25,4 +25,4 @@ function findPid { export PATH=/usr/ucb/bin:/bin:/sbin:/usr/bin:/usr/sbin findPid $@ || exit 1 echo $FOUND_PID -exit 0 +exit $? diff --git a/scriptbuilder/src/test/resources/test_init.cmd b/scriptbuilder/src/test/resources/test_init.cmd deleted file mode 100644 index f7cc2709c3..0000000000 --- a/scriptbuilder/src/test/resources/test_init.cmd +++ /dev/null @@ -1,134 +0,0 @@ -@echo off -set PATH= -set JAVA_HOME= -set PATH= -GOTO FUNCTION_END -:abort - echo aborting: %EXCEPTION% - exit /b 1 -:default - set INSTANCE_NAME=mkebsboot -set INSTANCE_HOME=/mnt/tmp -set LOG_DIR=/mnt/tmp - exit /b 0 -:mkebsboot - set TMP_DIR=/mnt/tmp - exit /b 0 -:findPid - set FOUND_PID= - set _expression=%1 - shift - set FIND_PROCESS=TASKLIST /FI "WINDOWTITLE eq %_expression%" /NH - FOR /F "usebackq tokens=2 delims= " %%A IN (`cmd /c "%FIND_PROCESS% 2>NUL"`) DO ( - SET FOUND_PID=%%A - ) - if defined FOUND_PID ( - exit /b 0 - ) else ( - set EXCEPTION=%_expression% not found - exit /b 1 - ) -:forget - SETLOCAL - set FOUND_PID= - set NEXT_MINUTE= - set INSTANCE_NAME=%1 - shift - set SCRIPT=%1 - shift - set LOG_DIR=%1 - shift - CALL :findProcess %INSTANCE_NAME% - if defined FOUND_PID ( - echo %INSTANCE_NAME% already running pid [%FOUND_PID%] - ) else ( - CALL :nextMinute - set _DATE=%DATE:~4% - set CMD=schtasks /create /sd %_DATE% /tn %INSTANCE_NAME% /ru System /tr "cmd /c title %INSTANCE_NAME%&%SCRIPT% >%LOG_DIR%\stdout.log 2>%LOG_DIR%\stderr.log" /sc:once /st %NEXT_MINUTE% - echo %INSTANCE_NAME% will start at %NEXT_MINUTE% - set SECONDS=%TIME:~6,2% - set /a SECOND=60-SECONDS - %CMD% >NUL - ping -n %SECONDS% 127.0.0.1 > NUL 2>&1 - CALL :findProcess %INSTANCE_NAME% - if not defined FOUND_PID ( - set EXCEPTION=%INSTANCE_NAME% did not start - abort - ) - ) - exit /b 0 -:FUNCTION_END -set PATH=c:\windows\;C:\windows\system32;c:\windows\system32\wbem -if not "%1" == "init" if not "%1" == "status" if not "%1" == "stop" if not "%1" == "start" if not "%1" == "tail" if not "%1" == "tailerr" if not "%1" == "run" ( - set EXCEPTION=bad argument: %1 not in init status stop start tail tailerr run - goto abort -) -goto CASE_%1 -:CASE_init - call :default - if errorlevel 1 goto abort - call :mkebsboot - if errorlevel 1 goto abort - md %INSTANCE_HOME% - del %INSTANCE_HOME%\mkebsboot.cmd 2>NUL - echo @echo off>>%INSTANCE_HOME%\mkebsboot.cmd - echo title mkebsboot>>%INSTANCE_HOME%\mkebsboot.cmd - echo set PATH=c:\windows\;C:\windows\system32;c:\windows\system32\wbem>>%INSTANCE_HOME%\mkebsboot.cmd - echo set INSTANCE_NAME=mkebsboot>>%INSTANCE_HOME%\mkebsboot.cmd - echo set TMP_DIR=%TMP_DIR%>>%INSTANCE_HOME%\mkebsboot.cmd - echo set INSTANCE_NAME=%INSTANCE_NAME%>>%INSTANCE_HOME%\mkebsboot.cmd - echo set INSTANCE_HOME=%INSTANCE_HOME%>>%INSTANCE_HOME%\mkebsboot.cmd - echo set LOG_DIR=%LOG_DIR%>>%INSTANCE_HOME%\mkebsboot.cmd - echo cd /d %%INSTANCE_HOME%%>>%INSTANCE_HOME%\mkebsboot.cmd - md %INSTANCE_HOME% - del %INSTANCE_HOME%\mkebsboot.cmd 2>NUL - echo @echo off>>%INSTANCE_HOME%\mkebsboot.cmd - echo title mkebsboot>>%INSTANCE_HOME%\mkebsboot.cmd - echo set PATH=c:\windows\;C:\windows\system32;c:\windows\system32\wbem>>%INSTANCE_HOME%\mkebsboot.cmd - echo set INSTANCE_NAME=mkebsboot>>%INSTANCE_HOME%\mkebsboot.cmd - echo set TMP_DIR=%TMP_DIR%>>%INSTANCE_HOME%\mkebsboot.cmd - echo set INSTANCE_NAME=%INSTANCE_NAME%>>%INSTANCE_HOME%\mkebsboot.cmd - echo set INSTANCE_HOME=%INSTANCE_HOME%>>%INSTANCE_HOME%\mkebsboot.cmd - echo set LOG_DIR=%LOG_DIR%>>%INSTANCE_HOME%\mkebsboot.cmd - echo cd /d %%INSTANCE_HOME%%>>%INSTANCE_HOME%\mkebsboot.cmd - echo exit /b 0 >>%INSTANCE_HOME%\mkebsboot.cmd - GOTO END_SWITCH -:CASE_status - call :default - if errorlevel 1 goto abort - call :findPid %INSTANCE_NAME% - if errorlevel 1 goto abort - echo [%FOUND_PID%] - GOTO END_SWITCH -:CASE_stop - call :default - if errorlevel 1 goto abort - call :findPid %INSTANCE_NAME% - if errorlevel 1 goto abort - if defined FOUND_PID ( - TASKKILL /F /T /PID %FOUND_PID% >NUL - ) - GOTO END_SWITCH -:CASE_start - call :default - if errorlevel 1 goto abort - call :forget %INSTANCE_NAME% %INSTANCE_HOME%\%INSTANCE_NAME%.cmd %LOG_DIR% - if errorlevel 1 goto abort - GOTO END_SWITCH -:CASE_tail - call :default - if errorlevel 1 goto abort - tail %LOG_DIR%\stdout.log - GOTO END_SWITCH -:CASE_tailerr - call :default - if errorlevel 1 goto abort - tail %LOG_DIR%\stderr.log - GOTO END_SWITCH -:CASE_run - call :default - if errorlevel 1 goto abort - %INSTANCE_HOME%\%INSTANCE_NAME%.cmd - GOTO END_SWITCH -:END_SWITCH -exit /b 0 diff --git a/scriptbuilder/src/test/resources/test_init.sh b/scriptbuilder/src/test/resources/test_init.sh index f4ad0e84a7..f2bf3e98f2 100644 --- a/scriptbuilder/src/test/resources/test_init.sh +++ b/scriptbuilder/src/test/resources/test_init.sh @@ -11,11 +11,11 @@ function default { export INSTANCE_NAME="mkebsboot" export INSTANCE_HOME="/mnt/tmp" export LOG_DIR="/mnt/tmp" - return 0 + return $? } function mkebsboot { export TMP_DIR="/mnt/tmp" - return 0 + return $? } function findPid { unset FOUND_PID; @@ -63,35 +63,40 @@ init) mkdir -p $INSTANCE_HOME # create runscript header - cat > $INSTANCE_HOME/mkebsboot.sh < $INSTANCE_HOME/mkebsboot.sh <<-'END_OF_JCLOUDS_SCRIPT' + #!/bin/bash + set +u + shopt -s xpg_echo + shopt -s expand_aliases + + PROMPT_COMMAND='echo -ne \"\033]0;mkebsboot\007\"' + export PATH=/usr/ucb/bin:/bin:/sbin:/usr/bin:/usr/sbin + + export INSTANCE_NAME='mkebsboot' +END_OF_JCLOUDS_SCRIPT + cat >> $INSTANCE_HOME/mkebsboot.sh <<-END_OF_JCLOUDS_SCRIPT + export TMP_DIR='$TMP_DIR' + export INSTANCE_NAME='$INSTANCE_NAME' + export INSTANCE_HOME='$INSTANCE_HOME' + export LOG_DIR='$LOG_DIR' +END_OF_JCLOUDS_SCRIPT # add desired commands from the user - cat >> $INSTANCE_HOME/mkebsboot.sh <<'END_OF_SCRIPT' -cd $INSTANCE_HOME -cat >> /tmp/$USER/scripttest/temp.txt <<'END_OF_FILE' -hello world -END_OF_FILE - -find / - -END_OF_SCRIPT + cat >> $INSTANCE_HOME/mkebsboot.sh <<-'END_OF_JCLOUDS_SCRIPT' + cd $INSTANCE_HOME + cat >> /tmp/$USER/scripttest/temp.txt <<-'END_OF_JCLOUDS_FILE' + hello world + END_OF_JCLOUDS_FILE + + find / + +END_OF_JCLOUDS_SCRIPT # add runscript footer - cat >> $INSTANCE_HOME/mkebsboot.sh <<'END_OF_SCRIPT' -exit 0 -END_OF_SCRIPT + cat >> $INSTANCE_HOME/mkebsboot.sh <<-'END_OF_JCLOUDS_SCRIPT' + exit $? + +END_OF_JCLOUDS_SCRIPT chmod u+x $INSTANCE_HOME/mkebsboot.sh ;; @@ -125,4 +130,4 @@ run) $INSTANCE_HOME/$INSTANCE_NAME.sh ;; esac -exit 0 +exit $? diff --git a/scriptbuilder/src/test/resources/test_init_script.sh b/scriptbuilder/src/test/resources/test_init_script.sh new file mode 100644 index 0000000000..a47ed3ec79 --- /dev/null +++ b/scriptbuilder/src/test/resources/test_init_script.sh @@ -0,0 +1,162 @@ +#!/bin/bash +set +u +shopt -s xpg_echo +shopt -s expand_aliases +unset PATH JAVA_HOME LD_LIBRARY_PATH +function abort { + echo "aborting: $@" 1>&2 + exit 1 +} +function default { + export INSTANCE_NAME="testcall" +export INSTANCE_HOME="/tmp/$INSTANCE_NAME" +export LOG_DIR="$INSTANCE_HOME" + return $? +} +function testcall { + return $? +} +function findPid { + unset FOUND_PID; + [ $# -eq 1 ] || { + abort "findPid requires a parameter of pattern to match" + return 1 + } + local PATTERN="$1"; shift + local _FOUND=`ps auxwww|grep "$PATTERN"|grep -v " $0"|grep -v grep|grep -v $$|awk '{print $2}'` + [ -n "$_FOUND" ] && { + export FOUND_PID=$_FOUND + return 0 + } || { + return 1 + } +} +function forget { + unset FOUND_PID; + [ $# -eq 3 ] || { + abort "forget requires parameters INSTANCE_NAME SCRIPT LOG_DIR" + return 1 + } + local INSTANCE_NAME="$1"; shift + local SCRIPT="$1"; shift + local LOG_DIR="$1"; shift + mkdir -p $LOG_DIR + findPid $INSTANCE_NAME + [ -n "$FOUND_PID" -a -f $LOG_DIR/stdout.log ] && { + echo $INSTANCE_NAME already running pid [$FOUND_PID] + return 1; + } || { + nohup $SCRIPT >$LOG_DIR/stdout.log 2>$LOG_DIR/stderr.log & + RETURN=$? + # this is generally followed by findPid, so we shouldn't exit + # immediately as the proc may not have registered in ps, yet + test $RETURN && sleep 1 + return $RETURN; + } +} +export PATH=/usr/ucb/bin:/bin:/sbin:/usr/bin:/usr/sbin +case $1 in +init) + default || exit 1 + testcall || exit 1 + echo hello + mkdir -p $INSTANCE_HOME + + # create runscript header + cat > $INSTANCE_HOME/testcall.sh <<-'END_OF_JCLOUDS_SCRIPT' + #!/bin/bash + set +u + shopt -s xpg_echo + shopt -s expand_aliases + + PROMPT_COMMAND='echo -ne \"\033]0;testcall\007\"' + export PATH=/usr/ucb/bin:/bin:/sbin:/usr/bin:/usr/sbin + + export INSTANCE_NAME='testcall' +END_OF_JCLOUDS_SCRIPT + cat >> $INSTANCE_HOME/testcall.sh <<-END_OF_JCLOUDS_SCRIPT + export INSTANCE_NAME='$INSTANCE_NAME' + export INSTANCE_HOME='$INSTANCE_HOME' + export LOG_DIR='$LOG_DIR' +END_OF_JCLOUDS_SCRIPT + cat >> $INSTANCE_HOME/testcall.sh <<-'END_OF_JCLOUDS_SCRIPT' + function abort { + echo "aborting: $@" 1>&2 + exit 1 +} +function sourceEnvFile { + [ $# -eq 1 ] || { + abort "sourceEnvFile requires a parameter of the file to source" + return 1 + } + local ENV_FILE="$1"; shift + . "$ENV_FILE" || { + abort "Please append 'return 0' to the end of '$ENV_FILE'" + return 1 + } + return 0 +} + +END_OF_JCLOUDS_SCRIPT + + # add desired commands from the user + cat >> $INSTANCE_HOME/testcall.sh <<-'END_OF_JCLOUDS_SCRIPT' + cd $INSTANCE_HOME + rm -f $INSTANCE_HOME/rc + trap 'echo $?>$INSTANCE_HOME/rc' 0 1 2 3 15 + sourceEnvFile foo || exit 1 + + find / + +END_OF_JCLOUDS_SCRIPT + + # add runscript footer + cat >> $INSTANCE_HOME/testcall.sh <<-'END_OF_JCLOUDS_SCRIPT' + exit $? + +END_OF_JCLOUDS_SCRIPT + + chmod u+x $INSTANCE_HOME/testcall.sh + ;; +status) + default || exit 1 + findPid $INSTANCE_NAME || exit 1 + echo [$FOUND_PID] + ;; +stop) + default || exit 1 + findPid $INSTANCE_NAME || exit 1 + [ -n "$FOUND_PID" ] && { + echo stopping $FOUND_PID + kill -9 $FOUND_PID + } + ;; +start) + default || exit 1 + forget $INSTANCE_NAME $INSTANCE_HOME/$INSTANCE_NAME.sh $LOG_DIR || exit 1 + ;; +stdout) + default || exit 1 + cat $LOG_DIR/stdout.log + ;; +stderr) + default || exit 1 + cat $LOG_DIR/stderr.log + ;; +exitstatus) + default || exit 1 + [ -f $LOG_DIR/rc ] && cat $LOG_DIR/rc;; +tail) + default || exit 1 + tail $LOG_DIR/stdout.log + ;; +tailerr) + default || exit 1 + tail $LOG_DIR/stderr.log + ;; +run) + default || exit 1 + $INSTANCE_HOME/$INSTANCE_NAME.sh + ;; +esac +exit $? diff --git a/scriptbuilder/src/test/resources/test_install_jdk_from_url.sh b/scriptbuilder/src/test/resources/test_install_jdk_from_url.sh index a96b138d20..f1638bb15d 100644 --- a/scriptbuilder/src/test/resources/test_install_jdk_from_url.sh +++ b/scriptbuilder/src/test/resources/test_install_jdk_from_url.sh @@ -2,20 +2,20 @@ setupPublicCurl || return 1 curl -q -s -S -L --connect-timeout 10 --max-time 600 --retry 20 -X GET http://download.oracle.com/otn-pub/java/jdk/7/jdk-7-linux-x64.tar.gz |(mkdir -p /usr/local &&cd /usr/local &&tar -xpzf -) mv /usr/local/jdk* /usr/local/jdk/ test -n "$SUDO_USER" && -cat >> /home/$SUDO_USER/.bashrc <<'END_OF_FILE' -export JAVA_HOME=/usr/local/jdk -export PATH=$JAVA_HOME/bin:$PATH -END_OF_FILE -cat >> /etc/bashrc <<'END_OF_FILE' -export JAVA_HOME=/usr/local/jdk -export PATH=$JAVA_HOME/bin:$PATH -END_OF_FILE -cat >> $HOME/.bashrc <<'END_OF_FILE' -export JAVA_HOME=/usr/local/jdk -export PATH=$JAVA_HOME/bin:$PATH -END_OF_FILE -cat >> /etc/skel/.bashrc <<'END_OF_FILE' -export JAVA_HOME=/usr/local/jdk -export PATH=$JAVA_HOME/bin:$PATH -END_OF_FILE +cat >> /home/$SUDO_USER/.bashrc <<-'END_OF_JCLOUDS_FILE' + export JAVA_HOME=/usr/local/jdk + export PATH=$JAVA_HOME/bin:$PATH +END_OF_JCLOUDS_FILE +cat >> /etc/bashrc <<-'END_OF_JCLOUDS_FILE' + export JAVA_HOME=/usr/local/jdk + export PATH=$JAVA_HOME/bin:$PATH +END_OF_JCLOUDS_FILE +cat >> $HOME/.bashrc <<-'END_OF_JCLOUDS_FILE' + export JAVA_HOME=/usr/local/jdk + export PATH=$JAVA_HOME/bin:$PATH +END_OF_JCLOUDS_FILE +cat >> /etc/skel/.bashrc <<-'END_OF_JCLOUDS_FILE' + export JAVA_HOME=/usr/local/jdk + export PATH=$JAVA_HOME/bin:$PATH +END_OF_JCLOUDS_FILE ln -fs /usr/local/jdk/bin/java /usr/bin/java diff --git a/scriptbuilder/src/test/resources/test_install_jdk_scriptbuilder.sh b/scriptbuilder/src/test/resources/test_install_jdk_scriptbuilder.sh index b6e0722e1d..971338e0e8 100644 --- a/scriptbuilder/src/test/resources/test_install_jdk_scriptbuilder.sh +++ b/scriptbuilder/src/test/resources/test_install_jdk_scriptbuilder.sh @@ -9,12 +9,12 @@ function abort { } function default { export INSTANCE_NAME="install_jdk" -export INSTANCE_HOME="$HOME/instances/install_jdk" -export LOG_DIR="$HOME/instances/install_jdk" - return 0 +export INSTANCE_HOME="/tmp/$INSTANCE_NAME" +export LOG_DIR="$INSTANCE_HOME" + return $? } function install_jdk { - return 0 + return $? } function findPid { unset FOUND_PID; @@ -62,35 +62,41 @@ init) mkdir -p $INSTANCE_HOME # create runscript header - cat > $INSTANCE_HOME/install_jdk.sh <&2 + cat > $INSTANCE_HOME/install_jdk.sh <<-'END_OF_JCLOUDS_SCRIPT' + #!/bin/bash + set +u + shopt -s xpg_echo + shopt -s expand_aliases + + PROMPT_COMMAND='echo -ne \"\033]0;install_jdk\007\"' + export PATH=/usr/ucb/bin:/bin:/sbin:/usr/bin:/usr/sbin + + export INSTANCE_NAME='install_jdk' +END_OF_JCLOUDS_SCRIPT + cat >> $INSTANCE_HOME/install_jdk.sh <<-END_OF_JCLOUDS_SCRIPT + export INSTANCE_NAME='$INSTANCE_NAME' + export INSTANCE_HOME='$INSTANCE_HOME' + export LOG_DIR='$LOG_DIR' +END_OF_JCLOUDS_SCRIPT + cat >> $INSTANCE_HOME/install_jdk.sh <<-'END_OF_JCLOUDS_SCRIPT' + function abort { + echo "aborting: $@" 1>&2 exit 1 } alias apt-get-install="apt-get install -f -y -qq --force-yes" alias apt-get-upgrade="(apt-get update -qq&&apt-get upgrade -y -qq)" function ensure_cmd_or_install_package_apt(){ - local cmd=\$1 - local pkg=\$2 + local cmd=$1 + local pkg=$2 - hash \$cmd 2>/dev/null || apt-get-install \$pkg || ( apt-get-upgrade && apt-get-install \$pkg ) + hash $cmd 2>/dev/null || apt-get-install $pkg || ( apt-get-upgrade && apt-get-install $pkg ) } function ensure_cmd_or_install_package_yum(){ - local cmd=\$1 - local pkg=\$2 - hash \$cmd 2>/dev/null || yum --nogpgcheck -y ensure \$pkg + local cmd=$1 + local pkg=$2 + hash $cmd 2>/dev/null || yum --nogpgcheck -y ensure $pkg } function ensure_netutils_apt() { @@ -106,7 +112,7 @@ function ensure_netutils_yum() { # most network services require that the hostname is in # the /etc/hosts file, or they won't operate function ensure_hostname_in_hosts() { - egrep -q `hostname` /etc/hosts || awk -v hostname=`hostname` 'END { print \$1" "hostname }' /proc/net/arp >> /etc/hosts + egrep -q `hostname` /etc/hosts || awk -v hostname=`hostname` 'END { print $1" "hostname }' /proc/net/arp >> /etc/hosts } # download locations for many services are at public dns @@ -127,39 +133,43 @@ function setupPublicCurl() { ensure_can_resolve_public_dns return 0 } -END_OF_SCRIPT + +END_OF_JCLOUDS_SCRIPT # add desired commands from the user - cat >> $INSTANCE_HOME/install_jdk.sh <<'END_OF_SCRIPT' -cd $INSTANCE_HOME -setupPublicCurl || exit 1 -curl -q -s -S -L --connect-timeout 10 --max-time 600 --retry 20 -X GET http://download.oracle.com/otn-pub/java/jdk/7/jdk-7-linux-x64.tar.gz |(mkdir -p /usr/local &&cd /usr/local &&tar -xpzf -) -mv /usr/local/jdk* /usr/local/jdk/ -test -n "$SUDO_USER" && -cat >> /home/$SUDO_USER/.bashrc <<'END_OF_FILE' -export JAVA_HOME=/usr/local/jdk -export PATH=$JAVA_HOME/bin:$PATH -END_OF_FILE -cat >> /etc/bashrc <<'END_OF_FILE' -export JAVA_HOME=/usr/local/jdk -export PATH=$JAVA_HOME/bin:$PATH -END_OF_FILE -cat >> $HOME/.bashrc <<'END_OF_FILE' -export JAVA_HOME=/usr/local/jdk -export PATH=$JAVA_HOME/bin:$PATH -END_OF_FILE -cat >> /etc/skel/.bashrc <<'END_OF_FILE' -export JAVA_HOME=/usr/local/jdk -export PATH=$JAVA_HOME/bin:$PATH -END_OF_FILE -ln -fs /usr/local/jdk/bin/java /usr/bin/java - -END_OF_SCRIPT + cat >> $INSTANCE_HOME/install_jdk.sh <<-'END_OF_JCLOUDS_SCRIPT' + cd $INSTANCE_HOME + rm -f $INSTANCE_HOME/rc + trap 'echo $?>$INSTANCE_HOME/rc' 0 1 2 3 15 + setupPublicCurl || exit 1 + curl -q -s -S -L --connect-timeout 10 --max-time 600 --retry 20 -X GET http://download.oracle.com/otn-pub/java/jdk/7/jdk-7-linux-x64.tar.gz |(mkdir -p /usr/local &&cd /usr/local &&tar -xpzf -) + mv /usr/local/jdk* /usr/local/jdk/ + test -n "$SUDO_USER" && + cat >> /home/$SUDO_USER/.bashrc <<-'END_OF_JCLOUDS_FILE' + export JAVA_HOME=/usr/local/jdk + export PATH=$JAVA_HOME/bin:$PATH + END_OF_JCLOUDS_FILE + cat >> /etc/bashrc <<-'END_OF_JCLOUDS_FILE' + export JAVA_HOME=/usr/local/jdk + export PATH=$JAVA_HOME/bin:$PATH + END_OF_JCLOUDS_FILE + cat >> $HOME/.bashrc <<-'END_OF_JCLOUDS_FILE' + export JAVA_HOME=/usr/local/jdk + export PATH=$JAVA_HOME/bin:$PATH + END_OF_JCLOUDS_FILE + cat >> /etc/skel/.bashrc <<-'END_OF_JCLOUDS_FILE' + export JAVA_HOME=/usr/local/jdk + export PATH=$JAVA_HOME/bin:$PATH + END_OF_JCLOUDS_FILE + ln -fs /usr/local/jdk/bin/java /usr/bin/java + +END_OF_JCLOUDS_SCRIPT # add runscript footer - cat >> $INSTANCE_HOME/install_jdk.sh <<'END_OF_SCRIPT' -exit 0 -END_OF_SCRIPT + cat >> $INSTANCE_HOME/install_jdk.sh <<-'END_OF_JCLOUDS_SCRIPT' + exit $? + +END_OF_JCLOUDS_SCRIPT chmod u+x $INSTANCE_HOME/install_jdk.sh ;; @@ -180,6 +190,17 @@ start) default || exit 1 forget $INSTANCE_NAME $INSTANCE_HOME/$INSTANCE_NAME.sh $LOG_DIR || exit 1 ;; +stdout) + default || exit 1 + cat $LOG_DIR/stdout.log + ;; +stderr) + default || exit 1 + cat $LOG_DIR/stderr.log + ;; +exitstatus) + default || exit 1 + [ -f $LOG_DIR/rc ] && cat $LOG_DIR/rc;; tail) default || exit 1 tail $LOG_DIR/stdout.log @@ -193,4 +214,4 @@ run) $INSTANCE_HOME/$INSTANCE_NAME.sh ;; esac -exit 0 +exit $? diff --git a/scriptbuilder/src/test/resources/test_runrun.cmd b/scriptbuilder/src/test/resources/test_runrun.cmd deleted file mode 100644 index ab5cf7c7bd..0000000000 --- a/scriptbuilder/src/test/resources/test_runrun.cmd +++ /dev/null @@ -1,17 +0,0 @@ -md %TEMP%\%USERNAME%\scripttest -del %TEMP%\%USERNAME%\scripttest\yahooprod.cmd 2>NUL -echo @echo off>>%TEMP%\%USERNAME%\scripttest\yahooprod.cmd -echo title yahooprod>>%TEMP%\%USERNAME%\scripttest\yahooprod.cmd -echo set PATH=c:\windows\;C:\windows\system32;c:\windows\system32\wbem>>%TEMP%\%USERNAME%\scripttest\yahooprod.cmd -echo set INSTANCE_NAME=yahooprod>>%TEMP%\%USERNAME%\scripttest\yahooprod.cmd -echo set JAVA_HOME=%JAVA_HOME%>>%TEMP%\%USERNAME%\scripttest\yahooprod.cmd -echo cd /d %TEMP%\%USERNAME%\scripttest>>%TEMP%\%USERNAME%\scripttest\yahooprod.cmd -md %TEMP%\%USERNAME%\scripttest -del %TEMP%\%USERNAME%\scripttest\yahooprod.cmd 2>NUL -echo @echo off>>%TEMP%\%USERNAME%\scripttest\yahooprod.cmd -echo title yahooprod>>%TEMP%\%USERNAME%\scripttest\yahooprod.cmd -echo set PATH=c:\windows\;C:\windows\system32;c:\windows\system32\wbem>>%TEMP%\%USERNAME%\scripttest\yahooprod.cmd -echo set INSTANCE_NAME=yahooprod>>%TEMP%\%USERNAME%\scripttest\yahooprod.cmd -echo set JAVA_HOME=%JAVA_HOME%>>%TEMP%\%USERNAME%\scripttest\yahooprod.cmd -echo cd /d %TEMP%\%USERNAME%\scripttest>>%TEMP%\%USERNAME%\scripttest\yahooprod.cmd -echo exit /b 0 >>%TEMP%\%USERNAME%\scripttest\yahooprod.cmd diff --git a/scriptbuilder/src/test/resources/test_runrun.sh b/scriptbuilder/src/test/resources/test_runrun.sh index ee687974f6..fc7935ba48 100644 --- a/scriptbuilder/src/test/resources/test_runrun.sh +++ b/scriptbuilder/src/test/resources/test_runrun.sh @@ -1,33 +1,38 @@ mkdir -p /tmp/$USER/scripttest # create runscript header -cat > /tmp/$USER/scripttest/yahooprod.sh < /tmp/$USER/scripttest/yahooprod.sh <<-'END_OF_JCLOUDS_SCRIPT' + #!/bin/bash + set +u + shopt -s xpg_echo + shopt -s expand_aliases + + PROMPT_COMMAND='echo -ne \"\033]0;yahooprod\007\"' + export PATH=/usr/ucb/bin:/bin:/sbin:/usr/bin:/usr/sbin + + export INSTANCE_NAME='yahooprod' +END_OF_JCLOUDS_SCRIPT +cat >> /tmp/$USER/scripttest/yahooprod.sh <<-END_OF_JCLOUDS_SCRIPT + export JAVA_HOME='$JAVA_HOME' +END_OF_JCLOUDS_SCRIPT # add desired commands from the user -cat >> /tmp/$USER/scripttest/yahooprod.sh <<'END_OF_SCRIPT' -cd /tmp/$USER/scripttest -echo hello - -cat >> /tmp/$USER/scripttest/temp.txt <<'END_OF_FILE' -hello world -END_OF_FILE - -echo $JAVA_HOME/bin/java -DinstanceName=$INSTANCE_NAME myServer.Main - -END_OF_SCRIPT +cat >> /tmp/$USER/scripttest/yahooprod.sh <<-'END_OF_JCLOUDS_SCRIPT' + cd /tmp/$USER/scripttest + echo hello + + cat >> /tmp/$USER/scripttest/temp.txt <<-'END_OF_JCLOUDS_FILE' + hello world + END_OF_JCLOUDS_FILE + + echo $JAVA_HOME/bin/java -DinstanceName=$INSTANCE_NAME myServer.Main + +END_OF_JCLOUDS_SCRIPT # add runscript footer -cat >> /tmp/$USER/scripttest/yahooprod.sh <<'END_OF_SCRIPT' -exit 0 -END_OF_SCRIPT +cat >> /tmp/$USER/scripttest/yahooprod.sh <<-'END_OF_JCLOUDS_SCRIPT' + exit $? + +END_OF_JCLOUDS_SCRIPT chmod u+x /tmp/$USER/scripttest/yahooprod.sh diff --git a/scriptbuilder/src/test/resources/test_script.sh b/scriptbuilder/src/test/resources/test_script.sh index 5fde6790b1..ea7e1de915 100644 --- a/scriptbuilder/src/test/resources/test_script.sh +++ b/scriptbuilder/src/test/resources/test_script.sh @@ -9,7 +9,7 @@ function abort { } function default { export RUNTIME="Moo" - return 0 + return $? } export PATH=/usr/ucb/bin:/bin:/sbin:/usr/bin:/usr/sbin case $1 in @@ -22,10 +22,10 @@ stop) echo stop $RUNTIME ;; status) - cat >> /tmp/$USER/scripttest/temp.txt <<'END_OF_FILE' -hello world -END_OF_FILE + cat >> /tmp/$USER/scripttest/temp.txt <<-'END_OF_JCLOUDS_FILE' + hello world +END_OF_JCLOUDS_FILE echo "the following should be []: [$RUNTIME]" ;; esac -exit 0 +exit $? diff --git a/scriptbuilder/src/test/resources/test_seek_and_destroy.sh b/scriptbuilder/src/test/resources/test_seek_and_destroy.sh index dad6f6dc5b..3f8d68c701 100644 --- a/scriptbuilder/src/test/resources/test_seek_and_destroy.sh +++ b/scriptbuilder/src/test/resources/test_seek_and_destroy.sh @@ -28,4 +28,4 @@ findPid $@ || exit 1 echo stopping $FOUND_PID kill -9 $FOUND_PID } -exit 0 +exit $?