PageRenderTime 30ms CodeModel.GetById 17ms app.highlight 11ms RepoModel.GetById 0ms app.codeStats 0ms

/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 */
 19package org.jclouds.compute.callables;
 20
 21import static org.easymock.EasyMock.createMock;
 22import static org.easymock.EasyMock.expect;
 23import static org.easymock.EasyMock.replay;
 24import static org.easymock.EasyMock.verify;
 25import static org.jclouds.scriptbuilder.domain.Statements.exec;
 26import static org.testng.Assert.assertEquals;
 27
 28import java.util.ArrayList;
 29import java.util.List;
 30
 31import org.easymock.IAnswer;
 32import org.jclouds.Constants;
 33import org.jclouds.compute.domain.ExecResponse;
 34import org.jclouds.compute.domain.NodeMetadata;
 35import org.jclouds.compute.domain.NodeMetadataBuilder;
 36import org.jclouds.compute.domain.NodeState;
 37import org.jclouds.compute.options.RunScriptOptions;
 38import org.jclouds.compute.reference.ComputeServiceConstants;
 39import org.jclouds.compute.reference.ComputeServiceConstants.Timeouts;
 40import org.jclouds.concurrent.MoreExecutors;
 41import org.jclouds.concurrent.config.ExecutorServiceModule;
 42import org.jclouds.domain.LoginCredentials;
 43import org.jclouds.predicates.RetryablePredicateTest;
 44import org.jclouds.scriptbuilder.InitBuilder;
 45import org.jclouds.scriptbuilder.domain.OsFamily;
 46import org.jclouds.scriptbuilder.domain.Statement;
 47import org.jclouds.ssh.SshClient;
 48import org.testng.annotations.Test;
 49
 50import com.google.common.base.Functions;
 51import com.google.common.base.Stopwatch;
 52import com.google.common.collect.ImmutableMap;
 53import com.google.common.collect.ImmutableSet;
 54import com.google.inject.AbstractModule;
 55import com.google.inject.Guice;
 56import com.google.inject.assistedinject.FactoryModuleBuilder;
 57import com.google.inject.name.Names;
 58
 59/**
 60 * @author Adrian Cole
 61 */
 62@Test(groups = "unit", singleThreaded = true, testName = "RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilCompleteTest")
 63public class RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilCompleteTest {
 64
 65   BlockUntilInitScriptStatusIsZeroThenReturnOutput.Factory statusFactory = Guice.createInjector(
 66            new ExecutorServiceModule(MoreExecutors.sameThreadExecutor(), MoreExecutors.sameThreadExecutor()),
 67            new AbstractModule() {
 68
 69               @Override
 70               protected void configure() {
 71                  bindConstant().annotatedWith(Names.named(Constants.PROPERTY_USER_THREADS)).to(1);
 72                  bindConstant().annotatedWith(Names.named(Constants.PROPERTY_IO_WORKER_THREADS)).to(1);
 73                  bindConstant().annotatedWith(Names.named(ComputeServiceConstants.PROPERTY_TIMEOUT_SCRIPT_COMPLETE))
 74                           .to(100);
 75                  install(new FactoryModuleBuilder()
 76                           .build(BlockUntilInitScriptStatusIsZeroThenReturnOutput.Factory.class));
 77               }
 78            }).getInstance(BlockUntilInitScriptStatusIsZeroThenReturnOutput.Factory.class);
 79
 80   // fail faster than normal
 81   Timeouts timeouts = Guice.createInjector(new AbstractModule() {
 82
 83      @Override
 84      protected void configure() {
 85         bindConstant().annotatedWith(Names.named(ComputeServiceConstants.PROPERTY_TIMEOUT_SCRIPT_COMPLETE)).to(100l);
 86      }
 87   }).getInstance(Timeouts.class);
 88
 89   @Test(expectedExceptions = IllegalStateException.class)
 90   public void testWithoutInitThrowsIllegalStateException() {
 91      Statement command = exec("doFoo");
 92      NodeMetadata node = new NodeMetadataBuilder().ids("id").state(NodeState.RUNNING).credentials(
 93            new LoginCredentials("tester", "testpassword!", null, false)).build();
 94
 95      SshClient sshClient = createMock(SshClient.class);
 96
 97      replay(sshClient);
 98
 99      RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilComplete testMe = new RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilComplete(
100               statusFactory, timeouts, Functions.forMap(ImmutableMap.of(node, sshClient)),
101               InitScriptConfigurationForTasks.create().appendIncrementingNumberToAnonymousTaskNames(), node, command,
102               new RunScriptOptions());
103
104      testMe.call();
105   }
106
107   public void testDefault() {
108      runDefaults(null, 1);
109   }
110   
111   @Test
112   public void testRepeatedlyChecksIfInitScriptCompleted() {
113      final List<Long> callTimes = new ArrayList<Long>();
114      final int succeedOnAttempt = 3;
115      final Stopwatch stopwatch = new Stopwatch();
116      stopwatch.start();
117      
118      IAnswer<ExecResponse> answerForScriptStatus = new IAnswer<ExecResponse>() {
119         private int count = 0;
120         @Override
121         public ExecResponse answer() throws Throwable {
122            callTimes.add(stopwatch.elapsedMillis());
123            String stdout = (++count < succeedOnAttempt) ? "someresult" : ""; 
124            return new ExecResponse(stdout, "", 1);
125         }
126      };
127
128      runDefaults(answerForScriptStatus, succeedOnAttempt);
129      
130      // Expect checking-status to be called repeatedly, until process had finished
131      RetryablePredicateTest.assertCallTimes(callTimes, 0, 500, (int)(500+(500*1.5)));
132   }
133   
134   /**
135    * @param answerForScriptStatus Answer to use for `jclouds-script-0 status`, or null for default of succeed immediately
136    * @param timesForScriptStatus  Num times to expect call for `jclouds-script-0 status`; ignored if answer is null
137    */
138   private void runDefaults(IAnswer<ExecResponse> answerForScriptStatus, int timesForScriptStatus) {
139      Statement command = exec("doFoo");
140      NodeMetadata node = new NodeMetadataBuilder().ids("id").state(NodeState.RUNNING).credentials(
141            new LoginCredentials("tester", "testpassword!", null, false)).build();
142
143      SshClient sshClient = createMock(SshClient.class);
144
145      InitBuilder init = new InitBuilder("jclouds-script-0", "/tmp/jclouds-script-0", "/tmp/jclouds-script-0",
146               ImmutableMap.<String, String> of(), ImmutableSet.of(command));
147
148      sshClient.connect();
149      sshClient.put("/tmp/init-jclouds-script-0", init.render(OsFamily.UNIX));
150      expect(sshClient.getUsername()).andReturn("tester").atLeastOnce();
151      expect(sshClient.getHostAddress()).andReturn("somewhere.example.com").atLeastOnce();
152
153      // setup script as default user
154      expect(sshClient.exec("chmod 755 /tmp/init-jclouds-script-0")).andReturn(new ExecResponse("", "", 0));
155      expect(sshClient.exec("ln -fs /tmp/init-jclouds-script-0 jclouds-script-0")).andReturn(
156               new ExecResponse("", "", 0));
157      expect(sshClient.exec("./jclouds-script-0 init")).andReturn(new ExecResponse("", "", 0));
158
159      // start script as root via sudo, note that since there's no adminPassword we do a straight
160      // sudo
161      expect(sshClient.exec("sudo ./jclouds-script-0 start")).andReturn(new ExecResponse("", "", 0));
162
163      // signal the command completed
164      if (answerForScriptStatus == null) {
165         expect(sshClient.exec("./jclouds-script-0 status")).andReturn(new ExecResponse("", "", 1)).times(1);
166      } else {
167         expect(sshClient.exec("./jclouds-script-0 status")).andAnswer(answerForScriptStatus).times(timesForScriptStatus);
168      }
169      expect(sshClient.exec("./jclouds-script-0 tail")).andReturn(new ExecResponse("out", "", 0));
170      expect(sshClient.exec("./jclouds-script-0 tailerr")).andReturn(new ExecResponse("err", "", 0));
171
172      sshClient.disconnect();
173      replay(sshClient);
174
175      RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilComplete testMe = new RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilComplete(
176               statusFactory, timeouts, Functions.forMap(ImmutableMap.of(node, sshClient)),
177               InitScriptConfigurationForTasks.create().appendIncrementingNumberToAnonymousTaskNames(), node, command,
178               new RunScriptOptions());
179
180      assertEquals(testMe.getInitFile(), "/tmp/init-jclouds-script-0");
181      assertEquals(testMe.getNode(), node);
182      assertEquals(testMe.getStatement(), init);
183
184      testMe.init();
185      
186      assertEquals(testMe.call(), new ExecResponse("out", "err", 0));
187
188      verify(sshClient);
189   }
190
191   public void testWithSudoPassword() {
192      Statement command = exec("doFoo");
193      NodeMetadata node = new NodeMetadataBuilder().ids("id").state(NodeState.RUNNING).credentials(
194            new LoginCredentials("tester", "testpassword!", null, true)).build();
195
196      SshClient sshClient = createMock(SshClient.class);
197
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
201      sshClient.connect();
202      sshClient.put("/tmp/init-jclouds-script-0", init.render(OsFamily.UNIX));
203      expect(sshClient.getUsername()).andReturn("tester").atLeastOnce();
204      expect(sshClient.getHostAddress()).andReturn("somewhere.example.com").atLeastOnce();
205
206      // setup script as default user
207      expect(sshClient.exec("chmod 755 /tmp/init-jclouds-script-0")).andReturn(new ExecResponse("", "", 0));
208      expect(sshClient.exec("ln -fs /tmp/init-jclouds-script-0 jclouds-script-0")).andReturn(
209               new ExecResponse("", "", 0));
210      expect(sshClient.exec("./jclouds-script-0 init")).andReturn(new ExecResponse("", "", 0));
211
212      // since there's an adminPassword we must pass this in
213      expect(sshClient.exec("echo 'testpassword!'|sudo -S ./jclouds-script-0 start")).andReturn(new ExecResponse("", "", 0));
214
215      // signal the command completed
216      expect(sshClient.exec("./jclouds-script-0 status")).andReturn(new ExecResponse("", "", 1));
217      expect(sshClient.exec("./jclouds-script-0 tail")).andReturn(new ExecResponse("out", "", 0));
218      expect(sshClient.exec("./jclouds-script-0 tailerr")).andReturn(new ExecResponse("err", "", 0));
219
220      sshClient.disconnect();
221      replay(sshClient);
222
223      RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilComplete testMe = new RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilComplete(
224               statusFactory, timeouts, Functions.forMap(ImmutableMap.of(node, sshClient)),
225               InitScriptConfigurationForTasks.create().appendIncrementingNumberToAnonymousTaskNames(), node, command,
226               new RunScriptOptions());
227
228      assertEquals(testMe.getInitFile(), "/tmp/init-jclouds-script-0");
229      assertEquals(testMe.getNode(), node);
230      assertEquals(testMe.getStatement(), init);
231
232      testMe.init();
233      
234      assertEquals(testMe.call(), new ExecResponse("out", "err", 0));
235      
236      verify(sshClient);
237   }
238
239   public void testNotRoot() {
240      Statement command = exec("doFoo");
241      NodeMetadata node = new NodeMetadataBuilder().ids("id").state(NodeState.RUNNING).credentials(
242            new LoginCredentials("tester", "testpassword!", null, true)).build();
243
244      SshClient sshClient = createMock(SshClient.class);
245
246      InitBuilder init = new InitBuilder("jclouds-script-0", "/tmp/jclouds-script-0", "/tmp/jclouds-script-0",
247               ImmutableMap.<String, String> of(), ImmutableSet.of(command));
248
249      sshClient.connect();
250      sshClient.put("/tmp/init-jclouds-script-0", init.render(OsFamily.UNIX));
251      expect(sshClient.getUsername()).andReturn("tester").atLeastOnce();
252      expect(sshClient.getHostAddress()).andReturn("somewhere.example.com").atLeastOnce();
253
254      // setup script as default user
255      expect(sshClient.exec("chmod 755 /tmp/init-jclouds-script-0")).andReturn(new ExecResponse("", "", 0));
256      expect(sshClient.exec("ln -fs /tmp/init-jclouds-script-0 jclouds-script-0")).andReturn(
257               new ExecResponse("", "", 0));
258      expect(sshClient.exec("./jclouds-script-0 init")).andReturn(new ExecResponse("", "", 0));
259
260      // kick off as current user
261      expect(sshClient.exec("./jclouds-script-0 start")).andReturn(new ExecResponse("", "", 0));
262
263      // signal the command completed
264      expect(sshClient.exec("./jclouds-script-0 status")).andReturn(new ExecResponse("", "", 1));
265      expect(sshClient.exec("./jclouds-script-0 tail")).andReturn(new ExecResponse("out", "", 0));
266      expect(sshClient.exec("./jclouds-script-0 tailerr")).andReturn(new ExecResponse("err", "", 0));
267
268      sshClient.disconnect();
269      replay(sshClient);
270
271      RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilComplete testMe = new RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilComplete(
272               statusFactory, timeouts, Functions.forMap(ImmutableMap.of(node, sshClient)),
273               InitScriptConfigurationForTasks.create().appendIncrementingNumberToAnonymousTaskNames(), node, command,
274               new RunScriptOptions().runAsRoot(false));
275
276      assertEquals(testMe.getInitFile(), "/tmp/init-jclouds-script-0");
277      assertEquals(testMe.getNode(), node);
278      assertEquals(testMe.getStatement(), init);
279
280      testMe.init();
281      
282      assertEquals(testMe.call(), new ExecResponse("out", "err", 0));
283
284      verify(sshClient);
285   }
286   
287}