| /** |
| * 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.ec2; |
| |
| import static com.google.common.base.Preconditions.checkNotNull; |
| import static org.jclouds.ec2.options.RunInstancesOptions.Builder.asType; |
| import static org.jclouds.scriptbuilder.domain.Statements.exec; |
| import static org.testng.Assert.assertEquals; |
| import static org.testng.Assert.assertFalse; |
| import static org.testng.Assert.assertNotNull; |
| |
| import java.io.IOException; |
| import java.net.UnknownHostException; |
| import java.util.Map; |
| import java.util.Properties; |
| import java.util.Set; |
| import java.util.concurrent.ExecutionException; |
| import java.util.concurrent.TimeUnit; |
| import java.util.concurrent.TimeoutException; |
| |
| import org.jclouds.Constants; |
| import org.jclouds.aws.AWSResponseException; |
| import org.jclouds.compute.domain.ExecResponse; |
| import org.jclouds.domain.LoginCredentials; |
| import org.jclouds.ec2.domain.BlockDevice; |
| import org.jclouds.ec2.domain.Image.EbsBlockDevice; |
| import org.jclouds.ec2.domain.InstanceState; |
| import org.jclouds.ec2.domain.InstanceType; |
| import org.jclouds.ec2.domain.IpProtocol; |
| import org.jclouds.ec2.domain.KeyPair; |
| import org.jclouds.ec2.domain.PublicIpInstanceIdPair; |
| import org.jclouds.ec2.domain.Reservation; |
| import org.jclouds.ec2.domain.RunningInstance; |
| import org.jclouds.ec2.domain.Volume.InstanceInitiatedShutdownBehavior; |
| import org.jclouds.ec2.predicates.InstanceHasIpAddress; |
| import org.jclouds.ec2.predicates.InstanceStateRunning; |
| import org.jclouds.http.HttpResponseException; |
| import org.jclouds.logging.log4j.config.Log4JLoggingModule; |
| import org.jclouds.net.IPSocket; |
| import org.jclouds.predicates.RetryablePredicate; |
| import org.jclouds.predicates.SocketOpen; |
| import org.jclouds.rest.RestContextFactory; |
| import org.jclouds.scriptbuilder.ScriptBuilder; |
| import org.jclouds.scriptbuilder.domain.OsFamily; |
| import org.jclouds.ssh.SshClient; |
| import org.jclouds.ssh.SshException; |
| import org.testng.annotations.AfterTest; |
| import org.testng.annotations.BeforeGroups; |
| import org.testng.annotations.Test; |
| |
| import com.google.common.collect.ImmutableMap; |
| import com.google.common.collect.ImmutableSet; |
| import com.google.common.collect.Iterables; |
| import com.google.common.collect.Maps; |
| import com.google.inject.Injector; |
| import com.google.inject.Module; |
| |
| /** |
| * Follows the book Cloud Application Architectures ISBN: 978-0-596-15636-7 |
| * <p/> |
| * adds in functionality to boot a lamp instance: http://alestic.com/2009/06/ec2-user-data-scripts |
| * <p/> |
| * Generally disabled, as it incurs higher fees. |
| * |
| * @author Adrian Cole |
| */ |
| @Test(groups = "live", enabled = false, sequential = true) |
| public class CloudApplicationArchitecturesEC2ClientLiveTest { |
| |
| private EC2Client client; |
| protected SshClient.Factory sshFactory; |
| private String instancePrefix = System.getProperty("user.name") + ".ec2"; |
| private KeyPair keyPair; |
| private String securityGroupName; |
| private String instanceId; |
| private String address; |
| |
| private RetryablePredicate<IPSocket> socketTester; |
| private RetryablePredicate<RunningInstance> hasIpTester; |
| private RetryablePredicate<RunningInstance> runningTester; |
| |
| protected String provider = "ec2"; |
| protected String identity; |
| protected String credential; |
| protected String endpoint; |
| protected String apiversion; |
| |
| protected void setupCredentials() { |
| identity = checkNotNull(System.getProperty("test." + provider + ".identity"), "test." + provider + ".identity"); |
| credential = checkNotNull(System.getProperty("test." + provider + ".credential"), "test." + provider |
| + ".credential"); |
| endpoint = checkNotNull(System.getProperty("test." + provider + ".endpoint"), "test." + provider + ".endpoint"); |
| apiversion = checkNotNull(System.getProperty("test." + provider + ".apiversion"), "test." + provider |
| + ".apiversion"); |
| } |
| |
| protected Properties setupProperties() { |
| Properties overrides = new Properties(); |
| overrides.setProperty(Constants.PROPERTY_TRUST_ALL_CERTS, "true"); |
| overrides.setProperty(Constants.PROPERTY_RELAX_HOSTNAME, "true"); |
| overrides.setProperty(provider + ".identity", identity); |
| overrides.setProperty(provider + ".credential", credential); |
| overrides.setProperty(provider + ".endpoint", endpoint); |
| overrides.setProperty(provider + ".apiversion", apiversion); |
| return overrides; |
| } |
| |
| @BeforeGroups(groups = { "live" }) |
| public void setupClient() { |
| setupCredentials(); |
| Properties overrides = setupProperties(); |
| Injector injector = new RestContextFactory().createContextBuilder(provider, |
| ImmutableSet.<Module> of(new Log4JLoggingModule()), overrides).buildInjector(); |
| client = injector.getInstance(EC2Client.class); |
| sshFactory = injector.getInstance(SshClient.Factory.class); |
| runningTester = new RetryablePredicate<RunningInstance>(new InstanceStateRunning(client), 180, 5, |
| TimeUnit.SECONDS); |
| hasIpTester = new RetryablePredicate<RunningInstance>(new InstanceHasIpAddress(client), 180, 5, TimeUnit.SECONDS); |
| SocketOpen socketOpen = injector.getInstance(SocketOpen.class); |
| socketTester = new RetryablePredicate<IPSocket>(socketOpen, 180, 1, TimeUnit.SECONDS); |
| } |
| |
| @Test(enabled = false) |
| void testCreateSecurityGroupIngressCidr() throws InterruptedException, ExecutionException, TimeoutException { |
| securityGroupName = instancePrefix + "ingress"; |
| |
| try { |
| client.getSecurityGroupServices().deleteSecurityGroupInRegion(null, securityGroupName); |
| } catch (Exception e) { |
| } |
| |
| client.getSecurityGroupServices().createSecurityGroupInRegion(null, securityGroupName, securityGroupName); |
| for (int port : new int[] { 80, 443, 22 }) { |
| client.getSecurityGroupServices().authorizeSecurityGroupIngressInRegion(null, securityGroupName, |
| IpProtocol.TCP, port, port, "0.0.0.0/0"); |
| } |
| } |
| |
| @Test(enabled = false) |
| void testCreateKeyPair() throws InterruptedException, ExecutionException, TimeoutException { |
| String keyName = instancePrefix + "1"; |
| try { |
| client.getKeyPairServices().deleteKeyPairInRegion(null, keyName); |
| } catch (Exception e) { |
| |
| } |
| client.getKeyPairServices().deleteKeyPairInRegion(null, keyName); |
| |
| keyPair = client.getKeyPairServices().createKeyPairInRegion(null, keyName); |
| assertNotNull(keyPair); |
| assertNotNull(keyPair.getKeyMaterial()); |
| assertNotNull(keyPair.getSha1OfPrivateKey()); |
| assertEquals(keyPair.getKeyName(), keyName); |
| } |
| |
| @Test(enabled = false, dependsOnMethods = { "testCreateKeyPair", "testCreateSecurityGroupIngressCidr" }) |
| public void testCreateRunningInstance() throws Exception { |
| String script = new ScriptBuilder() // lamp install script |
| .addStatement(exec("runurl run.alestic.com/apt/upgrade"))// |
| .addStatement(exec("runurl run.alestic.com/install/lamp"))// |
| .render(OsFamily.UNIX); |
| |
| RunningInstance instance = null; |
| while (instance == null) { |
| try { |
| |
| System.out.printf("%d: running instance%n", System.currentTimeMillis()); |
| Reservation<? extends RunningInstance> reservation = client.getInstanceServices().runInstancesInRegion( |
| null, null, // allow |
| // ec2 |
| // to |
| // chose |
| // an |
| // availability |
| // zone |
| "ami-ccf615a5", // alestic ami allows auto-invoke of |
| // user data scripts |
| 1, // minimum instances |
| 1, // maximum instances |
| asType(InstanceType.M1_SMALL) // smallest instance size |
| .withKeyName(keyPair.getKeyName()) // key I |
| // created |
| // above |
| .withSecurityGroup(securityGroupName) // group I |
| // created |
| // above |
| .withUserData(script.getBytes())); // script to |
| // run as root |
| |
| instance = Iterables.getOnlyElement(reservation); |
| |
| } catch (HttpResponseException htpe) { |
| if (htpe.getResponse().getStatusCode() == 400) |
| continue; |
| throw htpe; |
| } |
| } |
| assertNotNull(instance.getId()); |
| instanceId = instance.getId(); |
| assertEquals(instance.getInstanceState(), InstanceState.PENDING); |
| instance = blockUntilWeCanSshIntoInstance(instance); |
| |
| verifyInstanceProperties(script); |
| tryToChangeStuff(); |
| sshPing(instance); |
| System.out.printf("%d: %s ssh connection made%n", System.currentTimeMillis(), instanceId); |
| |
| } |
| |
| private void verifyInstanceProperties(String script) { |
| assertEquals(script, client.getInstanceServices().getUserDataForInstanceInRegion(null, instanceId)); |
| |
| assertEquals(null, client.getInstanceServices().getRootDeviceNameForInstanceInRegion(null, instanceId)); |
| |
| assert client.getInstanceServices().getRamdiskForInstanceInRegion(null, instanceId).startsWith("ari-"); |
| |
| assertEquals(false, client.getInstanceServices().isApiTerminationDisabledForInstanceInRegion(null, instanceId)); |
| |
| assert client.getInstanceServices().getKernelForInstanceInRegion(null, instanceId).startsWith("aki-"); |
| |
| assertEquals(InstanceType.M1_SMALL, |
| client.getInstanceServices().getInstanceTypeForInstanceInRegion(null, instanceId)); |
| |
| assertEquals(InstanceInitiatedShutdownBehavior.TERMINATE, client.getInstanceServices() |
| .getInstanceInitiatedShutdownBehaviorForInstanceInRegion(null, instanceId)); |
| |
| assertEquals(ImmutableMap.<String, EbsBlockDevice> of(), client.getInstanceServices() |
| .getBlockDeviceMappingForInstanceInRegion(null, instanceId)); |
| } |
| |
| private void setApiTerminationDisabledForInstanceInRegion() { |
| client.getInstanceServices().setApiTerminationDisabledForInstanceInRegion(null, instanceId, true); |
| assertEquals(true, client.getInstanceServices().isApiTerminationDisabledForInstanceInRegion(null, instanceId)); |
| client.getInstanceServices().setApiTerminationDisabledForInstanceInRegion(null, instanceId, false); |
| assertEquals(false, client.getInstanceServices().isApiTerminationDisabledForInstanceInRegion(null, instanceId)); |
| } |
| |
| private void tryToChangeStuff() { |
| setApiTerminationDisabledForInstanceInRegion(); |
| setUserDataForInstanceInRegion(); |
| setRamdiskForInstanceInRegion(); |
| setKernelForInstanceInRegion(); |
| setInstanceTypeForInstanceInRegion(); |
| setInstanceInitiatedShutdownBehaviorForInstanceInRegion(); |
| setBlockDeviceMappingForInstanceInRegion(); |
| } |
| |
| private void setUserDataForInstanceInRegion() { |
| try { |
| client.getInstanceServices().setUserDataForInstanceInRegion(null, instanceId, "test".getBytes()); |
| assert false : "shouldn't be allowed, as instance needs to be stopped"; |
| } catch (AWSResponseException e) { |
| assertEquals("IncorrectInstanceState", e.getError().getCode()); |
| } |
| } |
| |
| private void setRamdiskForInstanceInRegion() { |
| try { |
| String ramdisk = client.getInstanceServices().getRamdiskForInstanceInRegion(null, instanceId); |
| client.getInstanceServices().setRamdiskForInstanceInRegion(null, instanceId, ramdisk); |
| assert false : "shouldn't be allowed, as instance needs to be stopped"; |
| } catch (AWSResponseException e) { |
| assertEquals("IncorrectInstanceState", e.getError().getCode()); |
| } |
| } |
| |
| private void setKernelForInstanceInRegion() { |
| try { |
| String oldKernel = client.getInstanceServices().getKernelForInstanceInRegion(null, instanceId); |
| client.getInstanceServices().setKernelForInstanceInRegion(null, instanceId, oldKernel); |
| assert false : "shouldn't be allowed, as instance needs to be stopped"; |
| } catch (AWSResponseException e) { |
| assertEquals("IncorrectInstanceState", e.getError().getCode()); |
| } |
| } |
| |
| private void setInstanceTypeForInstanceInRegion() { |
| try { |
| client.getInstanceServices().setInstanceTypeForInstanceInRegion(null, instanceId, InstanceType.C1_MEDIUM); |
| assert false : "shouldn't be allowed, as instance needs to be stopped"; |
| } catch (AWSResponseException e) { |
| assertEquals("IncorrectInstanceState", e.getError().getCode()); |
| } |
| } |
| |
| private void setBlockDeviceMappingForInstanceInRegion() { |
| Map<String, BlockDevice> mapping = Maps.newLinkedHashMap(); |
| try { |
| client.getInstanceServices().setBlockDeviceMappingForInstanceInRegion(null, instanceId, mapping); |
| assert false : "shouldn't be allowed, as instance needs to be ebs based-ami"; |
| } catch (AWSResponseException e) { |
| assertEquals("InvalidParameterCombination", e.getError().getCode()); |
| } |
| } |
| |
| private void setInstanceInitiatedShutdownBehaviorForInstanceInRegion() { |
| try { |
| client.getInstanceServices().setInstanceInitiatedShutdownBehaviorForInstanceInRegion(null, instanceId, |
| InstanceInitiatedShutdownBehavior.STOP); |
| assert false : "shouldn't be allowed, as instance needs to be ebs based-ami"; |
| } catch (AWSResponseException e) { |
| assertEquals("UnsupportedInstanceAttribute", e.getError().getCode()); |
| } |
| } |
| |
| @Test(enabled = false, dependsOnMethods = "testCreateRunningInstance") |
| void testReboot() throws InterruptedException, ExecutionException, TimeoutException, IOException { |
| RunningInstance instance = getInstance(instanceId); |
| System.out.printf("%d: %s rebooting instance %n", System.currentTimeMillis(), instanceId); |
| client.getInstanceServices().rebootInstancesInRegion(null, instanceId); |
| Thread.sleep(1000); |
| instance = getInstance(instanceId); |
| blockUntilWeCanSshIntoInstance(instance); |
| SshClient ssh = sshFactory.create(new IPSocket(instance.getIpAddress(), 22), |
| LoginCredentials.builder().user("root").privateKey(keyPair.getKeyMaterial()).build()); |
| try { |
| ssh.connect(); |
| ExecResponse uptime = ssh.exec("uptime"); |
| assert uptime.getOutput().indexOf("0 min") != -1 : "reboot didn't work: " + uptime; |
| } finally { |
| if (ssh != null) |
| ssh.disconnect(); |
| } |
| } |
| |
| @Test(enabled = false, dependsOnMethods = "testReboot") |
| void testElasticIpAddress() throws InterruptedException, ExecutionException, TimeoutException, IOException { |
| address = client.getElasticIPAddressServices().allocateAddressInRegion(null); |
| assertNotNull(address); |
| |
| PublicIpInstanceIdPair compare = Iterables.getLast(client.getElasticIPAddressServices() |
| .describeAddressesInRegion(null, address)); |
| |
| assertEquals(compare.getPublicIp(), address); |
| assert compare.getInstanceId() == null; |
| |
| client.getElasticIPAddressServices().associateAddressInRegion(null, address, instanceId); |
| |
| compare = Iterables.getLast(client.getElasticIPAddressServices().describeAddressesInRegion(null, address)); |
| |
| assertEquals(compare.getPublicIp(), address); |
| assertEquals(compare.getInstanceId(), instanceId); |
| |
| Reservation<? extends RunningInstance> reservation = Iterables.getOnlyElement(client.getInstanceServices() |
| .describeInstancesInRegion(null, instanceId)); |
| |
| assertNotNull(Iterables.getOnlyElement(reservation).getIpAddress()); |
| assertFalse(Iterables.getOnlyElement(reservation).getIpAddress().equals(address)); |
| |
| doCheckKey(address); |
| |
| client.getElasticIPAddressServices().disassociateAddressInRegion(null, address); |
| |
| compare = Iterables.getLast(client.getElasticIPAddressServices().describeAddressesInRegion(null, address)); |
| |
| assertEquals(compare.getPublicIp(), address); |
| assert compare.getInstanceId() == null; |
| |
| reservation = Iterables.getOnlyElement(client.getInstanceServices().describeInstancesInRegion(null, instanceId)); |
| // assert reservation.getRunningInstances().last().getIpAddress() == null; |
| // TODO |
| } |
| |
| private RunningInstance blockUntilWeCanSshIntoInstance(RunningInstance instance) throws UnknownHostException { |
| System.out.printf("%d: %s awaiting instance to run %n", System.currentTimeMillis(), instance.getId()); |
| assert runningTester.apply(instance); |
| |
| instance = getInstance(instance.getId()); |
| |
| System.out |
| .printf("%d: %s awaiting instance to have ip assigned %n", System.currentTimeMillis(), instance.getId()); |
| assert hasIpTester.apply(instance); |
| |
| System.out.printf("%d: %s awaiting ssh service to start%n", System.currentTimeMillis(), instance.getIpAddress()); |
| assert socketTester.apply(new IPSocket(instance.getIpAddress(), 22)); |
| |
| System.out.printf("%d: %s ssh service started%n", System.currentTimeMillis(), instance.getDnsName()); |
| sshPing(instance); |
| System.out.printf("%d: %s ssh connection made%n", System.currentTimeMillis(), instance.getId()); |
| |
| System.out.printf("%d: %s awaiting http service to start%n", System.currentTimeMillis(), instance.getIpAddress()); |
| assert socketTester.apply(new IPSocket(instance.getIpAddress(), 80)); |
| System.out.printf("%d: %s http service started%n", System.currentTimeMillis(), instance.getDnsName()); |
| return instance; |
| } |
| |
| private RunningInstance getInstance(String instanceId) { |
| // search my identity for the instance I just created |
| Set<? extends Reservation<? extends RunningInstance>> reservations = client.getInstanceServices() |
| .describeInstancesInRegion(null, instanceId); // last parameter |
| // (ids) narrows the |
| // search |
| |
| return Iterables.getOnlyElement(Iterables.getOnlyElement(reservations)); |
| } |
| |
| /** |
| * this tests "personality" as the file looked up was sent during instance creation |
| * |
| * @throws UnknownHostException |
| */ |
| private void sshPing(RunningInstance newDetails) throws UnknownHostException { |
| try { |
| doCheckKey(newDetails); |
| } catch (SshException e) {// try twice in case there is a network timeout |
| try { |
| Thread.sleep(10 * 1000); |
| } catch (InterruptedException e1) { |
| } |
| doCheckKey(newDetails); |
| } |
| } |
| |
| private void doCheckKey(RunningInstance newDetails) throws UnknownHostException { |
| doCheckKey(newDetails.getIpAddress()); |
| } |
| |
| private void doCheckKey(String address) { |
| SshClient ssh = sshFactory.create(new IPSocket(address, 22), |
| LoginCredentials.builder().user("root").privateKey(keyPair.getKeyMaterial()).build()); |
| try { |
| ssh.connect(); |
| ExecResponse hello = ssh.exec("echo hello"); |
| assertEquals(hello.getOutput().trim(), "hello"); |
| } finally { |
| if (ssh != null) |
| ssh.disconnect(); |
| } |
| } |
| |
| @AfterTest |
| void cleanup() throws InterruptedException, ExecutionException, TimeoutException { |
| if (address != null) |
| client.getElasticIPAddressServices().releaseAddressInRegion(null, address); |
| if (instanceId != null) |
| client.getInstanceServices().terminateInstancesInRegion(null, instanceId); |
| if (keyPair != null) |
| client.getKeyPairServices().deleteKeyPairInRegion(null, keyPair.getKeyName()); |
| if (securityGroupName != null) |
| client.getSecurityGroupServices().deleteSecurityGroupInRegion(null, securityGroupName); |
| } |
| |
| } |