diff --git a/README.txt b/README.txt index 3b899a09ef..2a1849371c 100644 --- a/README.txt +++ b/README.txt @@ -7,8 +7,8 @@ two abstractions at the moment: compute and blobstore. compute helps you bootstrap machines in the cloud. blobstore helps you manage key-value data. -our current version is 1.2.0 -our next maintenance version is 1.2.1-SNAPSHOT +our current version is 1.2.1 +our next maintenance version is 1.2.2-SNAPSHOT our dev version is 1.3.0-SNAPSHOT check out our examples site! https://github.com/jclouds/jclouds-examples diff --git a/compute/src/main/java/org/jclouds/compute/callables/InitScriptConfigurationForTasks.java b/compute/src/main/java/org/jclouds/compute/callables/InitScriptConfigurationForTasks.java new file mode 100644 index 0000000000..ef6eab8804 --- /dev/null +++ b/compute/src/main/java/org/jclouds/compute/callables/InitScriptConfigurationForTasks.java @@ -0,0 +1,123 @@ +/** + * 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.callables; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.File; +import java.util.concurrent.atomic.AtomicInteger; + +import javax.inject.Named; +import javax.inject.Singleton; + +import org.jclouds.scriptbuilder.InitBuilder; + +import com.google.common.base.Supplier; +import com.google.inject.Inject; + +/** + * + * @author Adrian Cole + */ +@Singleton +public class InitScriptConfigurationForTasks { + public static final String PROPERTY_INIT_SCRIPT_PATTERN = "jclouds.compute.init-script-pattern"; + + public static InitScriptConfigurationForTasks create() { + return new InitScriptConfigurationForTasks(); + } + + private String basedir = "/tmp"; + private String initScriptPattern = basedir + "/init-%s"; + private Supplier suffixSupplier; + + protected InitScriptConfigurationForTasks() { + appendCurrentTimeMillisToAnonymousTaskNames(); + } + + @Inject(optional = true) + public InitScriptConfigurationForTasks initScriptPattern( + @Named(PROPERTY_INIT_SCRIPT_PATTERN) String initScriptPattern) { + this.initScriptPattern = checkNotNull(initScriptPattern, "initScriptPattern ex. /tmp/init-%s"); + this.basedir = new File(initScriptPattern).getParent(); + return this; + } + + public InitScriptConfigurationForTasks appendCurrentTimeMillisToAnonymousTaskNames() { + this.suffixSupplier = new Supplier() { + + @Override + public String get() { + return System.currentTimeMillis() + ""; + } + + @Override + public String toString() { + return "currentTimeMillis()"; + } + }; + return this; + } + + public InitScriptConfigurationForTasks appendIncrementingNumberToAnonymousTaskNames() { + this.suffixSupplier = new Supplier() { + private final AtomicInteger integer = new AtomicInteger(); + + @Override + public String get() { + return integer.getAndIncrement() + ""; + } + + @Override + public String toString() { + return "incrementingNumber()"; + } + }; + return this; + } + + /** + * Directory where the init script is stored. the runtime directory of the process will be in + * this dir/taskName + */ + public String getBasedir() { + return basedir; + } + + /** + * + * @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 + */ + public String getInitScriptPattern() { + return initScriptPattern; + } + + /** + * @return suffix where the taskName isn't set. by default this is + * {@link System#currentTimeMillis} + * @see #appendCurrentTimeMillisToAnonymousTaskNames + * @see #appendIncrementingNumberToAnonymousTaskNames + */ + public Supplier getAnonymousTaskSuffixSupplier() { + return suffixSupplier; + } +} \ No newline at end of file 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 cf834964a4..74e9469f6a 100644 --- a/compute/src/main/java/org/jclouds/compute/callables/RunScriptOnNodeAsInitScriptUsingSsh.java +++ b/compute/src/main/java/org/jclouds/compute/callables/RunScriptOnNodeAsInitScriptUsingSsh.java @@ -44,7 +44,6 @@ import org.jclouds.ssh.SshException; import com.google.common.base.Function; import com.google.common.base.Splitter; -import com.google.inject.Inject; import com.google.inject.assistedinject.Assisted; import com.google.inject.assistedinject.AssistedInject; @@ -53,38 +52,28 @@ import com.google.inject.assistedinject.AssistedInject; * @author Adrian Cole */ public class RunScriptOnNodeAsInitScriptUsingSsh extends SudoAwareInitManager implements RunScriptOnNode { - public static final String PROPERTY_INIT_SCRIPT_PATTERN = "jclouds.compute.init-script-pattern"; @Resource @Named(ComputeServiceConstants.COMPUTE_LOGGER) protected Logger logger = Logger.NULL; + protected final String initFile; /** - * - * determines the naming convention of init scripts. - * - * ex. {@code /tmp/init-%s} + * @return the absolute path to the file on disk relating to this task. */ - @Inject(optional = true) - @Named(PROPERTY_INIT_SCRIPT_PATTERN) - private String initScriptPattern = "/tmp/init-%s"; + public String getInitFile() { + return initFile; + } @AssistedInject public RunScriptOnNodeAsInitScriptUsingSsh(Function sshFactory, - @Assisted NodeMetadata node, @Assisted Statement script, @Assisted RunScriptOptions options) { - super(sshFactory, options.shouldRunAsRoot(), checkNotNull(node, "node"), init(script, options.getTaskName())); - this.initFile = String.format(initScriptPattern, options.getTaskName()); - } - - private static InitBuilder init(Statement script, String name) { - if (name == null) { - if (checkNotNull(script, "script") instanceof InitBuilder) - name = InitBuilder.class.cast(script).getInstanceName(); - else - name = "jclouds-script-" + System.currentTimeMillis(); - } - return checkNotNull(script, "script") instanceof InitBuilder ? InitBuilder.class.cast(script) : createInitScript( - name, script); + 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) + : createInitScript(checkNotNull(initScriptConfiguration, "initScriptConfiguration"), options + .getTaskName(), script)); + this.initFile = String.format(initScriptConfiguration.getInitScriptPattern(), init.getInstanceName()); } @Override @@ -105,9 +94,12 @@ public class RunScriptOnNodeAsInitScriptUsingSsh extends SudoAwareInitManager im } } - public static InitBuilder createInitScript(String name, Statement script) { - String path = "/tmp/" + name; - return new InitBuilder(name, path, path, Collections. emptyMap(), Collections.singleton(script)); + public static InitBuilder 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)); } protected void refreshSshIfNewAdminCredentialsConfigured(AdminAccess input) { 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 136db8e607..d95899ff33 100644 --- a/compute/src/main/java/org/jclouds/compute/callables/RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilComplete.java +++ b/compute/src/main/java/org/jclouds/compute/callables/RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilComplete.java @@ -47,9 +47,9 @@ public class RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilComplete extends Ru @Inject public RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilComplete( BlockUntilInitScriptStatusIsZeroThenReturnOutput.Factory statusFactory, Timeouts timeouts, - Function sshFactory, @Assisted NodeMetadata node, @Assisted Statement script, - @Assisted RunScriptOptions options) { - super(sshFactory, node, script, options); + Function sshFactory, InitScriptConfigurationForTasks initScriptConfiguration, + @Assisted NodeMetadata node, @Assisted Statement script, @Assisted RunScriptOptions options) { + super(sshFactory, initScriptConfiguration, node, script, options); this.statusFactory = checkNotNull(statusFactory, "statusFactory"); this.timeouts = checkNotNull(timeouts, "timeouts"); } 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 daf50668b3..17326073c7 100644 --- a/compute/src/main/java/org/jclouds/compute/callables/SudoAwareInitManager.java +++ b/compute/src/main/java/org/jclouds/compute/callables/SudoAwareInitManager.java @@ -57,8 +57,8 @@ public class SudoAwareInitManager { InitBuilder init) { this.sshFactory = checkNotNull(sshFactory, "sshFactory"); this.runAsRoot = runAsRoot; - this.node = node; - this.init = init; + this.node = checkNotNull(node, "node"); + this.init = checkNotNull(init, "init"); } @PostConstruct diff --git a/compute/src/test/java/org/jclouds/compute/callables/InitScriptConfigurationForTasksTest.java b/compute/src/test/java/org/jclouds/compute/callables/InitScriptConfigurationForTasksTest.java new file mode 100644 index 0000000000..1e55115dc9 --- /dev/null +++ b/compute/src/test/java/org/jclouds/compute/callables/InitScriptConfigurationForTasksTest.java @@ -0,0 +1,81 @@ +/** + * 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.callables; + +import static org.testng.Assert.assertEquals; + +import org.testng.annotations.Test; + +import com.google.inject.AbstractModule; +import com.google.inject.Guice; +import com.google.inject.name.Names; + +/** + * @author Adam Lowe + */ +@Test(groups = "unit", singleThreaded = true, testName = "InitScriptConfigurationForTasksTest") +public class InitScriptConfigurationForTasksTest { + + public void testDefaults() { + InitScriptConfigurationForTasks config = InitScriptConfigurationForTasks.create(); + assertEquals(config.getAnonymousTaskSuffixSupplier().toString(), "currentTimeMillis()"); + assertEquals(config.getBasedir(), "/tmp"); + assertEquals(config.getInitScriptPattern(), "/tmp/init-%s"); + } + + public void testPatternUpdatesBasedir() { + InitScriptConfigurationForTasks config = InitScriptConfigurationForTasks.create(); + config.initScriptPattern("/var/foo-init-%s"); + assertEquals(config.getBasedir(), "/var"); + assertEquals(config.getInitScriptPattern(), "/var/foo-init-%s"); + } + + public void testPatternUpdatesBasedirGuice() { + InitScriptConfigurationForTasks config = Guice.createInjector(new AbstractModule() { + + @Override + protected void configure() { + bindConstant().annotatedWith(Names.named(InitScriptConfigurationForTasks.PROPERTY_INIT_SCRIPT_PATTERN)).to( + "/var/foo-init-%s"); + } + + }).getInstance(InitScriptConfigurationForTasks.class); + config.initScriptPattern("/var/foo-init-%s"); + assertEquals(config.getBasedir(), "/var"); + assertEquals(config.getInitScriptPattern(), "/var/foo-init-%s"); + } + + public void testCurrentTimeSupplier() throws InterruptedException { + InitScriptConfigurationForTasks config = InitScriptConfigurationForTasks.create(); + long time1 = Long.parseLong(config.getAnonymousTaskSuffixSupplier().get()); + assert time1 <= System.currentTimeMillis(); + Thread.sleep(10); + long time2 = Long.parseLong(config.getAnonymousTaskSuffixSupplier().get()); + assert time2 <= System.currentTimeMillis(); + assert time2 > time1; + } + + public void testIncrementingTimeSupplier() throws InterruptedException { + InitScriptConfigurationForTasks config = InitScriptConfigurationForTasks.create() + .appendIncrementingNumberToAnonymousTaskNames(); + assertEquals(config.getAnonymousTaskSuffixSupplier().get(), "0"); + assertEquals(config.getAnonymousTaskSuffixSupplier().get(), "1"); + } + +} diff --git a/compute/src/test/java/org/jclouds/compute/callables/RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilCompleteTest.java b/compute/src/test/java/org/jclouds/compute/callables/RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilCompleteTest.java new file mode 100644 index 0000000000..6f2e092c85 --- /dev/null +++ b/compute/src/test/java/org/jclouds/compute/callables/RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilCompleteTest.java @@ -0,0 +1,245 @@ +/** + * 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.callables; + +import static org.easymock.EasyMock.createMock; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.replay; +import static org.easymock.EasyMock.verify; +import static org.jclouds.scriptbuilder.domain.Statements.exec; +import static org.testng.Assert.assertEquals; + +import org.jclouds.Constants; +import org.jclouds.compute.domain.ExecResponse; +import org.jclouds.compute.domain.NodeMetadata; +import org.jclouds.compute.domain.NodeMetadataBuilder; +import org.jclouds.compute.domain.NodeState; +import org.jclouds.compute.options.RunScriptOptions; +import org.jclouds.compute.reference.ComputeServiceConstants; +import org.jclouds.compute.reference.ComputeServiceConstants.Timeouts; +import org.jclouds.concurrent.MoreExecutors; +import org.jclouds.concurrent.config.ExecutorServiceModule; +import org.jclouds.domain.Credentials; +import org.jclouds.scriptbuilder.InitBuilder; +import org.jclouds.scriptbuilder.domain.OsFamily; +import org.jclouds.scriptbuilder.domain.Statement; +import org.jclouds.ssh.SshClient; +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.inject.AbstractModule; +import com.google.inject.Guice; +import com.google.inject.assistedinject.FactoryModuleBuilder; +import com.google.inject.name.Names; + +/** + * @author Adrian Cole + */ +@Test(groups = "unit", singleThreaded = true, testName = "RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilCompleteTest") +public class RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilCompleteTest { + + BlockUntilInitScriptStatusIsZeroThenReturnOutput.Factory statusFactory = Guice.createInjector( + new ExecutorServiceModule(MoreExecutors.sameThreadExecutor(), MoreExecutors.sameThreadExecutor()), + new AbstractModule() { + + @Override + protected void configure() { + bindConstant().annotatedWith(Names.named(Constants.PROPERTY_USER_THREADS)).to(1); + bindConstant().annotatedWith(Names.named(Constants.PROPERTY_IO_WORKER_THREADS)).to(1); + bindConstant().annotatedWith(Names.named(ComputeServiceConstants.PROPERTY_TIMEOUT_SCRIPT_COMPLETE)) + .to(100); + install(new FactoryModuleBuilder() + .build(BlockUntilInitScriptStatusIsZeroThenReturnOutput.Factory.class)); + } + }).getInstance(BlockUntilInitScriptStatusIsZeroThenReturnOutput.Factory.class); + + // fail faster than normal + Timeouts timeouts = Guice.createInjector(new AbstractModule() { + + @Override + protected void configure() { + bindConstant().annotatedWith(Names.named(ComputeServiceConstants.PROPERTY_TIMEOUT_SCRIPT_COMPLETE)).to(100l); + } + }).getInstance(Timeouts.class); + + @Test(expectedExceptions = IllegalStateException.class) + public void testWithoutInitThrowsIllegalStateException() { + Statement command = exec("doFoo"); + NodeMetadata node = new NodeMetadataBuilder().ids("id").state(NodeState.RUNNING).credentials( + new Credentials("tester", "notalot")).build(); + + SshClient sshClient = createMock(SshClient.class); + + replay(sshClient); + + RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilComplete testMe = new RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilComplete( + statusFactory, timeouts, Functions.forMap(ImmutableMap.of(node, sshClient)), + InitScriptConfigurationForTasks.create().appendIncrementingNumberToAnonymousTaskNames(), node, command, + new RunScriptOptions()); + + testMe.call(); + } + + public void testDefault() { + Statement command = exec("doFoo"); + NodeMetadata node = new NodeMetadataBuilder().ids("id").state(NodeState.RUNNING).credentials( + new Credentials("tester", "notalot")).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)); + + 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("./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)); + + // 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)); + + sshClient.disconnect(); + replay(sshClient); + + RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilComplete testMe = new RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilComplete( + statusFactory, timeouts, Functions.forMap(ImmutableMap.of(node, sshClient)), + InitScriptConfigurationForTasks.create().appendIncrementingNumberToAnonymousTaskNames(), node, command, + new RunScriptOptions()); + + 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", 0)); + + verify(sshClient); + } + + public void testWithSudoPassword() { + Statement command = exec("doFoo"); + NodeMetadata node = new NodeMetadataBuilder().ids("id").state(NodeState.RUNNING).credentials( + new Credentials("tester", "notalot")).adminPassword("rootme").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)); + + 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("./jclouds-script-0 init")).andReturn(new ExecResponse("", "", 0)); + + // since there's an adminPassword we must pass this in + expect(sshClient.exec("echo 'rootme'|sudo -S ./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)); + + sshClient.disconnect(); + replay(sshClient); + + RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilComplete testMe = new RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilComplete( + statusFactory, timeouts, Functions.forMap(ImmutableMap.of(node, sshClient)), + InitScriptConfigurationForTasks.create().appendIncrementingNumberToAnonymousTaskNames(), node, command, + new RunScriptOptions()); + + 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", 0)); + + verify(sshClient); + } + + public void testNotRoot() { + Statement command = exec("doFoo"); + NodeMetadata node = new NodeMetadataBuilder().ids("id").state(NodeState.RUNNING).credentials( + new Credentials("tester", "notalot")).adminPassword("rootme").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)); + + 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("./jclouds-script-0 init")).andReturn(new ExecResponse("", "", 0)); + + // kick off as current user + expect(sshClient.exec("./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)); + + sshClient.disconnect(); + replay(sshClient); + + RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilComplete testMe = new RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilComplete( + statusFactory, timeouts, Functions.forMap(ImmutableMap.of(node, sshClient)), + 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", 0)); + + 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 new file mode 100644 index 0000000000..16878c855f --- /dev/null +++ b/compute/src/test/java/org/jclouds/compute/callables/RunScriptOnNodeAsInitScriptUsingSshTest.java @@ -0,0 +1,188 @@ +/** + * 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.callables; + +import static org.easymock.EasyMock.createMock; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.replay; +import static org.easymock.EasyMock.verify; +import static org.jclouds.scriptbuilder.domain.Statements.exec; +import static org.testng.Assert.assertEquals; + +import org.jclouds.compute.domain.ExecResponse; +import org.jclouds.compute.domain.NodeMetadata; +import org.jclouds.compute.domain.NodeMetadataBuilder; +import org.jclouds.compute.domain.NodeState; +import org.jclouds.compute.options.RunScriptOptions; +import org.jclouds.domain.Credentials; +import org.jclouds.scriptbuilder.InitBuilder; +import org.jclouds.scriptbuilder.domain.OsFamily; +import org.jclouds.scriptbuilder.domain.Statement; +import org.jclouds.ssh.SshClient; +import org.testng.annotations.Test; + +import com.google.common.base.Functions; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; + +/** + * @author Adrian Cole + */ +@Test(groups = "unit", singleThreaded = true, testName = "RunScriptOnNodeAsInitScriptUsingSshTest") +public class RunScriptOnNodeAsInitScriptUsingSshTest { + + @Test(expectedExceptions = IllegalStateException.class) + public void testWithoutInitThrowsIllegalStateException() { + Statement command = exec("doFoo"); + NodeMetadata node = new NodeMetadataBuilder().ids("id").state(NodeState.RUNNING).credentials( + new Credentials("tester", "notalot")).build(); + + SshClient sshClient = createMock(SshClient.class); + + replay(sshClient); + + RunScriptOnNodeAsInitScriptUsingSsh testMe = new RunScriptOnNodeAsInitScriptUsingSsh(Functions + .forMap(ImmutableMap.of(node, sshClient)), InitScriptConfigurationForTasks.create() + .appendIncrementingNumberToAnonymousTaskNames(), node, command, new RunScriptOptions()); + + testMe.call(); + } + + public void testDefault() { + Statement command = exec("doFoo"); + NodeMetadata node = new NodeMetadataBuilder().ids("id").state(NodeState.RUNNING).credentials( + new Credentials("tester", "notalot")).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)); + + 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("./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)); + + + sshClient.disconnect(); + replay(sshClient); + + RunScriptOnNodeAsInitScriptUsingSsh testMe = new RunScriptOnNodeAsInitScriptUsingSsh(Functions + .forMap(ImmutableMap.of(node, sshClient)), InitScriptConfigurationForTasks.create() + .appendIncrementingNumberToAnonymousTaskNames(), node, command, new RunScriptOptions()); + + assertEquals(testMe.getInitFile(), "/tmp/init-jclouds-script-0"); + assertEquals(testMe.getNode(), node); + assertEquals(testMe.getStatement(), init); + + testMe.init(); + testMe.call(); + verify(sshClient); + } + + + public void testWithSudoPassword() { + Statement command = exec("doFoo"); + NodeMetadata node = new NodeMetadataBuilder().ids("id").state(NodeState.RUNNING).credentials( + new Credentials("tester", "notalot")).adminPassword("rootme").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)); + + 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("./jclouds-script-0 init")).andReturn(new ExecResponse("", "", 0)); + + // since there's an adminPassword we must pass this in + expect(sshClient.exec("echo 'rootme'|sudo -S ./jclouds-script-0 start")).andReturn(new ExecResponse("", "", 0)); + + + sshClient.disconnect(); + replay(sshClient); + + RunScriptOnNodeAsInitScriptUsingSsh testMe = new RunScriptOnNodeAsInitScriptUsingSsh(Functions + .forMap(ImmutableMap.of(node, sshClient)), InitScriptConfigurationForTasks.create() + .appendIncrementingNumberToAnonymousTaskNames(), node, command, new RunScriptOptions()); + + assertEquals(testMe.getInitFile(), "/tmp/init-jclouds-script-0"); + assertEquals(testMe.getNode(), node); + assertEquals(testMe.getStatement(), init); + + testMe.init(); + testMe.call(); + verify(sshClient); + } + + + + public void testNotRoot() { + Statement command = exec("doFoo"); + NodeMetadata node = new NodeMetadataBuilder().ids("id").state(NodeState.RUNNING).credentials( + new Credentials("tester", "notalot")).adminPassword("rootme").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)); + + 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("./jclouds-script-0 init")).andReturn(new ExecResponse("", "", 0)); + + // kick off as current user + expect(sshClient.exec("./jclouds-script-0 start")).andReturn(new ExecResponse("", "", 0)); + + sshClient.disconnect(); + replay(sshClient); + + RunScriptOnNodeAsInitScriptUsingSsh testMe = new RunScriptOnNodeAsInitScriptUsingSsh(Functions + .forMap(ImmutableMap.of(node, sshClient)), 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(); + testMe.call(); + verify(sshClient); + } +} diff --git a/compute/src/test/java/org/jclouds/compute/callable/RunScriptOnNodeUsingSshTest.java b/compute/src/test/java/org/jclouds/compute/callables/RunScriptOnNodeUsingSshTest.java similarity index 96% rename from compute/src/test/java/org/jclouds/compute/callable/RunScriptOnNodeUsingSshTest.java rename to compute/src/test/java/org/jclouds/compute/callables/RunScriptOnNodeUsingSshTest.java index 976d742482..9f537006be 100644 --- a/compute/src/test/java/org/jclouds/compute/callable/RunScriptOnNodeUsingSshTest.java +++ b/compute/src/test/java/org/jclouds/compute/callables/RunScriptOnNodeUsingSshTest.java @@ -16,10 +16,14 @@ * specific language governing permissions and limitations * under the License. */ -package org.jclouds.compute.callable; +package org.jclouds.compute.callables; + +import static org.easymock.EasyMock.createMock; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.replay; +import static org.jclouds.compute.options.RunScriptOptions.Builder.wrapInInitScript; +import static org.jclouds.scriptbuilder.domain.Statements.exec; -import com.google.common.base.Function; -import org.jclouds.compute.callables.RunScriptOnNodeUsingSsh; import org.jclouds.compute.domain.ExecResponse; import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.domain.Credentials; @@ -29,9 +33,7 @@ import org.jclouds.ssh.SshClient; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; -import static org.easymock.EasyMock.*; -import static org.jclouds.compute.options.RunScriptOptions.Builder.wrapInInitScript; -import static org.jclouds.scriptbuilder.domain.Statements.exec; +import com.google.common.base.Function; /** * @author Adam Lowe diff --git a/compute/src/test/java/org/jclouds/compute/callable/ScriptStillRunningExceptionTest.java b/compute/src/test/java/org/jclouds/compute/callables/ScriptStillRunningExceptionTest.java similarity index 94% rename from compute/src/test/java/org/jclouds/compute/callable/ScriptStillRunningExceptionTest.java rename to compute/src/test/java/org/jclouds/compute/callables/ScriptStillRunningExceptionTest.java index f78579c199..ccf33fd937 100644 --- a/compute/src/test/java/org/jclouds/compute/callable/ScriptStillRunningExceptionTest.java +++ b/compute/src/test/java/org/jclouds/compute/callables/ScriptStillRunningExceptionTest.java @@ -16,13 +16,12 @@ * specific language governing permissions and limitations * under the License. */ -package org.jclouds.compute.callable; +package org.jclouds.compute.callables; import static org.testng.Assert.assertEquals; import java.util.concurrent.TimeUnit; -import org.jclouds.compute.callables.ScriptStillRunningException; import org.jclouds.compute.domain.ExecResponse; import org.testng.annotations.Test; diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/AWSEC2TemplateBuilderLiveTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/AWSEC2TemplateBuilderLiveTest.java index 865e5b170e..9d40d007ac 100644 --- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/AWSEC2TemplateBuilderLiveTest.java +++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/AWSEC2TemplateBuilderLiveTest.java @@ -81,14 +81,14 @@ public class AWSEC2TemplateBuilderLiveTest extends BaseTemplateBuilderLiveTest { } @Test - public void testTemplateBuilderM1SMALLWithDescription() { + public void testTemplateBuilderM1SMALLWithDescriptionPrecise() { Template template = context.getComputeService().templateBuilder().hardwareId(InstanceType.M1_SMALL) - .osVersionMatches("1[10].[10][04]").imageDescriptionMatches("ubuntu-images").osFamily(OsFamily.UBUNTU) + .osVersionMatches("1[012].[10][04]").imageDescriptionMatches("ubuntu-images").osFamily(OsFamily.UBUNTU) .build(); assert (template.getImage().getProviderId().startsWith("ami-")) : template; - assertEquals(template.getImage().getOperatingSystem().getVersion(), "11.10"); + assertEquals(template.getImage().getOperatingSystem().getVersion(), "12.04"); assertEquals(template.getImage().getOperatingSystem().is64Bit(), false); assertEquals(template.getImage().getOperatingSystem().getFamily(), OsFamily.UBUNTU); assertEquals(template.getImage().getUserMetadata().get("rootDeviceType"), "instance-store"); @@ -99,15 +99,15 @@ public class AWSEC2TemplateBuilderLiveTest extends BaseTemplateBuilderLiveTest { } @Test - public void testUbuntuInstanceStoreGoesM1Small() { + public void testUbuntuInstanceStoreGoesM1SmallPrecise() { Template template = context.getComputeService().templateBuilder() .imageMatches(EC2ImagePredicates.rootDeviceType(RootDeviceType.INSTANCE_STORE)) - .osVersionMatches("1[10].[10][04]").imageDescriptionMatches("ubuntu-images").osFamily(OsFamily.UBUNTU) + .osVersionMatches("1[012].[10][04]").imageDescriptionMatches("ubuntu-images").osFamily(OsFamily.UBUNTU) .build(); assert (template.getImage().getProviderId().startsWith("ami-")) : template; - assertEquals(template.getImage().getOperatingSystem().getVersion(), "11.10"); + assertEquals(template.getImage().getOperatingSystem().getVersion(), "12.04"); assertEquals(template.getImage().getOperatingSystem().is64Bit(), false); assertEquals(template.getImage().getOperatingSystem().getFamily(), OsFamily.UBUNTU); assertEquals(template.getImage().getUserMetadata().get("rootDeviceType"), "instance-store"); diff --git a/providers/cloudsigma-zrh/src/main/java/org/jclouds/cloudsigma/compute/config/CloudSigmaZurichComputeServiceContextModule.java b/providers/cloudsigma-zrh/src/main/java/org/jclouds/cloudsigma/compute/config/CloudSigmaZurichComputeServiceContextModule.java index 4d99e00478..c9bf69c8ed 100644 --- a/providers/cloudsigma-zrh/src/main/java/org/jclouds/cloudsigma/compute/config/CloudSigmaZurichComputeServiceContextModule.java +++ b/providers/cloudsigma-zrh/src/main/java/org/jclouds/cloudsigma/compute/config/CloudSigmaZurichComputeServiceContextModule.java @@ -18,10 +18,6 @@ */ package org.jclouds.cloudsigma.compute.config; -import org.jclouds.compute.domain.OsFamily; -import org.jclouds.compute.domain.TemplateBuilder; - -import com.google.inject.Injector; /** * @@ -29,9 +25,4 @@ import com.google.inject.Injector; */ public class CloudSigmaZurichComputeServiceContextModule extends CloudSigmaComputeServiceContextModule { - // 11.04 image doesn't work. - @Override - protected TemplateBuilder provideTemplate(Injector injector, TemplateBuilder template) { - return template.osFamily(OsFamily.UBUNTU).osVersionMatches("10.04").imageNameMatches(".*automated SSH Access.*"); - } } \ No newline at end of file diff --git a/providers/cloudsigma-zrh/src/test/java/org/jclouds/cloudsigma/CloudSigmaZurichClientLiveTest.java b/providers/cloudsigma-zrh/src/test/java/org/jclouds/cloudsigma/CloudSigmaZurichClientLiveTest.java index 92a8d04923..5ccfc93e5a 100644 --- a/providers/cloudsigma-zrh/src/test/java/org/jclouds/cloudsigma/CloudSigmaZurichClientLiveTest.java +++ b/providers/cloudsigma-zrh/src/test/java/org/jclouds/cloudsigma/CloudSigmaZurichClientLiveTest.java @@ -29,6 +29,6 @@ import org.testng.annotations.Test; public class CloudSigmaZurichClientLiveTest extends CloudSigmaClientLiveTest { public CloudSigmaZurichClientLiveTest() { provider = "cloudsigma-zrh"; - bootDrive = "f3c7c665-cd54-4a78-8fd2-7ec2f028cf29"; + bootDrive = "7fad4fe1-daf3-4cb8-a847-082aae4d8506"; } } diff --git a/providers/cloudsigma-zrh/src/test/java/org/jclouds/cloudsigma/compute/CloudSigmaZurichTemplateBuilderLiveTest.java b/providers/cloudsigma-zrh/src/test/java/org/jclouds/cloudsigma/compute/CloudSigmaZurichTemplateBuilderLiveTest.java index 334871f092..162f14631f 100644 --- a/providers/cloudsigma-zrh/src/test/java/org/jclouds/cloudsigma/compute/CloudSigmaZurichTemplateBuilderLiveTest.java +++ b/providers/cloudsigma-zrh/src/test/java/org/jclouds/cloudsigma/compute/CloudSigmaZurichTemplateBuilderLiveTest.java @@ -76,9 +76,9 @@ public class CloudSigmaZurichTemplateBuilderLiveTest extends BaseTemplateBuilder @Override public void testDefaultTemplateBuilder() throws IOException { Template defaultTemplate = context.getComputeService().templateBuilder().build(); - assertEquals(defaultTemplate.getImage().getOperatingSystem().getVersion(), "10.04"); - assertEquals(defaultTemplate.getImage().getOperatingSystem().is64Bit(), false); - assertEquals(defaultTemplate.getImage().getId(), "f3c7c665-cd54-4a78-8fd2-7ec2f028cf29"); + assertEquals(defaultTemplate.getImage().getOperatingSystem().getVersion(), "11.04"); + assertEquals(defaultTemplate.getImage().getOperatingSystem().is64Bit(), true); + assertEquals(defaultTemplate.getImage().getId(), "7fad4fe1-daf3-4cb8-a847-082aae4d8506"); assertEquals(defaultTemplate.getImage().getOperatingSystem().getFamily(), OsFamily.UBUNTU); assertEquals(getCores(defaultTemplate.getHardware()), 1.0d); }