Skip to content
Browse files

added fields for Availability Zone and Security Group as well as an A…

…MI validation button and little more help doc
  • Loading branch information...
1 parent bb4a1f6 commit 90802e9d3abbf4c943808bd0ba5ac03823179cec @adphillips committed Apr 19, 2011
View
7 pom.xml
@@ -26,6 +26,13 @@
<type>jar</type>
<scope>compile</scope>
</dependency>
+ <dependency>
+ <groupId>commons-lang</groupId>
+ <artifactId>commons-lang</artifactId>
+ <version>2.6</version>
+ <type>jar</type>
+ <scope>compile</scope>
+ </dependency>
</dependencies>
<developers>
View
50 src/main/java/jenkins/plugins/ec2slave/EC2ImageLaunchWrapper.java
@@ -37,19 +37,28 @@
import java.io.IOException;
import java.io.PrintStream;
import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.List;
import java.util.logging.Logger;
+import org.apache.commons.lang.StringUtils;
+
import com.amazonaws.AmazonClientException;
import com.amazonaws.AmazonServiceException;
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.ec2.AmazonEC2Client;
+import com.amazonaws.services.ec2.model.AvailabilityZone;
+import com.amazonaws.services.ec2.model.DescribeAvailabilityZonesResult;
import com.amazonaws.services.ec2.model.DescribeInstancesRequest;
+import com.amazonaws.services.ec2.model.DescribeSecurityGroupsResult;
import com.amazonaws.services.ec2.model.Instance;
import com.amazonaws.services.ec2.model.InstanceStateName;
+import com.amazonaws.services.ec2.model.Placement;
import com.amazonaws.services.ec2.model.Reservation;
import com.amazonaws.services.ec2.model.RunInstancesRequest;
import com.amazonaws.services.ec2.model.RunInstancesResult;
+import com.amazonaws.services.ec2.model.SecurityGroup;
import com.amazonaws.services.ec2.model.TerminateInstancesRequest;
/**
@@ -73,7 +82,11 @@
private String ami;
private String instanceType;
-
+
+ private String securityGroup;
+
+ private String availabilityZone;
+
private String keypairName;
private int retryIntervalSeconds = 10;
@@ -93,12 +106,14 @@
private transient boolean preLaunchOk = false;
public EC2ImageLaunchWrapper(ComputerConnector computerConnector, String secretKey, String accessKey, String ami,
- String instanceType, String keypairName) {
+ String instanceType, String keypairName, String securityGroup, String availabilityZone) {
this.ami = ami;
this.computerConnector = computerConnector;
// TODO: make a combobox for instance type in the slave config
this.instanceType = instanceType;
this.keypairName = keypairName;
+ this.securityGroup = securityGroup;
+ this.availabilityZone = availabilityZone;
AWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey);
ec2 = new AmazonEC2Client(credentials);
@@ -109,7 +124,18 @@ public EC2ImageLaunchWrapper(ComputerConnector computerConnector, String secretK
//
protected String launchInstanceFromImage() {
RunInstancesRequest req = new RunInstancesRequest().withImageId(ami).withInstanceType(instanceType)
- .withKeyName(keypairName).withSecurityGroups("default").withMinCount(1).withMaxCount(1);
+ .withKeyName(keypairName).withMinCount(1).withMaxCount(1);
+
+ if(!StringUtils.isEmpty(securityGroup)) {
+ req.withSecurityGroups(securityGroup);
+ } else {
+ req.withSecurityGroups("default");
+ }
+
+ if(!StringUtils.isEmpty(availabilityZone)) {
+ req.setPlacement(new Placement(availabilityZone));
+ }
+
RunInstancesResult res = ec2.runInstances(req);
Reservation rvn = res.getReservation();
@@ -136,6 +162,24 @@ public void terminateInstance(PrintStream logger) {
ec2.terminateInstances(new TerminateInstancesRequest().withInstanceIds(curInstanceId));
}
+
+ public List<String> getAvailabilityZones() {
+ DescribeAvailabilityZonesResult res = ec2.describeAvailabilityZones();
+ ArrayList<String> ret = new ArrayList<String>();
+ for(AvailabilityZone z : res.getAvailabilityZones()) {
+ ret.add(z.getZoneName());
+ }
+ return ret;
+ }
+
+ public List<String> getSecurityGroups() {
+ DescribeSecurityGroupsResult res = ec2.describeSecurityGroups();
+ ArrayList<String> ret = new ArrayList<String>();
+ for(SecurityGroup s : res.getSecurityGroups()) {
+ ret.add(s.getGroupName());
+ }
+ return ret;
+ }
//
////
View
74 src/main/java/jenkins/plugins/ec2slave/EC2ImageSlave.java
@@ -34,11 +34,22 @@
import hudson.slaves.ComputerLauncher;
import hudson.slaves.DumbSlave;
import hudson.slaves.RetentionStrategy;
+import hudson.util.FormValidation;
import java.io.IOException;
import java.util.List;
+import java.util.logging.Logger;
import org.kohsuke.stapler.DataBoundConstructor;
+import org.kohsuke.stapler.QueryParameter;
+
+import com.amazonaws.AmazonServiceException;
+import com.amazonaws.auth.AWSCredentials;
+import com.amazonaws.auth.BasicAWSCredentials;
+import com.amazonaws.services.ec2.AmazonEC2Client;
+import com.amazonaws.services.ec2.model.DescribeImagesRequest;
+import com.amazonaws.services.ec2.model.DescribeImagesResult;
+import com.amazonaws.services.ec2.model.Image;
/**
* The {@link EC2ImageSlave} is a slave in the same way {@link DumbSlave} is, i.e.
@@ -56,7 +67,9 @@
public final class EC2ImageSlave extends Slave {
private static final long serialVersionUID = -3392496004371742586L;
- private String instanceType, accessKey, imageId, secretKey, keypairName;
+ private static final Logger LOGGER = Logger.getLogger(EC2ImageSlave.class.getName());
+
+ private String instanceType, accessKey, imageId, secretKey, keypairName, securityGroup, availabilityZone;
private transient EC2ImageLaunchWrapper ec2ImageLaunchWrapper;
@@ -68,9 +81,10 @@
@DataBoundConstructor
public EC2ImageSlave(String secretKey, String accessKey, String imageId, String instanceType, String keypairName,
- String name, String nodeDescription, String remoteFS, String numExecutors, Mode mode, String labelString,
- ComputerConnector computerConnector, RetentionStrategy retentionStrategy,
- List<? extends NodeProperty<?>> nodeProperties) throws FormException, IOException {
+ String securityGroup, String availabilityZone, String name, String nodeDescription, String remoteFS,
+ String numExecutors, Mode mode, String labelString, ComputerConnector computerConnector,
+ RetentionStrategy retentionStrategy, List<? extends NodeProperty<?>> nodeProperties) throws FormException,
+ IOException {
super(name, nodeDescription, remoteFS, numExecutors, mode, labelString,
null /* null launcher because we create it dynamically in getLauncher */, retentionStrategy, nodeProperties);
@@ -95,6 +109,8 @@ public EC2ImageSlave(String secretKey, String accessKey, String imageId, String
this.imageId = imageId;
this.instanceType = instanceType;
this.keypairName = keypairName;
+ this.securityGroup = securityGroup;
+ this.availabilityZone = availabilityZone;
}
@Override
@@ -104,7 +120,7 @@ public ComputerLauncher getLauncher() {
//hostname already set. This implies that the EC2ImageSlave config will be displaying
//Computer *Connector* descriptor stuff rather than *Launcher*
ec2ImageLaunchWrapper = new EC2ImageLaunchWrapper(computerConnector, secretKey, accessKey, imageId, instanceType,
- keypairName);
+ keypairName, securityGroup, availabilityZone);
setLauncher(ec2ImageLaunchWrapper);
@@ -117,15 +133,37 @@ public String getDisplayName() {
return "EC2 Image Slave";
}
- // public FormValidation doCheckImageId(@QueryParameter String value,
- // @QueryParameter("secretKey") String secretKey,
- // @QueryParameter("accessKey") String accessKey,
- // @QueryParameter("instanceType") String instanceType,
- // @QueryParameter("keypairName") String keypairName) {
- // if(ec2ImageLaunchWrapper
- // if(looksOk(value)) return FormValidation.ok();
- // else return FormValidation.error("There's a problem here");
- // }
+ public FormValidation doTestConnection(@QueryParameter String accessKey, @QueryParameter String secretKey) {
+ try {
+ AWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey);
+ AmazonEC2Client ec2 = new AmazonEC2Client(credentials);
+ ec2.describeAvailabilityZones();
+ return FormValidation.ok("Success");
+ } catch (AmazonServiceException e) {
+ LOGGER.warning("Failed to check EC2 credential: " + e.getMessage());
+ return FormValidation.error(e.getMessage());
+ }
+ }
+
+ public FormValidation doValidateAmi(@QueryParameter String accessKey, @QueryParameter String secretKey,
+ final @QueryParameter String imageId) {
+
+ FormValidation val = doTestConnection(accessKey, secretKey);
+ if(val.kind == FormValidation.Kind.ERROR) {
+ return val;
+ }
+
+ AWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey);
+ AmazonEC2Client ec2 = new AmazonEC2Client(credentials);
+ DescribeImagesResult res = ec2.describeImages(new DescribeImagesRequest().withImageIds(imageId));
+
+ if (res.getImages().size() > 0 && res.getImages().get(0).getImageId().equals(imageId)) {
+ Image image = res.getImages().get(0);
+ return FormValidation.ok("manifest: "+image.getImageLocation() +"\ndescription: " + image.getDescription());
+ } else {
+ return FormValidation.error("No such AMI: " + imageId);
+ }
+ }
public static List<Descriptor<ComputerConnector>> getComputerConnectorDescriptors() {
return Hudson.getInstance().<ComputerConnector, Descriptor<ComputerConnector>> getDescriptorList(
@@ -153,6 +191,14 @@ public String getKeypairName() {
return keypairName;
}
+ public String getAvailabilityZone() {
+ return availabilityZone;
+ }
+
+ public String getSecurityGroup() {
+ return securityGroup;
+ }
+
public ComputerConnector getComputerConnector() {
return computerConnector;
}
View
22 src/main/resources/jenkins/plugins/ec2slave/EC2ImageSlave/configure-entries.jelly
@@ -29,30 +29,48 @@ THE SOFTWARE.
<f:section title="${%EC2 Configuration}">
- <f:entry title="${%Access Key}" field="accessKey">
+ <f:entry title="${%Access Key ID}" field="accessKey">
<f:textbox />
</f:entry>
<f:entry title="${%Secret Key}" field="secretKey">
<f:password />
</f:entry>
+ <!-- f:validateButton title="${%Test Connection}" progress="${%Testing...}" method="testConnection" with="secretKey,accessKey" /-->
+
<f:entry title="${%AMI}" field="imageId">
<f:textbox />
</f:entry>
+ <f:validateButton title="${%Check AMI}" progress="${%Checking...}" method="validateAmi" with="secretKey,accessKey,imageId" />
+
<f:entry title="${%Instance Type}" field="instanceType">
<f:textbox />
</f:entry>
<f:entry title="${%Keypair Name}" field="keypairName">
<f:textbox />
</f:entry>
+
+ <f:advanced>
+
+ <f:entry title="${%Security Group}" field="securityGroup">
+ <f:textbox />
+ </f:entry>
+
+ <f:entry title="${%Availability Zone}" field="availabilityZone">
+ <f:textbox />
+ </f:entry>
+
+ </f:advanced>
+
+ <!-- f:validateButton title="${%Check AMI}" progress="${%Checking...}" method="validateAmi" with="secretKey,accessId,region,ami" /-->
</f:section>
<!-- In order to display ComputerConnector choices, we might not be able to simply import dumbSlave's description -->
- <f:section title="${%Slave Process Configuration}">
+ <f:section title="${%Jenkins Slave Configuration}">
<!-- below we include config that looks just like dumbslave's config-entries.jelly
file but with computerConnector in place of computerLauncher
View
6 src/main/resources/jenkins/plugins/ec2slave/EC2ImageSlave/help-accessKey.html
@@ -0,0 +1,6 @@
+<div>
+ Jenkins uses this access key ID and secret access key to interface with Amazon EC2.
+ If you have already signed up on EC2, you can obtain this from
+ <a href="http://aws-portal.amazon.com/gp/aws/developer/account/index.html?action=access-key">here</a>.
+ Otherwise, you need to sign up to Amazon Web Services to get one.
+</div>
View
6 src/main/resources/jenkins/plugins/ec2slave/EC2ImageSlave/help-instanceType.html
@@ -0,0 +1,6 @@
+<div>
+ The type of EC2 image to launch. Possible values are t1.micro, m1.small, m1.large, m1.xlarge,
+ m2.xlarge, m2.2xlarge, m2.4xlarge, c1.medium, c1.xlarge, cc1.4xlarge, or cg1.4xlarge.
+ Note: not all values apply to a given AMI. For a description of image types see
+ <a href="http://aws.amazon.com/ec2/instance-types/">here</a>.
+</div>
View
62 src/test/java/jenkins/plugins/ec2slave/EC2ImageLaunchWrapperTest.java
@@ -26,15 +26,42 @@
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
+import java.util.List;
import java.util.Properties;
+import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
+import com.amazonaws.auth.AWSCredentials;
+import com.amazonaws.auth.BasicAWSCredentials;
+import com.amazonaws.services.ec2.AmazonEC2Client;
+import com.amazonaws.services.ec2.model.AvailabilityZone;
+import com.amazonaws.services.ec2.model.DescribeImagesRequest;
+import com.amazonaws.services.ec2.model.DescribeImagesResult;
+import com.amazonaws.services.ec2.model.Image;
+import com.amazonaws.services.ec2.model.SecurityGroup;
+
public class EC2ImageLaunchWrapperTest {
- private String credsFile = "../../aws-test/src/AwsCredentials.properties";
+ private String credsFile = "../aws-test/src/AwsCredentials.properties";
+ EC2ImageLaunchWrapper launcher;
+ Properties props = new Properties();
+
+ @Before
+ public void setup() throws FileNotFoundException, IOException {
+
+ props.load(new FileReader(credsFile));
+
+ String ami = "ami-xxxx";
+ String instanceType = "t1.micro";
+ String keypairName = "mykeypair";
+ launcher = new EC2ImageLaunchWrapper(null,
+ props.getProperty("secretKey"), props.getProperty("accessKey"),
+ ami, instanceType, keypairName, null, null);
+ }
+
/**
* For obvious reasons this test doesn't work out of the box. It is really only practical for
* developer (not CI) testing. To run it, point to your AwsCredentials.properties and set ami,
@@ -43,19 +70,30 @@
@Ignore
@Test
public void testImageLauncher() throws FileNotFoundException, IOException, InterruptedException {
- Properties props = new Properties();
- props.load(new FileReader(credsFile));
-
- String ami = "ami-xxxx";
- String instanceType = "t1.micro";
- String keypairName = "mykeypair";
-
- EC2ImageLaunchWrapper launcher = new EC2ImageLaunchWrapper(null,
- props.getProperty("secretKey"), props.getProperty("accessKey"),
- ami, instanceType, keypairName);
-
launcher.preLaunch(System.out);
launcher.terminateInstance(System.out);
}
+
+ @Test
+ public void testGetAvailabilityZones() {
+ List<String> azs = launcher.getAvailabilityZones();
+ System.out.println(azs);
+ }
+
+ @Test
+ public void testGetSecurityGroups() {
+ List<String> sec = launcher.getSecurityGroups();
+ System.out.println(sec);
+ }
+
+ @Test
+ public void testCheckAMI() {
+ AWSCredentials credentials = new BasicAWSCredentials(props.getProperty("accessKey"), props.getProperty("secretKey"));
+ AmazonEC2Client ec2 = new AmazonEC2Client(credentials);
+ DescribeImagesResult res = ec2.describeImages(new DescribeImagesRequest().withImageIds("ami-xxxxxx"));
+ System.out.println(res.getImages());
+ Image image = res.getImages().get(0);
+ System.out.println(image.getImageLocation());
+ }
}

0 comments on commit 90802e9

Please sign in to comment.
Something went wrong with that request. Please try again.