| /** |
| * 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; |
| |
| import static org.easymock.EasyMock.eq; |
| import static org.easymock.EasyMock.expect; |
| import static org.easymock.EasyMock.reportMatcher; |
| import static org.easymock.classextension.EasyMock.createMock; |
| import static org.easymock.classextension.EasyMock.replay; |
| import static org.testng.Assert.assertEquals; |
| |
| import java.io.FileNotFoundException; |
| import java.io.IOException; |
| import java.io.Serializable; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.concurrent.ConcurrentMap; |
| import java.util.concurrent.ExecutionException; |
| import java.util.concurrent.TimeUnit; |
| import java.util.concurrent.TimeoutException; |
| |
| import org.easymock.IArgumentMatcher; |
| import org.jclouds.compute.domain.ExecResponse; |
| import org.jclouds.compute.domain.NodeMetadata; |
| import org.jclouds.crypto.Pems; |
| import org.jclouds.domain.LoginCredentials; |
| import org.jclouds.io.Payload; |
| import org.jclouds.net.IPSocket; |
| import org.jclouds.predicates.RetryablePredicate; |
| import org.jclouds.predicates.SocketOpen; |
| import org.jclouds.rest.AuthorizationException; |
| import org.jclouds.rest.RestContext; |
| import org.jclouds.scriptbuilder.statements.login.AdminAccess; |
| import org.jclouds.scriptbuilder.statements.login.AdminAccess.Configuration; |
| import org.jclouds.ssh.SshClient; |
| import org.jclouds.util.Strings2; |
| import org.testng.annotations.Test; |
| |
| import com.google.common.base.Function; |
| import com.google.common.base.Supplier; |
| import com.google.common.base.Suppliers; |
| import com.google.common.base.Throwables; |
| import com.google.common.collect.ImmutableMap; |
| import com.google.inject.AbstractModule; |
| import com.google.inject.Module; |
| |
| /** |
| * |
| * |
| * @author Adrian Cole |
| */ |
| @Test(groups = "live") |
| public class StubComputeServiceIntegrationTest extends BaseComputeServiceLiveTest { |
| |
| 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"; |
| } |
| |
| @Override |
| public void testCorrectAuthException() throws Exception { |
| } |
| |
| protected void buildSocketTester() { |
| SocketOpen socketOpen = createMock(SocketOpen.class); |
| |
| expect(socketOpen.apply(new IPSocket("144.175.1.1", 22))).andReturn(true).times(5); |
| // restart of jboss |
| expect(socketOpen.apply(new IPSocket("144.175.1.1", 8080))).andReturn(true).times(2); |
| |
| replay(socketOpen); |
| |
| preciseSocketTester = socketTester = new RetryablePredicate<IPSocket>(socketOpen, 1, 1, TimeUnit.MILLISECONDS); |
| } |
| |
| @Override |
| protected void checkHttpGet(NodeMetadata node) { |
| |
| } |
| |
| @Override |
| protected Module getSshModule() { |
| return new AbstractModule() { |
| |
| @Override |
| protected void configure() { |
| bind(AdminAccess.Configuration.class).toInstance(new Configuration() { |
| |
| @Override |
| public Supplier<String> defaultAdminUsername() { |
| return Suppliers.ofInstance("defaultAdminUsername"); |
| } |
| |
| @Override |
| public Supplier<Map<String, String>> defaultAdminSshKeys() { |
| return Suppliers.<Map<String, String>> ofInstance(ImmutableMap.of("public", "publicKey", "private", |
| Pems.PRIVATE_PKCS1_MARKER)); |
| } |
| |
| @Override |
| public Function<String, String> cryptFunction() { |
| return new Function<String, String>() { |
| |
| @Override |
| public String apply(String input) { |
| return String.format("crypt(%s)", input); |
| } |
| |
| }; |
| } |
| |
| public Supplier<String> passwordGenerator() { |
| return Suppliers.ofInstance("randompassword"); |
| } |
| }); |
| SshClient.Factory factory = createMock(SshClient.Factory.class); |
| SshClient client1 = createMock(SshClient.class); |
| SshClient client1New = createMock(SshClient.class); |
| SshClient client2 = createMock(SshClient.class); |
| SshClient client2New = createMock(SshClient.class); |
| SshClient client2Foo = createMock(SshClient.class); |
| SshClient client3 = createMock(SshClient.class); |
| SshClient client4 = createMock(SshClient.class); |
| SshClient client5 = createMock(SshClient.class); |
| |
| expect( |
| factory.create(new IPSocket("144.175.1.1", 22), |
| LoginCredentials.builder().user("root").password("password1").build())).andReturn(client1); |
| expect( |
| factory.create(new IPSocket("144.175.1.1", 22), |
| LoginCredentials.builder().user("web").privateKey(Pems.PRIVATE_PKCS1_MARKER).build())).andReturn(client1New) |
| .times(10); |
| runScriptAndService(client1, client1New); |
| |
| expect( |
| factory.create(new IPSocket("144.175.1.2", 22), |
| LoginCredentials.builder().user("root").password("password2").build())).andReturn(client2) |
| .times(4); |
| expect( |
| factory.create(new IPSocket("144.175.1.2", 22), |
| LoginCredentials.builder().user("root").password("password2").build())).andReturn(client2New); |
| expect( |
| factory.create(new IPSocket("144.175.1.2", 22), |
| LoginCredentials.builder().user("foo").privateKey(Pems.PRIVATE_PKCS1_MARKER).build())).andReturn(client2Foo); |
| expect( |
| factory.create(new IPSocket("144.175.1.2", 22), |
| LoginCredentials.builder().user("root").password("romeo").build())).andThrow( |
| new AuthorizationException("Auth fail", null)); |
| |
| // run script without backgrounding (via predicate) |
| client2.connect(); |
| expect(client2.exec("hostname\n")).andReturn(new ExecResponse("stub-r\n", "", 0)); |
| client2.disconnect(); |
| |
| // run script without backgrounding (via id) |
| client2.connect(); |
| expect(client2.exec("hostname\n")).andReturn(new ExecResponse("stub-r\n", "", 0)); |
| client2.disconnect(); |
| |
| client2.connect(); |
| try { |
| runScript(client2, "runScriptWithCreds", |
| Strings2.toStringAndClose(StubComputeServiceIntegrationTest.class |
| .getResourceAsStream("/runscript.sh")), 2); |
| } catch (IOException e) { |
| Throwables.propagate(e); |
| } |
| client2.disconnect(); |
| |
| client2New.connect(); |
| try { |
| runScript(client2New, "adminUpdate", Strings2.toStringAndClose(StubComputeServiceIntegrationTest.class |
| .getResourceAsStream("/runscript_adminUpdate.sh")), 2); |
| } catch (IOException e) { |
| Throwables.propagate(e); |
| } |
| client2New.disconnect(); |
| |
| // check id |
| client2Foo.connect(); |
| expect(client2Foo.getUsername()).andReturn("foo").atLeastOnce(); |
| expect(client2Foo.getHostAddress()).andReturn("foo").atLeastOnce(); |
| expect(client2Foo.exec("echo $USER\n")).andReturn(new ExecResponse("foo\n", "", 0)); |
| client2Foo.disconnect(); |
| |
| expect( |
| factory.create(new IPSocket("144.175.1.3", 22), |
| LoginCredentials.builder().user("root").password("password3").build())).andReturn(client3) |
| .times(2); |
| expect( |
| factory.create(new IPSocket("144.175.1.4", 22), |
| LoginCredentials.builder().user("root").password("password4").build())).andReturn(client4) |
| .times(2); |
| expect( |
| factory.create(new IPSocket("144.175.1.5", 22), |
| LoginCredentials.builder().user("root").password("password5").build())).andReturn(client5) |
| .times(2); |
| |
| runScriptAndInstallSsh(client3, "bootstrap", 3); |
| runScriptAndInstallSsh(client4, "bootstrap", 4); |
| runScriptAndInstallSsh(client5, "bootstrap", 5); |
| |
| expect( |
| factory.create(eq(new IPSocket("144.175.1.1", 22)), |
| eq(LoginCredentials.builder().user("defaultAdminUsername").privateKey(Pems.PRIVATE_PKCS1_MARKER).build()))) |
| .andReturn(client1); |
| expect( |
| factory.create(eq(new IPSocket("144.175.1.2", 22)), |
| eq(LoginCredentials.builder().user("defaultAdminUsername").privateKey(Pems.PRIVATE_PKCS1_MARKER).build()))) |
| .andReturn(client2); |
| expect( |
| factory.create(eq(new IPSocket("144.175.1.3", 22)), |
| eq(LoginCredentials.builder().user("defaultAdminUsername").privateKey(Pems.PRIVATE_PKCS1_MARKER).build()))) |
| .andReturn(client3); |
| expect( |
| factory.create(eq(new IPSocket("144.175.1.4", 22)), |
| eq(LoginCredentials.builder().user("defaultAdminUsername").privateKey(Pems.PRIVATE_PKCS1_MARKER).build()))) |
| .andReturn(client4); |
| expect( |
| factory.create(eq(new IPSocket("144.175.1.5", 22)), |
| eq(LoginCredentials.builder().user("defaultAdminUsername").privateKey(Pems.PRIVATE_PKCS1_MARKER).build()))) |
| .andReturn(client5); |
| |
| helloAndJava(client2); |
| helloAndJava(client3); |
| helloAndJava(client4); |
| helloAndJava(client5); |
| |
| replay(factory); |
| replay(client1); |
| replay(client1New); |
| replay(client2); |
| replay(client2New); |
| replay(client2Foo); |
| replay(client3); |
| replay(client4); |
| replay(client5); |
| |
| bind(SshClient.Factory.class).toInstance(factory); |
| } |
| |
| private void runScriptAndService(SshClient client, SshClient clientNew) { |
| client.connect(); |
| |
| try { |
| String scriptName = "configure-jboss"; |
| client.put("/tmp/init-" + scriptName, Strings2.toStringAndClose(StubComputeServiceIntegrationTest.class |
| .getResourceAsStream("/initscript_with_jboss.sh"))); |
| expect(client.exec("chmod 755 /tmp/init-" + scriptName)).andReturn(EXEC_GOOD); |
| 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("/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("/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(); |
| |
| clientNew.connect(); |
| expect(clientNew.exec("ls /usr/local/jboss/bundles/org/jboss/as/osgi/configadmin/main|sed -e 's/.*-//g' -e 's/.jar//g'\n")).andReturn(EXEC_GOOD); |
| clientNew.disconnect(); |
| |
| clientNew.connect(); |
| expect(clientNew.exec("nslookup -query=a -timeout=5 download.jboss.org|grep Address|tail -1|sed 's/.* //g'\n")).andReturn(EXEC_GOOD); |
| clientNew.disconnect(); |
| |
| clientNew.connect(); |
| expect(clientNew.exec("nslookup -query=a -timeout=5 download.oracle.com|grep Address|tail -1|sed 's/.* //g'\n")).andReturn(EXEC_GOOD); |
| clientNew.disconnect(); |
| |
| clientNew.connect(); |
| expect(clientNew.exec("curl -q -s -S -L --connect-timeout 10 --max-time 600 --retry 20 http://checkip.amazonaws.com/\n")).andReturn(EXEC_GOOD); |
| clientNew.disconnect(); |
| |
| clientNew.connect(); |
| expect(clientNew.exec("java -fullversion\n")).andReturn(EXEC_GOOD); |
| clientNew.disconnect(); |
| |
| clientNew.connect(); |
| scriptName = "jboss"; |
| clientNew.put("/tmp/init-" + scriptName, Strings2 |
| .toStringAndClose(StubComputeServiceIntegrationTest.class |
| .getResourceAsStream("/runscript_jboss.sh"))); |
| expect(clientNew.exec("chmod 755 /tmp/init-" + scriptName)).andReturn(EXEC_GOOD); |
| 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("/tmp/init-" + scriptName + " init")).andReturn(EXEC_GOOD); |
| expect(clientNew.exec("/tmp/init-" + scriptName + " start")).andReturn(EXEC_GOOD); |
| clientNew.disconnect(); |
| clientNew.connect(); |
| expect(clientNew.exec("/tmp/init-" + scriptName + " stdout\n")).andReturn(EXEC_GOOD); |
| clientNew.disconnect(); |
| |
| clientNew.connect(); |
| expect(clientNew.exec("/tmp/init-" + scriptName + " stop\n")).andReturn(EXEC_GOOD); |
| clientNew.disconnect(); |
| |
| clientNew.connect(); |
| expect(clientNew.exec("/tmp/init-" + scriptName + " start\n")).andReturn(EXEC_GOOD); |
| clientNew.disconnect(); |
| |
| clientNew.connect(); |
| expect(clientNew.exec("/tmp/init-" + scriptName + " stdout\n")).andReturn(EXEC_GOOD); |
| clientNew.disconnect(); |
| } catch (IOException e) { |
| Throwables.propagate(e); |
| } |
| clientNew.disconnect(); |
| |
| } |
| |
| private void runScriptAndInstallSsh(SshClient client, String scriptName, int nodeId) { |
| client.connect(); |
| |
| try { |
| runScript(client, scriptName, Strings2.toStringAndClose(StubComputeServiceIntegrationTest.class |
| .getResourceAsStream("/initscript_with_java.sh")), nodeId); |
| } catch (IOException e) { |
| Throwables.propagate(e); |
| } |
| |
| client.disconnect(); |
| |
| } |
| |
| private void runScript(SshClient client, String scriptName, String script, int nodeId) { |
| client.put("/tmp/init-" + scriptName, script); |
| expect(client.exec("chmod 755 /tmp/init-" + scriptName)).andReturn(EXEC_GOOD); |
| 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("/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("/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) { |
| client.connect(); |
| |
| expect(client.exec("echo hello")).andReturn(new ExecResponse("hello", "", 0)); |
| expect(client.exec("java -version")).andReturn(new ExecResponse("", "1.7", 0)); |
| |
| client.disconnect(); |
| } |
| |
| }; |
| } |
| |
| @Override |
| protected void setupCredentials() { |
| identity = "stub"; |
| credential = "stub"; |
| } |
| |
| protected void assertNodeZero(Set<? extends NodeMetadata> metadataSet) { |
| // TODO: this fails so we override it. |
| } |
| |
| public static Payload payloadEq(String value) { |
| reportMatcher(new PayloadEquals(value)); |
| return null; |
| } |
| |
| public void testAssignability() throws Exception { |
| @SuppressWarnings("unused") |
| RestContext<ConcurrentMap<String, NodeMetadata>, ConcurrentMap<String, NodeMetadata>> stubContext = new ComputeServiceContextFactory() |
| .createContext(provider, identity, credential).getProviderSpecificContext(); |
| } |
| |
| private static class PayloadEquals implements IArgumentMatcher, Serializable { |
| |
| private static final long serialVersionUID = 583055160049982067L; |
| |
| private final Object expected; |
| |
| public PayloadEquals(Object expected) { |
| this.expected = expected; |
| } |
| |
| public boolean matches(Object actual) { |
| if (this.expected == null) { |
| return actual == null; |
| } |
| try { |
| String real = Strings2.toStringAndClose(((Payload) actual).getInput()); |
| assertEquals(real, expected); |
| return true; |
| } catch (IOException e) { |
| Throwables.propagate(e); |
| return false; |
| } |
| } |
| |
| public void appendTo(StringBuffer buffer) { |
| appendQuoting(buffer); |
| buffer.append(expected); |
| appendQuoting(buffer); |
| } |
| |
| private void appendQuoting(StringBuffer buffer) { |
| if (expected instanceof String) { |
| buffer.append("\""); |
| } else if (expected instanceof Character) { |
| buffer.append("'"); |
| } |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| if (o == null || !this.getClass().equals(o.getClass())) |
| return false; |
| PayloadEquals other = (PayloadEquals) o; |
| return this.expected == null && other.expected == null || this.expected != null |
| && this.expected.equals(other.expected); |
| } |
| |
| @Override |
| public int hashCode() { |
| throw new UnsupportedOperationException("hashCode() is not supported"); |
| } |
| |
| } |
| |
| @Override |
| protected void setupKeyPairForTest() throws FileNotFoundException, IOException { |
| keyPair = ImmutableMap.<String, String> of("public", "ssh-rsa", "private", "-----BEGIN RSA PRIVATE KEY-----"); |
| } |
| |
| // TODO: I have absolutely no idea why I have to redeclare all this cruft. If |
| // I don't, then we |
| // get all sorts of not allowed to depend on errors. |
| @Override |
| public void testImagesCache() throws Exception { |
| super.testImagesCache(); |
| } |
| |
| @Override |
| public void testCompareSizes() throws Exception { |
| super.testCompareSizes(); |
| } |
| |
| @Test(enabled = true, dependsOnMethods = { "testImagesCache" }) |
| public void testAScriptExecutionAfterBootWithBasicTemplate() throws Exception { |
| super.testAScriptExecutionAfterBootWithBasicTemplate(); |
| } |
| |
| @Test(enabled = false) |
| @Override |
| public void weCanCancelTasks(NodeMetadata node) throws InterruptedException, ExecutionException { |
| // not sure how to do multithreading in a mock so that tests can work |
| } |
| |
| @Test(enabled = true, dependsOnMethods = { "testCompareSizes" }) |
| public void testCreateAndRunAService() throws Exception { |
| super.testCreateAndRunAService(); |
| } |
| |
| @Test(enabled = true, dependsOnMethods = "testTemplateMatch") |
| public void testCreateTwoNodesWithRunScript() throws Exception { |
| super.testCreateTwoNodesWithRunScript(); |
| } |
| |
| @Test(enabled = true, dependsOnMethods = "testCreateTwoNodesWithRunScript") |
| public void testCreateAnotherNodeWithANewContextToEnsureSharedMemIsntRequired() throws Exception { |
| super.testCreateAnotherNodeWithANewContextToEnsureSharedMemIsntRequired(); |
| } |
| |
| @Test(enabled = true, dependsOnMethods = "testCreateAnotherNodeWithANewContextToEnsureSharedMemIsntRequired") |
| public void testCredentialsCache() throws Exception { |
| super.testCredentialsCache(); |
| } |
| |
| @Test(enabled = true, dependsOnMethods = "testCreateAnotherNodeWithANewContextToEnsureSharedMemIsntRequired") |
| public void testGet() throws Exception { |
| super.testGet(); |
| } |
| |
| @Test(enabled = true, dependsOnMethods = "testGet") |
| public void testOptionToNotBlock() throws Exception { |
| super.testOptionToNotBlock(); |
| } |
| |
| @Test(enabled = true, dependsOnMethods = "testGet") |
| public void testReboot() throws Exception { |
| super.testReboot(); |
| } |
| |
| @Test(enabled = true, dependsOnMethods = "testReboot") |
| public void testSuspendResume() throws Exception { |
| super.testSuspendResume(); |
| } |
| |
| @Test(enabled = true, dependsOnMethods = { "testImagesCache" }) |
| public void testTemplateMatch() throws Exception { |
| super.testTemplateMatch(); |
| } |
| |
| @Test(enabled = true, dependsOnMethods = "testSuspendResume") |
| public void testGetNodesWithDetails() throws Exception { |
| super.testGetNodesWithDetails(); |
| } |
| |
| @Test(enabled = true, dependsOnMethods = "testSuspendResume") |
| public void testListNodes() throws Exception { |
| super.testListNodes(); |
| } |
| |
| @Test(enabled = true, dependsOnMethods = { "testListNodes", "testGetNodesWithDetails" }) |
| public void testDestroyNodes() { |
| super.testDestroyNodes(); |
| } |
| |
| @Override |
| protected void cleanup() throws InterruptedException, ExecutionException, TimeoutException { |
| super.cleanup(); |
| } |
| |
| } |