Skip to content
Permalink
Browse files
[FIXED JENKINS-8946] Option to set Zone as well as region for instanc…
…e creation [FIXED JENKINS-11953] Dynamically local AWS regions
  • Loading branch information
francisu committed Feb 22, 2012
1 parent bec9dbf commit f0e542fa9b2c6e4f112294b92145afd4e04eadd9
Showing 15 changed files with 209 additions and 113 deletions.
@@ -2,17 +2,25 @@

import hudson.Extension;
import hudson.util.FormValidation;
import hudson.util.ListBoxModel;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;
import java.util.Locale;

import javax.servlet.ServletException;

import org.apache.commons.lang.StringUtils;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.StaplerResponse;

import com.amazonaws.services.ec2.AmazonEC2;
import com.amazonaws.services.ec2.model.DescribeRegionsResult;
import com.amazonaws.services.ec2.model.Region;

/**
* The original implementation of {@link EC2Cloud}.
*
@@ -22,28 +30,48 @@ public class AmazonEC2Cloud extends EC2Cloud {
/**
* Represents the region. Can be null for backward compatibility reasons.
*/
private AwsRegion region;
private String region;

// Used when running unit tests
public static boolean testMode;


@DataBoundConstructor
public AmazonEC2Cloud(AwsRegion region, String accessId, String secretKey, String privateKey, String instanceCapStr, List<SlaveTemplate> templates) {
super("ec2-"+region.name(), accessId, secretKey, privateKey, instanceCapStr, templates);
public AmazonEC2Cloud(String accessId, String secretKey, String region, String privateKey, String instanceCapStr, List<SlaveTemplate> templates) {
super("ec2-"+region, accessId, secretKey, privateKey, instanceCapStr, templates);
this.region = region;
}

public AwsRegion getRegion() {
if (region==null)
region = AwsRegion.US_EAST_1; // backward data compatibility with earlier versions
public String getRegion() {
if (region == null)
region = DEFAULT_EC2_HOST; // Backward compatibility
// Handles pre 1.14 region names that used the old AwsRegion enum, note we don't change
// the region here to keep the meta-data compatible in the case of a downgrade (is that right?)
if (region.indexOf('_') > 0)
return region.replace('_', '-').toLowerCase(Locale.ENGLISH);
return region;
}

public static URL getEc2EndpointUrl(String region) {
try {
return new URL("https://" + region + "." + EC2_URL_HOST + "/");
} catch (MalformedURLException e) {
throw new Error(e); // Impossible
}
}

@Override
public URL getEc2EndpointUrl() {
return getRegion().ec2Endpoint;
return getEc2EndpointUrl(getRegion());
}

@Override
public URL getS3EndpointUrl() {
return getRegion().s3Endpoint;
try {
return new URL("https://"+getRegion()+".s3.amazonaws.com/");
} catch (MalformedURLException e) {
throw new Error(e); // Impossible
}
}

@Extension
@@ -53,17 +81,38 @@ public String getDisplayName() {
return "Amazon EC2";
}

public ListBoxModel doFillRegionItems(@QueryParameter String accessId,
@QueryParameter String secretKey) throws IOException,
ServletException {
ListBoxModel model = new ListBoxModel();
if (testMode) {
model.add(DEFAULT_EC2_HOST);
return model;
}

if (!StringUtils.isEmpty(accessId) && !StringUtils.isEmpty(secretKey)) {
AmazonEC2 client = connect(accessId, secretKey, new URL(
"http://ec2.amazonaws.com"));
DescribeRegionsResult regions = client.describeRegions();
List<Region> regionList = regions.getRegions();
for (Region r : regionList) {
model.add(r.getRegionName(), r.getRegionName());
}
}
return model;
}

public FormValidation doTestConnection(
@QueryParameter AwsRegion region,
@QueryParameter String region,
@QueryParameter String accessId,
@QueryParameter String secretKey,
@QueryParameter String privateKey) throws IOException, ServletException {
return super.doTestConnection(region.ec2Endpoint,accessId,secretKey,privateKey);
return super.doTestConnection(getEc2EndpointUrl(region),accessId,secretKey,privateKey);
}

public FormValidation doGenerateKey(
StaplerResponse rsp, @QueryParameter AwsRegion region, @QueryParameter String accessId, @QueryParameter String secretKey) throws IOException, ServletException {
return super.doGenerateKey(rsp,region.ec2Endpoint,accessId,secretKey);
StaplerResponse rsp, @QueryParameter String region, @QueryParameter String accessId, @QueryParameter String secretKey) throws IOException, ServletException {
return super.doGenerateKey(rsp,getEc2EndpointUrl(region),accessId,secretKey);
}
}
}

