PageRenderTime 80ms CodeModel.GetById 34ms RepoModel.GetById 0ms app.codeStats 1ms

/compute/src/test/java/org/jclouds/compute/callables/RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilCompleteTest.java

https://github.com/alasdairhodge/jclouds
Java | 287 lines | 191 code | 59 blank | 37 comment | 3 complexity | 0c9ea584488ebe11f51c833ecc7ed7ae MD5 | raw file
  1. /**
  2. * Licensed to jclouds, Inc. (jclouds) under one or more
  3. * contributor license agreements. See the NOTICE file
  4. * distributed with this work for additional information
  5. * regarding copyright ownership. jclouds licenses this file
  6. * to you under the Apache License, Version 2.0 (the
  7. * "License"); you may not use this file except in compliance
  8. * with the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing,
  13. * software distributed under the License is distributed on an
  14. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15. * KIND, either express or implied. See the License for the
  16. * specific language governing permissions and limitations
  17. * under the License.
  18. */
  19. package org.jclouds.compute.callables;
  20. import static org.easymock.EasyMock.createMock;
  21. import static org.easymock.EasyMock.expect;
  22. import static org.easymock.EasyMock.replay;
  23. import static org.easymock.EasyMock.verify;
  24. import static org.jclouds.scriptbuilder.domain.Statements.exec;
  25. import static org.testng.Assert.assertEquals;
  26. import java.util.ArrayList;
  27. import java.util.List;
  28. import org.easymock.IAnswer;
  29. import org.jclouds.Constants;
  30. import org.jclouds.compute.domain.ExecResponse;
  31. import org.jclouds.compute.domain.NodeMetadata;
  32. import org.jclouds.compute.domain.NodeMetadataBuilder;
  33. import org.jclouds.compute.domain.NodeState;
  34. import org.jclouds.compute.options.RunScriptOptions;
  35. import org.jclouds.compute.reference.ComputeServiceConstants;
  36. import org.jclouds.compute.reference.ComputeServiceConstants.Timeouts;
  37. import org.jclouds.concurrent.MoreExecutors;
  38. import org.jclouds.concurrent.config.ExecutorServiceModule;
  39. import org.jclouds.domain.LoginCredentials;
  40. import org.jclouds.predicates.RetryablePredicateTest;
  41. import org.jclouds.scriptbuilder.InitBuilder;
  42. import org.jclouds.scriptbuilder.domain.OsFamily;
  43. import org.jclouds.scriptbuilder.domain.Statement;
  44. import org.jclouds.ssh.SshClient;
  45. import org.testng.annotations.Test;
  46. import com.google.common.base.Functions;
  47. import com.google.common.base.Stopwatch;
  48. import com.google.common.collect.ImmutableMap;
  49. import com.google.common.collect.ImmutableSet;
  50. import com.google.inject.AbstractModule;
  51. import com.google.inject.Guice;
  52. import com.google.inject.assistedinject.FactoryModuleBuilder;
  53. import com.google.inject.name.Names;
  54. /**
  55. * @author Adrian Cole
  56. */
  57. @Test(groups = "unit", singleThreaded = true, testName = "RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilCompleteTest")
  58. public class RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilCompleteTest {
  59. BlockUntilInitScriptStatusIsZeroThenReturnOutput.Factory statusFactory = Guice.createInjector(
  60. new ExecutorServiceModule(MoreExecutors.sameThreadExecutor(), MoreExecutors.sameThreadExecutor()),
  61. new AbstractModule() {
  62. @Override
  63. protected void configure() {
  64. bindConstant().annotatedWith(Names.named(Constants.PROPERTY_USER_THREADS)).to(1);
  65. bindConstant().annotatedWith(Names.named(Constants.PROPERTY_IO_WORKER_THREADS)).to(1);
  66. bindConstant().annotatedWith(Names.named(ComputeServiceConstants.PROPERTY_TIMEOUT_SCRIPT_COMPLETE))
  67. .to(100);
  68. install(new FactoryModuleBuilder()
  69. .build(BlockUntilInitScriptStatusIsZeroThenReturnOutput.Factory.class));
  70. }
  71. }).getInstance(BlockUntilInitScriptStatusIsZeroThenReturnOutput.Factory.class);
  72. // fail faster than normal
  73. Timeouts timeouts = Guice.createInjector(new AbstractModule() {
  74. @Override
  75. protected void configure() {
  76. bindConstant().annotatedWith(Names.named(ComputeServiceConstants.PROPERTY_TIMEOUT_SCRIPT_COMPLETE)).to(100l);
  77. }
  78. }).getInstance(Timeouts.class);
  79. @Test(expectedExceptions = IllegalStateException.class)
  80. public void testWithoutInitThrowsIllegalStateException() {
  81. Statement command = exec("doFoo");
  82. NodeMetadata node = new NodeMetadataBuilder().ids("id").state(NodeState.RUNNING).credentials(
  83. new LoginCredentials("tester", "testpassword!", null, false)).build();
  84. SshClient sshClient = createMock(SshClient.class);
  85. replay(sshClient);
  86. RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilComplete testMe = new RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilComplete(
  87. statusFactory, timeouts, Functions.forMap(ImmutableMap.of(node, sshClient)),
  88. InitScriptConfigurationForTasks.create().appendIncrementingNumberToAnonymousTaskNames(), node, command,
  89. new RunScriptOptions());
  90. testMe.call();
  91. }
  92. public void testDefault() {
  93. runDefaults(null, 1);
  94. }
  95. @Test
  96. public void testRepeatedlyChecksIfInitScriptCompleted() {
  97. final List<Long> callTimes = new ArrayList<Long>();
  98. final int succeedOnAttempt = 3;
  99. final Stopwatch stopwatch = new Stopwatch();
  100. stopwatch.start();
  101. IAnswer<ExecResponse> answerForScriptStatus = new IAnswer<ExecResponse>() {
  102. private int count = 0;
  103. @Override
  104. public ExecResponse answer() throws Throwable {
  105. callTimes.add(stopwatch.elapsedMillis());
  106. String stdout = (++count < succeedOnAttempt) ? "someresult" : "";
  107. return new ExecResponse(stdout, "", 1);
  108. }
  109. };
  110. runDefaults(answerForScriptStatus, succeedOnAttempt);
  111. // Expect checking-status to be called repeatedly, until process had finished
  112. RetryablePredicateTest.assertCallTimes(callTimes, 0, 500, (int)(500+(500*1.5)));
  113. }
  114. /**
  115. * @param answerForScriptStatus Answer to use for `jclouds-script-0 status`, or null for default of succeed immediately
  116. * @param timesForScriptStatus Num times to expect call for `jclouds-script-0 status`; ignored if answer is null
  117. */
  118. private void runDefaults(IAnswer<ExecResponse> answerForScriptStatus, int timesForScriptStatus) {
  119. Statement command = exec("doFoo");
  120. NodeMetadata node = new NodeMetadataBuilder().ids("id").state(NodeState.RUNNING).credentials(
  121. new LoginCredentials("tester", "testpassword!", null, false)).build();
  122. SshClient sshClient = createMock(SshClient.class);
  123. InitBuilder init = new InitBuilder("jclouds-script-0", "/tmp/jclouds-script-0", "/tmp/jclouds-script-0",
  124. ImmutableMap.<String, String> of(), ImmutableSet.of(command));
  125. sshClient.connect();
  126. sshClient.put("/tmp/init-jclouds-script-0", init.render(OsFamily.UNIX));
  127. expect(sshClient.getUsername()).andReturn("tester").atLeastOnce();
  128. expect(sshClient.getHostAddress()).andReturn("somewhere.example.com").atLeastOnce();
  129. // setup script as default user
  130. expect(sshClient.exec("chmod 755 /tmp/init-jclouds-script-0")).andReturn(new ExecResponse("", "", 0));
  131. expect(sshClient.exec("ln -fs /tmp/init-jclouds-script-0 jclouds-script-0")).andReturn(
  132. new ExecResponse("", "", 0));
  133. expect(sshClient.exec("./jclouds-script-0 init")).andReturn(new ExecResponse("", "", 0));
  134. // start script as root via sudo, note that since there's no adminPassword we do a straight
  135. // sudo
  136. expect(sshClient.exec("sudo ./jclouds-script-0 start")).andReturn(new ExecResponse("", "", 0));
  137. // signal the command completed
  138. if (answerForScriptStatus == null) {
  139. expect(sshClient.exec("./jclouds-script-0 status")).andReturn(new ExecResponse("", "", 1)).times(1);
  140. } else {
  141. expect(sshClient.exec("./jclouds-script-0 status")).andAnswer(answerForScriptStatus).times(timesForScriptStatus);
  142. }
  143. expect(sshClient.exec("./jclouds-script-0 tail")).andReturn(new ExecResponse("out", "", 0));
  144. expect(sshClient.exec("./jclouds-script-0 tailerr")).andReturn(new ExecResponse("err", "", 0));
  145. sshClient.disconnect();
  146. replay(sshClient);
  147. RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilComplete testMe = new RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilComplete(
  148. statusFactory, timeouts, Functions.forMap(ImmutableMap.of(node, sshClient)),
  149. InitScriptConfigurationForTasks.create().appendIncrementingNumberToAnonymousTaskNames(), node, command,
  150. new RunScriptOptions());
  151. assertEquals(testMe.getInitFile(), "/tmp/init-jclouds-script-0");
  152. assertEquals(testMe.getNode(), node);
  153. assertEquals(testMe.getStatement(), init);
  154. testMe.init();
  155. assertEquals(testMe.call(), new ExecResponse("out", "err", 0));
  156. verify(sshClient);
  157. }
  158. public void testWithSudoPassword() {
  159. Statement command = exec("doFoo");
  160. NodeMetadata node = new NodeMetadataBuilder().ids("id").state(NodeState.RUNNING).credentials(
  161. new LoginCredentials("tester", "testpassword!", null, true)).build();
  162. SshClient sshClient = createMock(SshClient.class);
  163. InitBuilder init = new InitBuilder("jclouds-script-0", "/tmp/jclouds-script-0", "/tmp/jclouds-script-0",
  164. ImmutableMap.<String, String> of(), ImmutableSet.of(command));
  165. sshClient.connect();
  166. sshClient.put("/tmp/init-jclouds-script-0", init.render(OsFamily.UNIX));
  167. expect(sshClient.getUsername()).andReturn("tester").atLeastOnce();
  168. expect(sshClient.getHostAddress()).andReturn("somewhere.example.com").atLeastOnce();
  169. // setup script as default user
  170. expect(sshClient.exec("chmod 755 /tmp/init-jclouds-script-0")).andReturn(new ExecResponse("", "", 0));
  171. expect(sshClient.exec("ln -fs /tmp/init-jclouds-script-0 jclouds-script-0")).andReturn(
  172. new ExecResponse("", "", 0));
  173. expect(sshClient.exec("./jclouds-script-0 init")).andReturn(new ExecResponse("", "", 0));
  174. // since there's an adminPassword we must pass this in
  175. expect(sshClient.exec("echo 'testpassword!'|sudo -S ./jclouds-script-0 start")).andReturn(new ExecResponse("", "", 0));
  176. // signal the command completed
  177. expect(sshClient.exec("./jclouds-script-0 status")).andReturn(new ExecResponse("", "", 1));
  178. expect(sshClient.exec("./jclouds-script-0 tail")).andReturn(new ExecResponse("out", "", 0));
  179. expect(sshClient.exec("./jclouds-script-0 tailerr")).andReturn(new ExecResponse("err", "", 0));
  180. sshClient.disconnect();
  181. replay(sshClient);
  182. RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilComplete testMe = new RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilComplete(
  183. statusFactory, timeouts, Functions.forMap(ImmutableMap.of(node, sshClient)),
  184. InitScriptConfigurationForTasks.create().appendIncrementingNumberToAnonymousTaskNames(), node, command,
  185. new RunScriptOptions());
  186. assertEquals(testMe.getInitFile(), "/tmp/init-jclouds-script-0");
  187. assertEquals(testMe.getNode(), node);
  188. assertEquals(testMe.getStatement(), init);
  189. testMe.init();
  190. assertEquals(testMe.call(), new ExecResponse("out", "err", 0));
  191. verify(sshClient);
  192. }
  193. public void testNotRoot() {
  194. Statement command = exec("doFoo");
  195. NodeMetadata node = new NodeMetadataBuilder().ids("id").state(NodeState.RUNNING).credentials(
  196. new LoginCredentials("tester", "testpassword!", null, true)).build();
  197. SshClient sshClient = createMock(SshClient.class);
  198. InitBuilder init = new InitBuilder("jclouds-script-0", "/tmp/jclouds-script-0", "/tmp/jclouds-script-0",
  199. ImmutableMap.<String, String> of(), ImmutableSet.of(command));
  200. sshClient.connect();
  201. sshClient.put("/tmp/init-jclouds-script-0", init.render(OsFamily.UNIX));
  202. expect(sshClient.getUsername()).andReturn("tester").atLeastOnce();
  203. expect(sshClient.getHostAddress()).andReturn("somewhere.example.com").atLeastOnce();
  204. // setup script as default user
  205. expect(sshClient.exec("chmod 755 /tmp/init-jclouds-script-0")).andReturn(new ExecResponse("", "", 0));
  206. expect(sshClient.exec("ln -fs /tmp/init-jclouds-script-0 jclouds-script-0")).andReturn(
  207. new ExecResponse("", "", 0));
  208. expect(sshClient.exec("./jclouds-script-0 init")).andReturn(new ExecResponse("", "", 0));
  209. // kick off as current user
  210. expect(sshClient.exec("./jclouds-script-0 start")).andReturn(new ExecResponse("", "", 0));
  211. // signal the command completed
  212. expect(sshClient.exec("./jclouds-script-0 status")).andReturn(new ExecResponse("", "", 1));
  213. expect(sshClient.exec("./jclouds-script-0 tail")).andReturn(new ExecResponse("out", "", 0));
  214. expect(sshClient.exec("./jclouds-script-0 tailerr")).andReturn(new ExecResponse("err", "", 0));
  215. sshClient.disconnect();
  216. replay(sshClient);
  217. RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilComplete testMe = new RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilComplete(
  218. statusFactory, timeouts, Functions.forMap(ImmutableMap.of(node, sshClient)),
  219. InitScriptConfigurationForTasks.create().appendIncrementingNumberToAnonymousTaskNames(), node, command,
  220. new RunScriptOptions().runAsRoot(false));
  221. assertEquals(testMe.getInitFile(), "/tmp/init-jclouds-script-0");
  222. assertEquals(testMe.getNode(), node);
  223. assertEquals(testMe.getStatement(), init);
  224. testMe.init();
  225. assertEquals(testMe.call(), new ExecResponse("out", "err", 0));
  226. verify(sshClient);
  227. }
  228. }