From 093d1ce3933cc51719d32aa9b675effdc16615bd Mon Sep 17 00:00:00 2001 From: "adrian.f.cole" Date: Thu, 31 Dec 2009 03:14:13 +0000 Subject: [PATCH] Issue 29: example of how to create/destroy a lamp server git-svn-id: http://jclouds.googlecode.com/svn/trunk@2556 3d8758e0-26b5-11de-8745-db77d3ebf521 --- aws/demos/createlamp/.project | 24 ++ aws/demos/createlamp/README.txt | 30 +++ aws/demos/createlamp/pom.xml | 83 ++++++ .../aws/ec2/demos/createlamp/MainApp.java | 254 ++++++++++++++++++ aws/demos/pom.xml | 1 + 5 files changed, 392 insertions(+) create mode 100644 aws/demos/createlamp/.project create mode 100755 aws/demos/createlamp/README.txt create mode 100644 aws/demos/createlamp/pom.xml create mode 100755 aws/demos/createlamp/src/main/java/org/jclouds/aws/ec2/demos/createlamp/MainApp.java diff --git a/aws/demos/createlamp/.project b/aws/demos/createlamp/.project new file mode 100644 index 0000000000..f2cff9a0cd --- /dev/null +++ b/aws/demos/createlamp/.project @@ -0,0 +1,24 @@ + + + jclouds-aws-demo-createlamp + jclouds ec2 sample that creates an instance and all you need to access it + + jclouds-aws + jclouds-blobstore + jclouds-core + jclouds-jsch + jclouds-log4j + jclouds-scriptbuilder + resteasy-jaxrs-client + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/aws/demos/createlamp/README.txt b/aws/demos/createlamp/README.txt new file mode 100755 index 0000000000..089273bc7c --- /dev/null +++ b/aws/demos/createlamp/README.txt @@ -0,0 +1,30 @@ +==== + + Copyright (C) 2009 Cloud Conscious, LLC. + + ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF 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. + ==================================================================== +==== +# +# this is a simple example command line client that creates a lamp server and everything you need to do that in ec2 +# 1. execute 'mvn install' to build the sample +# 2. invoke the jar, passing your aws credentials and the name you wish to create or destroy +# ex. +# java -jar target/jclouds-aws-demo-createlamp-jar-with-dependencies.jar accesskey secretkey create adrianalmighty +# java -jar target/jclouds-aws-demo-createlamp-jar-with-dependencies.jar accesskey secretkey destroy adrianalmighty diff --git a/aws/demos/createlamp/pom.xml b/aws/demos/createlamp/pom.xml new file mode 100644 index 0000000000..f9158c486f --- /dev/null +++ b/aws/demos/createlamp/pom.xml @@ -0,0 +1,83 @@ + + + + 4.0.0 + + org.jclouds + jclouds-aws-demos-project + 1.0-SNAPSHOT + + jclouds-aws-demo-createlamp + jclouds ec2 sample that creates an instance and all you need to access it + jclouds ec2 sample that creates an instance and all you need to access it + + + ${project.groupId} + jclouds-scriptbuilder + ${project.version} + + + + ${project.artifactId} + + + + org.apache.maven.plugins + maven-jar-plugin + + + + org.jclouds.aws.ec2.demos.createlamp.MainApp + + + + + + + maven-assembly-plugin + + + jar-with-dependencies + + + + org.jclouds.aws.ec2.demos.createlamp.MainApp + + + + + + make-assembly + package + + single + + + + + + + + + diff --git a/aws/demos/createlamp/src/main/java/org/jclouds/aws/ec2/demos/createlamp/MainApp.java b/aws/demos/createlamp/src/main/java/org/jclouds/aws/ec2/demos/createlamp/MainApp.java new file mode 100755 index 0000000000..ae0b8422ad --- /dev/null +++ b/aws/demos/createlamp/src/main/java/org/jclouds/aws/ec2/demos/createlamp/MainApp.java @@ -0,0 +1,254 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF 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.aws.ec2.demos.createlamp; + +import static org.jclouds.aws.ec2.options.RunInstancesOptions.Builder.asType; +import static org.jclouds.scriptbuilder.domain.Statements.exec; + +import java.net.InetSocketAddress; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import org.jclouds.aws.ec2.EC2AsyncClient; +import org.jclouds.aws.ec2.EC2Client; +import org.jclouds.aws.ec2.EC2ContextFactory; +import org.jclouds.aws.ec2.domain.InstanceState; +import org.jclouds.aws.ec2.domain.InstanceType; +import org.jclouds.aws.ec2.domain.IpProtocol; +import org.jclouds.aws.ec2.domain.KeyPair; +import org.jclouds.aws.ec2.domain.Region; +import org.jclouds.aws.ec2.domain.Reservation; +import org.jclouds.aws.ec2.domain.RunningInstance; +import org.jclouds.aws.ec2.predicates.InstanceStateRunning; +import org.jclouds.encryption.internal.Base64; +import org.jclouds.predicates.RetryablePredicate; +import org.jclouds.predicates.SocketOpen; +import org.jclouds.rest.RestContext; +import org.jclouds.scriptbuilder.ScriptBuilder; +import org.jclouds.scriptbuilder.domain.OsFamily; + +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; +import com.google.common.collect.Sets; + +/** + * This the Main class of an Application that demonstrates the use of the EC2Client by creating a + * small lamp server. + * + * Usage is: java MainApp accesskeyid secretkey command name where command in create destroy + * + * @author Adrian Cole + */ +public class MainApp { + + public static int PARAMETERS = 4; + public static String INVALID_SYNTAX = "Invalid number of parameters. Syntax is: accesskeyid secretkey command name\nwhere command in create destroy"; + + public static void main(String[] args) throws TimeoutException { + + if (args.length < PARAMETERS) + throw new IllegalArgumentException(INVALID_SYNTAX); + + // Args + String accesskeyid = args[0]; + String secretkey = args[1]; + String command = args[2]; + String name = args[3]; + + // Init + RestContext context = EC2ContextFactory.createContext(accesskeyid, + secretkey); + + // Get a synchronous client + EC2Client client = context.getApi(); + + try { + if (command.equals("create")) { + + KeyPair pair = createKeyPair(client, name); + + RunningInstance instance = createSecurityGroupKeyPairAndInstance(client, name); + + System.out.printf("instance %s ready%n", instance.getId()); + System.out.printf("ip address: %s%n", instance.getIpAddress().getHostAddress()); + System.out.printf("dns name: %s%n", instance.getDnsName()); + System.out.printf("login identity:%n%s%n", pair.getKeyMaterial()); + + } else if (command.equals("destroy")) { + destroySecurityGroupKeyPairAndInstance(client, name); + } else { + throw new IllegalArgumentException(INVALID_SYNTAX); + } + } finally { + // Close connecton + context.close(); + System.exit(0); + } + + } + + private static void destroySecurityGroupKeyPairAndInstance(EC2Client client, String name) { + try { + String id = findInstanceByKeyName(client, name).getId(); + System.out.printf("%d: %s terminating instance%n", System.currentTimeMillis(), id); + client.getInstanceServices().terminateInstancesInRegion(Region.DEFAULT, + findInstanceByKeyName(client, name).getId()); + } catch (NoSuchElementException e) { + } catch (Exception e) { + e.printStackTrace(); + } + + try { + System.out.printf("%d: %s deleting keypair%n", System.currentTimeMillis(), name); + client.getKeyPairServices().deleteKeyPairInRegion(Region.DEFAULT, name); + } catch (Exception e) { + e.printStackTrace(); + } + + try { + System.out.printf("%d: %s deleting group%n", System.currentTimeMillis(), name); + client.getSecurityGroupServices().deleteSecurityGroupInRegion(Region.DEFAULT, name); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private static RunningInstance createSecurityGroupKeyPairAndInstance(EC2Client client, + String name) throws TimeoutException { + // create a new security group + createSecurityGroupAndAuthorizePorts(client, name); + + // create a new instance + RunningInstance instance = runInstance(client, name, name); + + // await for the instance to start + return blockUntilInstanceRunning(client, instance); + } + + static void createSecurityGroupAndAuthorizePorts(EC2Client client, String name) { + System.out.printf("%d: creating security group: %s%n", System.currentTimeMillis(), name); + client.getSecurityGroupServices().createSecurityGroupInRegion(Region.DEFAULT, name, name); + for (int port : new int[] { 80, 443, 22 }) { + client.getSecurityGroupServices().authorizeSecurityGroupIngressInRegion(Region.DEFAULT, + name, IpProtocol.TCP, port, port, "0.0.0.0/0"); + } + } + + static KeyPair createKeyPair(EC2Client client, String name) { + System.out.printf("%d: creating keypair: %s%n", System.currentTimeMillis(), name); + return client.getKeyPairServices().createKeyPairInRegion(Region.DEFAULT, name); + } + + static RunningInstance runInstance(EC2Client client, String securityGroupName, String keyPairName) { + + String script = new ScriptBuilder() // lamp install script + .addStatement(exec("runurl run.alestic.com/apt/upgrade")) + .addStatement(exec("runurl run.alestic.com/install/lamp")) + .build(OsFamily.UNIX); + + // userData must be base 64 encoded + String encodedScript = Base64.encodeBytes(script.getBytes()); + + System.out.printf("%d: running instance%n", System.currentTimeMillis()); + Reservation reservation = client.getInstanceServices().runInstancesInRegion(Region.DEFAULT, + 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(keyPairName) // key I created above + .withSecurityGroup(securityGroupName) // group I created above + .withUserData(encodedScript)); // script to run as root + + return Iterables.getOnlyElement(reservation.getRunningInstances()); + + } + + static RunningInstance blockUntilInstanceRunning(EC2Client client, RunningInstance instance) + throws TimeoutException { + // create utilities that wait for the instance to finish + RetryablePredicate runningTester = new RetryablePredicate( + new InstanceStateRunning(client.getInstanceServices()), 180, 5, TimeUnit.SECONDS); + RetryablePredicate socketTester = new RetryablePredicate( + new SocketOpen(), 180, 1, TimeUnit.SECONDS); + + System.out.printf("%d: %s awaiting instance to run %n", System.currentTimeMillis(), instance + .getId()); + if (!runningTester.apply(instance)) + throw new TimeoutException("timeout waiting for instance to run: " + instance.getId()); + + instance = findInstanceById(client, instance.getId()); + + System.out.printf("%d: %s awaiting ssh service to start%n", System.currentTimeMillis(), + instance.getIpAddress()); + if (!socketTester.apply(new InetSocketAddress(instance.getIpAddress(), 22))) + throw new TimeoutException("timeout waiting for ssh to start: " + instance.getIpAddress()); + + System.out.printf("%d: %s ssh service started%n", System.currentTimeMillis(), instance + .getIpAddress()); + + System.out.printf("%d: %s awaiting http service to start%n", System.currentTimeMillis(), + instance.getIpAddress()); + if (!socketTester.apply(new InetSocketAddress(instance.getIpAddress(), 80))) + throw new TimeoutException("timeout waiting for http to start: " + instance.getIpAddress()); + + System.out.printf("%d: %s http service started%n", System.currentTimeMillis(), instance + .getIpAddress()); + return instance; + } + + private static RunningInstance findInstanceById(EC2Client client, String instanceId) { + // search my account for the instance I just created + Set reservations = client.getInstanceServices().describeInstancesInRegion( + Region.DEFAULT, instanceId); // last parameter (ids) narrows the search + + // since we refined by instanceId there should only be one instance + return Iterables.getOnlyElement(Iterables.getOnlyElement(reservations).getRunningInstances()); + } + + private static RunningInstance findInstanceByKeyName(EC2Client client, final String keyName) { + // search my account for the instance I just created + Set reservations = client.getInstanceServices().describeInstancesInRegion( + Region.DEFAULT); + + // extract all the instances from all reservations + Set allInstances = Sets.newHashSet(); + for (Reservation reservation : reservations) { + allInstances.addAll(reservation.getRunningInstances()); + } + + // get the first one that has a keyname matching what I just created + return Iterables.find(allInstances, new Predicate() { + + @Override + public boolean apply(RunningInstance input) { + return input.getKeyName().equals(keyName) + && input.getInstanceState() != InstanceState.TERMINATED; + } + + }); + } +} diff --git a/aws/demos/pom.xml b/aws/demos/pom.xml index 2b650e0c6b..8a1c86eddb 100644 --- a/aws/demos/pom.xml +++ b/aws/demos/pom.xml @@ -39,6 +39,7 @@ + createlamp createandlistbuckets