This file was deleted.

@@ -56,6 +56,9 @@
*/
public abstract class EC2Cloud extends Cloud {

public static final String DEFAULT_EC2_HOST = "us-east-1";
public static final String EC2_URL_HOST = "ec2.amazonaws.com";

private final String accessId;
private final Secret secretKey;
private final EC2PrivateKey privateKey;
@@ -292,23 +295,12 @@ public static AmazonEC2 connect(String accessId, Secret secretKey, URL endpoint)
*/
public static String convertHostName(String ec2HostName) {
if (ec2HostName == null || ec2HostName.length()==0)
ec2HostName = "us-east-1";
ec2HostName = DEFAULT_EC2_HOST;
if (!ec2HostName.contains("."))
ec2HostName = ec2HostName + ".ec2.amazonaws.com";
ec2HostName = ec2HostName + "." + EC2_URL_HOST;
return ec2HostName;
}

/***
* Convert a configured s3 endpoint to a FQDN or ip address
*/
public static String convertS3HostName(String s3HostName) {
if (s3HostName == null || s3HostName.length()==0)
s3HostName = "s3";
if (!s3HostName.contains("."))
s3HostName = s3HostName + ".amazonaws.com";
return s3HostName;
}

/***
* Convert a user entered string into a port number
* "" -> -1 to indicate default based on SSL setting
@@ -391,7 +383,7 @@ protected FormValidation doTestConnection( URL ec2endpoint,
// check if this key exists
EC2PrivateKey pk = new EC2PrivateKey(privateKey);
if(pk.find(ec2)==null)
return FormValidation.error("The private key entered below isn't registered to this EC2 region (fingerprint is "+pk.getFingerprint()+")");
return FormValidation.error("The EC2 key pair private key isn't registered to this EC2 region (fingerprint is "+pk.getFingerprint()+")");
}

return FormValidation.ok(Messages.EC2Cloud_Success());
@@ -1,13 +1,19 @@
package hudson.plugins.ec2;

import hudson.Extension;
import hudson.Util;
import hudson.model.Slave.SlaveDescriptor;
import hudson.slaves.SlaveComputer;
import hudson.util.ListBoxModel;

import java.io.IOException;
import java.util.Collections;

import javax.servlet.ServletException;

import org.kohsuke.stapler.HttpRedirect;
import org.kohsuke.stapler.HttpResponse;
import org.kohsuke.stapler.QueryParameter;

import com.amazonaws.AmazonClientException;
import com.amazonaws.services.ec2.AmazonEC2;
@@ -123,4 +129,5 @@ public int getSshPort() {
public String getRootCommandPrefix() {
return getNode().getRootCommandPrefix();
}

}
@@ -8,17 +8,24 @@
import hudson.model.Slave;
import hudson.plugins.ec2.ssh.EC2UnixLauncher;
import hudson.slaves.NodeProperty;
import hudson.util.ListBoxModel;

import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.servlet.ServletException;

import org.apache.commons.lang.StringUtils;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.QueryParameter;

import com.amazonaws.AmazonClientException;
import com.amazonaws.services.ec2.AmazonEC2;
import com.amazonaws.services.ec2.model.AvailabilityZone;
import com.amazonaws.services.ec2.model.DescribeAvailabilityZonesResult;
import com.amazonaws.services.ec2.model.InstanceType;
import com.amazonaws.services.ec2.model.TerminateInstancesRequest;

@@ -31,6 +38,7 @@ public final class EC2Slave extends Slave {
/**
* Comes from {@link SlaveTemplate#initScript}.
*/
public final String zone;
public final String initScript;
public final String remoteAdmin; // e.g. 'ubuntu'
public final String rootCommandPrefix; // e.g. 'sudo'
@@ -41,13 +49,16 @@ public final class EC2Slave extends Slave {
*/
private final int sshPort;

public EC2Slave(String instanceId, String description, String remoteFS, int sshPort, int numExecutors, String labelString, String initScript, String remoteAdmin, String rootCommandPrefix, String jvmopts) throws FormException, IOException {
this(instanceId, description, remoteFS, sshPort, numExecutors, Mode.NORMAL, labelString, initScript, Collections.<NodeProperty<?>>emptyList(), remoteAdmin, rootCommandPrefix, jvmopts);
public static final String TEST_ZONE = "testZone";

public EC2Slave(String instanceId, String description, String zone, String remoteFS, int sshPort, int numExecutors, String labelString, String initScript, String remoteAdmin, String rootCommandPrefix, String jvmopts) throws FormException, IOException {
this(instanceId, description, zone, remoteFS, sshPort, numExecutors, Mode.NORMAL, labelString, initScript, Collections.<NodeProperty<?>>emptyList(), remoteAdmin, rootCommandPrefix, jvmopts);
}

@DataBoundConstructor
public EC2Slave(String instanceId, String description, String remoteFS, int sshPort, int numExecutors, Mode mode, String labelString, String initScript, List<? extends NodeProperty<?>> nodeProperties, String remoteAdmin, String rootCommandPrefix, String jvmopts) throws FormException, IOException {
public EC2Slave(String instanceId, String description, String zone, String remoteFS, int sshPort, int numExecutors, Mode mode, String labelString, String initScript, List<? extends NodeProperty<?>> nodeProperties, String remoteAdmin, String rootCommandPrefix, String jvmopts) throws FormException, IOException {
super(instanceId, description, remoteFS, numExecutors, mode, labelString, new EC2UnixLauncher(), new EC2RetentionStrategy(), nodeProperties);
this.zone = zone;
this.initScript = initScript;
this.remoteAdmin = remoteAdmin;
this.rootCommandPrefix = rootCommandPrefix;
@@ -59,7 +70,7 @@ public EC2Slave(String instanceId, String description, String remoteFS, int sshP
* Constructor for debugging.
*/
public EC2Slave(String instanceId) throws FormException, IOException {
this(instanceId,"debug","/tmp/hudson", 22, 1, Mode.NORMAL, "debug", "", Collections.<NodeProperty<?>>emptyList(), null, null, null);
this(instanceId,"debug","zone", "/tmp/hudson", 22, 1, Mode.NORMAL, "debug", "", Collections.<NodeProperty<?>>emptyList(), null, null, null);
}

/**
@@ -111,6 +122,10 @@ public void terminate() {
}
}

String getZone() {
return zone;
}

String getRemoteAdmin() {
if (remoteAdmin == null || remoteAdmin.length() == 0)
return "root";
@@ -131,6 +146,29 @@ public int getSshPort() {
return sshPort!=0 ? sshPort : 22;
}

public static ListBoxModel fillZoneItems(String accessId,
String secretKey, String region) throws IOException,
ServletException {
ListBoxModel model = new ListBoxModel();
if (AmazonEC2Cloud.testMode) {
model.add(TEST_ZONE);
return model;
}

if (!StringUtils.isEmpty(accessId) && !StringUtils.isEmpty(secretKey) && !StringUtils.isEmpty(region)) {
AmazonEC2 client = AmazonEC2Cloud.connect(accessId, secretKey, AmazonEC2Cloud.getEc2EndpointUrl(region));
DescribeAvailabilityZonesResult zones = client.describeAvailabilityZones();
List<AvailabilityZone> zoneList = zones.getAvailabilityZones();
model.add("<not specified>", "");
for (AvailabilityZone z : zoneList) {
model.add(z.getZoneName(), z.getZoneName());
}
}
return model;
}



@Extension
public static final class DescriptorImpl extends SlaveDescriptor {
@Override
@@ -142,6 +180,12 @@ public String getDisplayName() {
public boolean isInstantiable() {
return false;
}

public ListBoxModel doFillZoneItems(@QueryParameter String accessId,
@QueryParameter String secretKey, @QueryParameter String region) throws IOException,
ServletException {
return fillZoneItems(accessId, secretKey, region);
}
}

private static final Logger LOGGER = Logger.getLogger(EC2Slave.class.getName());
@@ -17,9 +17,6 @@ public void start() throws Exception {
Hudson.XSTREAM.alias("hudson.plugins.ec2.EC2Cloud",AmazonEC2Cloud.class);
// backward compatibility with the legacy instance type
Hudson.XSTREAM.registerConverter(new InstanceTypeConverter());

// make sure the converter gets registered by forcing static initializer
AwsRegion.AP_NORTHEAST_1.toString();

load();
}

0 comments on commit f0e542f

Please sign in to comment.