JCLOUDS-450. Adding support for EC2 MaxCount option.

Note - no live test for this, because it's very hard to guarantee a
situation where it would be relevant.
This commit is contained in:
Andrew Bayer 2014-02-04 11:08:02 -08:00
parent 30a29cc73e
commit e1c8c88cf5
13 changed files with 536 additions and 77 deletions

View File

@ -16,6 +16,16 @@
*/
package org.jclouds.ec2.compute.options;
import static com.google.common.base.Objects.equal;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.base.Strings.emptyToNull;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.google.common.base.Objects;
import com.google.common.base.Objects.ToStringHelper;
import com.google.common.collect.ImmutableSet;
@ -32,16 +42,6 @@ import org.jclouds.ec2.domain.BlockDeviceMapping.UnmapDeviceNamed;
import org.jclouds.javax.annotation.Nullable;
import org.jclouds.scriptbuilder.domain.Statement;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static com.google.common.base.Objects.equal;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.base.Strings.emptyToNull;
/**
* Contains options supported in the {@code ComputeService#runNode} operation on
* the "ec2" provider. <h2>
@ -82,6 +82,10 @@ public class EC2TemplateOptions extends TemplateOptions implements Cloneable {
eTo.noKeyPair();
if (getUserData() != null)
eTo.userData(getUserData());
if (getMaxCount() > 0)
eTo.maxCount(getMaxCount());
if (getClientToken() != null)
eTo.clientToken(getClientToken());
}
}
@ -90,6 +94,8 @@ public class EC2TemplateOptions extends TemplateOptions implements Cloneable {
private boolean noKeyPair;
private List<Byte> userData;
private ImmutableSet.Builder<BlockDeviceMapping> blockDeviceMappings = ImmutableSet.builder();
private Integer maxCount;
private String clientToken = null;
@Override
public boolean equals(Object o) {
@ -99,14 +105,17 @@ public class EC2TemplateOptions extends TemplateOptions implements Cloneable {
return false;
EC2TemplateOptions that = EC2TemplateOptions.class.cast(o);
return super.equals(that) && equal(this.groupNames, that.groupNames) && equal(this.keyPair, that.keyPair)
&& equal(this.noKeyPair, that.noKeyPair) && equal(this.userData, that.userData)
&& equal(this.blockDeviceMappings, that.blockDeviceMappings);
&& equal(this.noKeyPair, that.noKeyPair) && equal(this.userData, that.userData)
&& equal(this.blockDeviceMappings, that.blockDeviceMappings)
&& equal(this.maxCount, that.maxCount)
&& equal(this.clientToken, that.clientToken);
}
@Override
public int hashCode() {
return Objects
.hashCode(super.hashCode(), groupNames, keyPair, noKeyPair, userData, userData, blockDeviceMappings);
.hashCode(super.hashCode(), groupNames, keyPair, noKeyPair, userData, userData, blockDeviceMappings,
maxCount, clientToken);
}
@Override
@ -122,6 +131,10 @@ public class EC2TemplateOptions extends TemplateOptions implements Cloneable {
ImmutableSet<BlockDeviceMapping> mappings = blockDeviceMappings.build();
if (mappings.size() != 0)
toString.add("blockDeviceMappings", mappings);
if (maxCount != null && maxCount.intValue() > 0)
toString.add("maxCount", maxCount);
if (clientToken != null)
toString.add("clientToken", clientToken);
return toString;
}
@ -200,6 +213,16 @@ public class EC2TemplateOptions extends TemplateOptions implements Cloneable {
return this;
}
public EC2TemplateOptions maxCount(Integer maxCount) {
this.maxCount = maxCount;
return this;
}
public EC2TemplateOptions clientToken(String clientToken) {
this.clientToken = checkNotNull(clientToken, "clientToken");
return this;
}
public static class Builder extends TemplateOptions.Builder {
/**
* @see EC2TemplateOptions#blockDeviceMappings
@ -404,6 +427,16 @@ public class EC2TemplateOptions extends TemplateOptions implements Cloneable {
EC2TemplateOptions options = new EC2TemplateOptions();
return options.blockOnComplete(value);
}
public static EC2TemplateOptions maxCount(Integer maxCount) {
EC2TemplateOptions options = new EC2TemplateOptions();
return options.maxCount(maxCount);
}
public static EC2TemplateOptions clientToken(String clientToken) {
EC2TemplateOptions options = new EC2TemplateOptions();
return options.clientToken(clientToken);
}
}
// methods that only facilitate returning the correct object type
@ -621,4 +654,20 @@ public class EC2TemplateOptions extends TemplateOptions implements Cloneable {
return blockDeviceMappings.build();
}
/**
* @return the maximum number of instances to create
*/
public int getMaxCount() {
return maxCount != null ? maxCount.intValue() : 0;
}
/**
* See <a href="http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/Run_Instance_Idempotency.html">here</a> for more information.
*
* @return the optional client token string, used for idempotency
*/
public String getClientToken() {
return clientToken;
}
}

View File

@ -22,13 +22,18 @@ import static com.google.common.base.Preconditions.checkState;
import static org.jclouds.ssh.SshKeys.fingerprintPrivateKey;
import static org.jclouds.ssh.SshKeys.sha1PrivateKey;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;
import javax.inject.Named;
import javax.inject.Provider;
import javax.inject.Singleton;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSet.Builder;
import com.google.inject.Inject;
import org.jclouds.compute.domain.Template;
import org.jclouds.compute.functions.GroupNamingConvention;
import org.jclouds.compute.functions.GroupNamingConvention.Factory;
@ -41,13 +46,6 @@ import org.jclouds.ec2.domain.KeyPair;
import org.jclouds.ec2.options.RunInstancesOptions;
import org.jclouds.javax.annotation.Nullable;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSet.Builder;
import com.google.inject.Inject;
/**
*
* @author Adrian Cole
@ -101,6 +99,12 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions {
"BlockDeviceMapping only available on ebs boot");
instanceOptions.withBlockDeviceMappings(blockDeviceMappings);
}
String clientToken = EC2TemplateOptions.class.cast(template.getOptions()).getClientToken();
if (clientToken != null) {
instanceOptions.withClientToken(clientToken);
}
}
return instanceOptions;
}

View File

@ -26,15 +26,24 @@ import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_R
import static org.jclouds.compute.functions.DefaultCredentialsFromImageOrOverridingCredentials.overrideDefaultCredentialsWithOptionsIfPresent;
import static org.jclouds.ec2.compute.util.EC2ComputeUtils.getZoneFromLocationOrNull;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import javax.annotation.Resource;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.util.concurrent.ListenableFuture;
import org.jclouds.aws.util.AWSUtils;
import org.jclouds.compute.config.CustomizationResponse;
import org.jclouds.compute.domain.NodeMetadata;
@ -48,22 +57,12 @@ import org.jclouds.domain.LoginCredentials;
import org.jclouds.ec2.EC2Api;
import org.jclouds.ec2.compute.domain.RegionAndName;
import org.jclouds.ec2.compute.functions.PresentInstances;
import org.jclouds.ec2.compute.options.EC2TemplateOptions;
import org.jclouds.ec2.domain.RunningInstance;
import org.jclouds.ec2.options.RunInstancesOptions;
import org.jclouds.ec2.reference.EC2Constants;
import org.jclouds.logging.Logger;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.util.concurrent.ListenableFuture;
/**
* creates futures that correlate to
*
@ -218,12 +217,23 @@ public class EC2CreateNodesInGroupThenAddToSet implements CreateNodesInGroupThen
return createNodesInRegionAndZone(region, zone, group, count, template, instanceOptions);
}
protected Set<RunningInstance> createNodesInRegionAndZone(String region, String zone, String group, int count,
Template template, RunInstancesOptions instanceOptions) {
protected Set<RunningInstance> createNodesInRegionAndZone(String region, String zone, String group,
int count, Template template,
RunInstancesOptions instanceOptions) {
int countStarted = 0;
int tries = 0;
Set<RunningInstance> started = ImmutableSet.<RunningInstance> of();
int maxCount = EC2TemplateOptions.class.cast(template.getOptions()).getMaxCount();
int countToProvision;
if (maxCount == 0) {
maxCount = count;
countToProvision = 1;
} else {
countToProvision = count;
}
while (countStarted < count && tries++ < count) {
if (logger.isDebugEnabled())
logger.debug(">> running %d instance region(%s) zone(%s) ami(%s) params(%s)", count - countStarted, region,
@ -231,8 +241,8 @@ public class EC2CreateNodesInGroupThenAddToSet implements CreateNodesInGroupThen
started = ImmutableSet.copyOf(concat(
started,
client.getInstanceApi().get().runInstancesInRegion(region, zone, template.getImage().getProviderId(), 1,
count - countStarted, instanceOptions)));
client.getInstanceApi().get().runInstancesInRegion(region, zone, template.getImage().getProviderId(),
countToProvision, maxCount - countStarted, instanceOptions)));
countStarted = size(started);
if (countStarted < count)

View File

@ -145,6 +145,15 @@ public class RunInstancesOptions extends BaseEC2RequestOptions {
return this;
}
/**
* Specifies the optional ClientToken field, which triggers idempotent RunInstances calls.
* See <a href="http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/Run_Instance_Idempotency.html">here</a> for more details.
*/
public RunInstancesOptions withClientToken(String clientToken) {
formParameters.put("ClientToken", checkNotNull(clientToken, "clientToken"));
return this;
}
public static class Builder {
/**
* @see RunInstancesOptions#withKeyName(String)
@ -202,5 +211,13 @@ public class RunInstancesOptions extends BaseEC2RequestOptions {
return options.withBlockDeviceMappings(mappings);
}
/**
* @see RunInstancesOptions#withClientToken(String)
*/
public static RunInstancesOptions withClientToken(String clientToken) {
RunInstancesOptions options = new RunInstancesOptions();
return options.withClientToken(clientToken);
}
}
}

View File

@ -17,23 +17,24 @@
package org.jclouds.ec2.compute;
import static org.jclouds.ec2.compute.options.EC2TemplateOptions.Builder.blockUntilRunning;
import static org.jclouds.ec2.compute.options.EC2TemplateOptions.Builder.maxCount;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import javax.ws.rs.core.MediaType;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMap.Builder;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import org.jclouds.compute.ComputeService;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.ec2.compute.internal.BaseEC2ComputeServiceExpectTest;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMap.Builder;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
/**
* Tests the compute service abstraction of the EC2 api.
*
@ -41,6 +42,74 @@ import com.google.common.collect.Iterables;
*/
@Test(groups = "unit", testName = "EC2ComputeServiceExpectTest")
public class EC2ComputeServiceExpectTest extends BaseEC2ComputeServiceExpectTest {
protected HttpRequest createFirstTagRequest;
protected HttpRequest createSecondTagRequest;
protected HttpRequest createThirdTagRequest;
@BeforeClass
@Override
protected void setupDefaultRequests() {
super.setupDefaultRequests();
createFirstTagRequest =
formSigner.filter(HttpRequest.builder()
.method("POST")
.endpoint("https://ec2.us-east-1.amazonaws.com/")
.addHeader("Host", "ec2.us-east-1.amazonaws.com")
.payload(
payloadFromStringWithContentType(
"Action=CreateTags" +
"&ResourceId.1=i-2ba64342" +
"&Signature=Trp5e5%2BMqeBeBZbLYa9s9gxahQ9nkx6ETfsGl82IV8Y%3D" +
"&SignatureMethod=HmacSHA256" +
"&SignatureVersion=2" +
"&Tag.1.Key=Name" +
"&Tag.1.Value=test-2ba64342" +
"&Timestamp=2012-04-16T15%3A54%3A08.897Z" +
"&Version=2010-08-31" +
"&AWSAccessKeyId=identity",
"application/x-www-form-urlencoded"))
.build());
createSecondTagRequest =
formSigner.filter(HttpRequest.builder()
.method("POST")
.endpoint("https://ec2.us-east-1.amazonaws.com/")
.addHeader("Host", "ec2.us-east-1.amazonaws.com")
.payload(
payloadFromStringWithContentType(
"Action=CreateTags" +
"&ResourceId.1=i-2bc64242" +
"&Signature=Trp5e5%2BMqeBeBZbLYa9s9gxahQ9nkx6ETfsGl82IV8Y%3D" +
"&SignatureMethod=HmacSHA256" +
"&SignatureVersion=2" +
"&Tag.1.Key=Name" +
"&Tag.1.Value=test-2bc64242" +
"&Timestamp=2012-04-16T15%3A54%3A08.897Z" +
"&Version=2010-08-31" +
"&AWSAccessKeyId=identity",
"application/x-www-form-urlencoded"))
.build());
createThirdTagRequest =
formSigner.filter(HttpRequest.builder()
.method("POST")
.endpoint("https://ec2.us-east-1.amazonaws.com/")
.addHeader("Host", "ec2.us-east-1.amazonaws.com")
.payload(
payloadFromStringWithContentType(
"Action=CreateTags" +
"&ResourceId.1=i-2be64332" +
"&Signature=Trp5e5%2BMqeBeBZbLYa9s9gxahQ9nkx6ETfsGl82IV8Y%3D" +
"&SignatureMethod=HmacSHA256" +
"&SignatureVersion=2" +
"&Tag.1.Key=Name" +
"&Tag.1.Value=test-2be64332" +
"&Timestamp=2012-04-16T15%3A54%3A08.897Z" +
"&Version=2010-08-31" +
"&AWSAccessKeyId=identity",
"application/x-www-form-urlencoded"))
.build());
}
public void testCreateNodeWithGeneratedKeyPairAndOverriddenLoginUser() throws Exception {
Builder<HttpRequest, HttpResponse> requestResponseMap = ImmutableMap.<HttpRequest, HttpResponse> builder();
@ -67,6 +136,60 @@ public class EC2ComputeServiceExpectTest extends BaseEC2ComputeServiceExpectTest
assertNotNull(node.getCredentials().getPrivateKey());
}
public void testCreateThreeNodesWithMaxCountThree() throws Exception {
Builder<HttpRequest, HttpResponse> requestResponseMap = ImmutableMap.<HttpRequest, HttpResponse> builder();
requestResponseMap.put(describeRegionsRequest, describeRegionsResponse);
requestResponseMap.put(describeAvailabilityZonesRequest, describeAvailabilityZonesResponse);
requestResponseMap.put(describeImagesRequest, describeImagesResponse);
requestResponseMap.put(createKeyPairRequest, createKeyPairResponse);
requestResponseMap.put(createSecurityGroupRequest, createSecurityGroupResponse);
requestResponseMap.put(describeSecurityGroupRequest, describeSecurityGroupResponse);
requestResponseMap.put(authorizeSecurityGroupIngressRequest22, authorizeSecurityGroupIngressResponse);
requestResponseMap.put(authorizeSecurityGroupIngressRequestGroup, authorizeSecurityGroupIngressResponse);
requestResponseMap.put(runThreeInstancesRequest, runThreeInstancesResponse);
requestResponseMap.put(describeInstanceRequest, describeInstanceResponse);
requestResponseMap.put(describeInstanceThreeIdsRequest, describeInstanceThreeIdsResponse);
requestResponseMap.put(describeImageRequest, describeImagesResponse);
requestResponseMap.put(createFirstTagRequest, createTagsResponse);
requestResponseMap.put(createSecondTagRequest, createTagsResponse);
requestResponseMap.put(createThirdTagRequest, createTagsResponse);
ComputeService apiThatCreatesNode = requestsSendResponses(requestResponseMap.build());
NodeMetadata node = Iterables.getFirst(apiThatCreatesNode.createNodesInGroup("test", 3,
maxCount(3).blockUntilRunning(false).overrideLoginUser("ec2-user")), null);
assertNotNull(node, "Node should exist");
assertEquals(node.getCredentials().getUser(), "ec2-user", "User should be ec2-user");
}
public void testCreateThreeNodesWithMaxCountFourGetThreeNodes() throws Exception {
Builder<HttpRequest, HttpResponse> requestResponseMap = ImmutableMap.<HttpRequest, HttpResponse> builder();
requestResponseMap.put(describeRegionsRequest, describeRegionsResponse);
requestResponseMap.put(describeAvailabilityZonesRequest, describeAvailabilityZonesResponse);
requestResponseMap.put(describeImagesRequest, describeImagesResponse);
requestResponseMap.put(createKeyPairRequest, createKeyPairResponse);
requestResponseMap.put(createSecurityGroupRequest, createSecurityGroupResponse);
requestResponseMap.put(describeSecurityGroupRequest, describeSecurityGroupResponse);
requestResponseMap.put(authorizeSecurityGroupIngressRequest22, authorizeSecurityGroupIngressResponse);
requestResponseMap.put(authorizeSecurityGroupIngressRequestGroup, authorizeSecurityGroupIngressResponse);
requestResponseMap.put(runThreeToFourInstancesRequest, runThreeInstancesResponse);
requestResponseMap.put(describeInstanceRequest, describeInstanceResponse);
requestResponseMap.put(describeInstanceThreeIdsRequest, describeInstanceThreeIdsResponse);
requestResponseMap.put(describeImageRequest, describeImagesResponse);
requestResponseMap.put(createFirstTagRequest, createTagsResponse);
requestResponseMap.put(createSecondTagRequest, createTagsResponse);
requestResponseMap.put(createThirdTagRequest, createTagsResponse);
ComputeService apiThatCreatesNode = requestsSendResponses(requestResponseMap.build());
NodeMetadata node = Iterables.getFirst(apiThatCreatesNode.createNodesInGroup("test", 3,
maxCount(4).blockUntilRunning(false).overrideLoginUser("ec2-user")), null);
assertNotNull(node, "Node should exist");
assertEquals(node.getCredentials().getUser(), "ec2-user", "User should be ec2-user");
}
public void testCreateNodeWithSpecifiedName() throws Exception {
HttpRequest createNamedTagsRequest =
formSigner.filter(HttpRequest.builder()

View File

@ -20,6 +20,7 @@ import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;
import java.security.SecureRandom;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
@ -111,6 +112,8 @@ public class EC2ComputeServiceLiveTest extends BaseComputeServiceLiveTest {
@Test(enabled = true, dependsOnMethods = "testCompareSizes")
public void testExtendedOptionsAndLogin() throws Exception {
final SecureRandom random = new SecureRandom();
SecurityGroupApi securityGroupClient = view.unwrapApi(EC2Api.class)
.getSecurityGroupApi().get();
@ -125,6 +128,7 @@ public class EC2ComputeServiceLiveTest extends BaseComputeServiceLiveTest {
TemplateOptions options = client.templateOptions();
options.as(EC2TemplateOptions.class).securityGroups(group);
options.as(EC2TemplateOptions.class).clientToken(Integer.toHexString(random.nextInt(65536 * 1024)));
String startedId = null;
try {
@ -151,6 +155,9 @@ public class EC2ComputeServiceLiveTest extends BaseComputeServiceLiveTest {
assert first.getCredentials() != null : first;
assert first.getCredentials().identity != null : first;
// Verify that the output of createNodesInGroup is the same.
assertEquals(client.createNodesInGroup(group, 1, options), nodes, "Idempotency failing - got different instances");
startedId = Iterables.getOnlyElement(nodes).getProviderId();
RunningInstance instance = getInstance(instanceClient, startedId);

View File

@ -18,9 +18,8 @@ package org.jclouds.ec2.compute.internal;
import static org.jclouds.location.reference.LocationConstants.PROPERTY_REGIONS;
import java.util.Properties;
import javax.ws.rs.core.MediaType;
import java.util.Properties;
import org.jclouds.compute.ComputeService;
import org.jclouds.compute.ComputeServiceContext;
@ -54,10 +53,15 @@ public abstract class BaseEC2ComputeServiceExpectTest extends BaseEC2ComputeServ
protected HttpRequest authorizeSecurityGroupIngressRequestGroup;
protected HttpRequest runInstancesRequest;
protected HttpResponse runInstancesResponse;
protected HttpRequest runThreeInstancesRequest;
protected HttpRequest runThreeToFourInstancesRequest;
protected HttpResponse runThreeInstancesResponse;
protected HttpRequest describeInstanceRequest;
protected HttpResponse describeInstanceResponse;
protected HttpRequest describeInstanceMultiIdsRequest;
protected HttpResponse describeInstanceMultiIdsResponse;
protected HttpRequest describeInstanceThreeIdsRequest;
protected HttpResponse describeInstanceThreeIdsResponse;
protected HttpRequest describeImageRequest;
protected HttpRequest createTagsRequest;
protected HttpResponse createTagsResponse;
@ -177,14 +181,45 @@ public abstract class BaseEC2ComputeServiceExpectTest extends BaseEC2ComputeServ
HttpResponse.builder().statusCode(200)
.payload(payloadFromResourceWithContentType(
"/new_instance.xml", MediaType.APPLICATION_XML)).build();
describeInstanceRequest =
runThreeInstancesRequest =
formSigner.filter(HttpRequest.builder()
.method("POST")
.endpoint("https://ec2." + region + ".amazonaws.com/")
.addHeader("Host", "ec2." + region + ".amazonaws.com")
.addFormParam("Action", "RunInstances")
.addFormParam("ImageId", "ami-be3adfd7")
.addFormParam("InstanceType", "m1.small")
.addFormParam("KeyName", "jclouds#test#0")
.addFormParam("MaxCount", "3")
.addFormParam("MinCount", "3")
.addFormParam("SecurityGroup.1", "jclouds#test").build());
runThreeToFourInstancesRequest =
formSigner.filter(HttpRequest.builder()
.method("POST")
.endpoint("https://ec2." + region + ".amazonaws.com/")
.addHeader("Host", "ec2." + region + ".amazonaws.com")
.addFormParam("Action", "RunInstances")
.addFormParam("ImageId", "ami-be3adfd7")
.addFormParam("InstanceType", "m1.small")
.addFormParam("KeyName", "jclouds#test#0")
.addFormParam("MaxCount", "4")
.addFormParam("MinCount", "3")
.addFormParam("SecurityGroup.1", "jclouds#test").build());
runThreeInstancesResponse =
HttpResponse.builder().statusCode(200)
.payload(payloadFromResourceWithContentType(
"/run_instances_three.xml", MediaType.APPLICATION_XML)).build();
describeInstanceRequest =
formSigner.filter(HttpRequest.builder()
.method("POST")
.endpoint("https://ec2." + region + ".amazonaws.com/")
.addHeader("Host", "ec2." + region + ".amazonaws.com")
.addFormParam("Action", "DescribeInstances")
.addFormParam("InstanceId.1", "i-2baa5550").build());
.method("POST")
.endpoint("https://ec2." + region + ".amazonaws.com/")
.addHeader("Host", "ec2." + region + ".amazonaws.com")
.addFormParam("Action", "DescribeInstances")
.addFormParam("InstanceId.1", "i-2baa5550").build());
describeInstanceResponse =
HttpResponse.builder().statusCode(200)
@ -205,6 +240,21 @@ public abstract class BaseEC2ComputeServiceExpectTest extends BaseEC2ComputeServ
.payload(payloadFromResourceWithContentType(
"/describe_instances_multiple.xml", MediaType.APPLICATION_XML)).build();
describeInstanceThreeIdsRequest =
formSigner.filter(HttpRequest.builder()
.method("POST")
.endpoint("https://ec2." + region + ".amazonaws.com/")
.addHeader("Host", "ec2." + region + ".amazonaws.com")
.addFormParam("Action", "DescribeInstances")
.addFormParam("InstanceId.1", "i-2ba64342")
.addFormParam("InstanceId.2", "i-2bc64242")
.addFormParam("InstanceId.3", "i-2be64332").build());
describeInstanceThreeIdsResponse =
HttpResponse.builder().statusCode(200)
.payload(payloadFromResourceWithContentType(
"/describe_instances_multiple.xml", MediaType.APPLICATION_XML)).build();
//TODO: duplicate.. shouldn't need this
describeImageRequest =
formSigner.filter(HttpRequest.builder()

View File

@ -24,14 +24,14 @@ import static org.jclouds.ec2.compute.options.EC2TemplateOptions.Builder.keyPair
import static org.jclouds.ec2.compute.options.EC2TemplateOptions.Builder.noKeyPair;
import static org.jclouds.ec2.compute.options.EC2TemplateOptions.Builder.securityGroups;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNull;
import java.io.IOException;
import com.google.common.collect.ImmutableSet;
import org.jclouds.compute.options.TemplateOptions;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableSet;
/**
* Tests possible uses of EC2TemplateOptions and EC2TemplateOptions.Builder.*
*
@ -285,4 +285,43 @@ public class EC2TemplateOptionsTest {
assertEquals(options.getInboundPorts()[0], 22);
assertEquals(options.getInboundPorts()[1], 30);
}
@Test
public void testMaxCountDefault() {
EC2TemplateOptions options = new EC2TemplateOptions();
assertEquals(options.getMaxCount(), 0);
}
@Test
public void testMaxCount() {
EC2TemplateOptions options = new EC2TemplateOptions();
options.maxCount(2);
assertEquals(options.getMaxCount(), 2);
}
@Test
public void testMaxCountNull() {
EC2TemplateOptions options = new EC2TemplateOptions();
options.maxCount(null);
assertEquals(options.getMaxCount(), 0);
}
@Test
public void testClientTokenDefault() {
EC2TemplateOptions options = new EC2TemplateOptions();
assertNull(options.getClientToken());
}
@Test
public void testClientToken() {
EC2TemplateOptions options = new EC2TemplateOptions();
options.clientToken("some-token");
assertEquals(options.getClientToken(), "some-token");
}
@Test(expectedExceptions = NullPointerException.class)
public void testClientTokenNPE() {
EC2TemplateOptions options = new EC2TemplateOptions();
options.clientToken(null);
}
}

View File

@ -23,13 +23,16 @@ import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.verify;
import static org.testng.Assert.assertEquals;
import javax.inject.Provider;
import java.lang.reflect.Method;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException;
import javax.inject.Provider;
import com.google.common.base.Function;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSet;
import org.jclouds.aws.domain.Region;
import org.jclouds.compute.domain.Hardware;
import org.jclouds.compute.domain.Template;
@ -46,11 +49,6 @@ import org.jclouds.ec2.options.RunInstancesOptions;
import org.jclouds.scriptbuilder.domain.Statements;
import org.testng.annotations.Test;
import com.google.common.base.Function;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSet;
/**
* @author Adrian Cole
*/
@ -130,6 +128,7 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest {
systemGeneratedKeyPairName);
expect(strategy.getSecurityGroupsForTagAndOptions(region, group, options)).andReturn(generatedGroups);
expect(options.getUserData()).andReturn(null);
expect(options.getClientToken()).andReturn(null);
// replay mocks
replay(options);
@ -181,6 +180,7 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest {
expect(template.getHardware()).andReturn(size).atLeastOnce();
expect(template.getOptions()).andReturn(options).atLeastOnce();
expect(options.getBlockDeviceMappings()).andReturn(ImmutableSet.<BlockDeviceMapping> of()).atLeastOnce();
expect(options.getClientToken()).andReturn("some-token");
expect(strategy.createNewKeyPairUnlessUserSpecifiedOtherwise(region, group, options)).andReturn(
systemGeneratedKeyPairName);
expect(strategy.getSecurityGroupsForTagAndOptions(region, group, options)).andReturn(generatedGroups);
@ -197,7 +197,8 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest {
assertEquals(
customize.buildFormParameters().entries(),
ImmutableMultimap.<String, String> of("InstanceType", size.getProviderId(), "SecurityGroup.1", "group",
"KeyName", systemGeneratedKeyPairName, "UserData", base64().encode("hello".getBytes())).entries());
"KeyName", systemGeneratedKeyPairName, "UserData", base64().encode("hello".getBytes()),
"ClientToken", "some-token").entries());
assertEquals(customize.buildRequestHeaders(), ImmutableMultimap.<String, String> of());
assertEquals(customize.buildStringPayload(), null);

View File

@ -26,6 +26,11 @@ import static org.easymock.EasyMock.verify;
import java.util.Map;
import java.util.Set;
import com.google.common.base.Optional;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Multimap;
import org.easymock.IArgumentMatcher;
import org.jclouds.compute.config.CustomizationResponse;
import org.jclouds.compute.domain.Hardware;
@ -49,18 +54,12 @@ import org.jclouds.ec2.compute.functions.RunningInstanceToNodeMetadata;
import org.jclouds.ec2.compute.options.EC2TemplateOptions;
import org.jclouds.ec2.domain.Reservation;
import org.jclouds.ec2.domain.RunningInstance;
import org.jclouds.ec2.options.RunInstancesOptions;
import org.jclouds.ec2.features.ElasticIPAddressApi;
import org.jclouds.ec2.features.InstanceApi;
import org.jclouds.ec2.options.RunInstancesOptions;
import org.testng.Assert;
import org.testng.annotations.Test;
import com.google.common.base.Optional;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Multimap;
/**
* @author Adrian Cole
*/
@ -124,6 +123,7 @@ public class EC2CreateNodesInGroupThenAddToSetTest {
expect(input.options.getLoginPassword()).andReturn(null);
expect(input.options.getLoginPrivateKey()).andReturn(null);
expect(input.options.shouldAuthenticateSudo()).andReturn(null);
expect(input.options.getMaxCount()).andReturn(0);
expect(
strategy.utils.customizeNodesAndAddToGoodMapOrPutExceptionIntoBadMap(eq(input.options),
@ -222,7 +222,7 @@ public class EC2CreateNodesInGroupThenAddToSetTest {
expect(input.options.getLoginPassword()).andReturn(null);
expect(input.options.getLoginPrivateKey()).andReturn(null);
expect(input.options.shouldAuthenticateSudo()).andReturn(null);
expect(input.options.getMaxCount()).andReturn(0);
expect(strategy.runningInstanceToNodeMetadata.apply(instance)).andReturn(nodeMetadata);
expect(

View File

@ -18,6 +18,7 @@ package org.jclouds.ec2.options;
import static org.jclouds.ec2.options.RunInstancesOptions.Builder.asType;
import static org.jclouds.ec2.options.RunInstancesOptions.Builder.withBlockDeviceMappings;
import static org.jclouds.ec2.options.RunInstancesOptions.Builder.withClientToken;
import static org.jclouds.ec2.options.RunInstancesOptions.Builder.withKernelId;
import static org.jclouds.ec2.options.RunInstancesOptions.Builder.withKeyName;
import static org.jclouds.ec2.options.RunInstancesOptions.Builder.withRamdisk;
@ -25,14 +26,13 @@ import static org.jclouds.ec2.options.RunInstancesOptions.Builder.withSecurityGr
import static org.jclouds.ec2.options.RunInstancesOptions.Builder.withUserData;
import static org.testng.Assert.assertEquals;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import org.jclouds.ec2.domain.BlockDeviceMapping;
import org.jclouds.ec2.domain.InstanceType;
import org.jclouds.http.options.HttpRequestOptions;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
/**
* Tests possible uses of RunInstancesOptions and RunInstancesOptions.Builder.*
*
@ -244,4 +244,15 @@ public class RunInstancesOptionsTest {
withBlockDeviceMappings(null);
}
@Test
public void testWithClientToken() {
RunInstancesOptions options = withClientToken("some-token");
assertEquals(options.buildFormParameters().get("ClientToken"), ImmutableList.of("some-token"));
}
@Test(expectedExceptions = NullPointerException.class)
public void testWithClientTokenNPE() {
withClientToken(null);
}
}

View File

@ -0,0 +1,74 @@
<DescribeInstancesResponse xmlns="http://ec2.amazonaws.com/doc/2009-11-30/">
<reservationId>r-47a5402e</reservationId>
<ownerId>AIDADH4IGTRXXKCD</ownerId>
<groupSet>
<item>
<groupId>default</groupId>
</item>
</groupSet>
<instancesSet>
<item>
<instanceId>i-2ba64342</instanceId>
<imageId>ami-aecd60c7</imageId>
<instanceState>
<code>0</code>
<name>pending</name>
</instanceState>
<privateDnsName></privateDnsName>
<dnsName></dnsName>
<keyName>example-key-name</keyName>
<amiLaunchIndex>0</amiLaunchIndex>
<instanceType>m1.small</instanceType>
<launchTime>2007-08-07T11:51:50.000Z</launchTime>
<placement>
<availabilityZone>us-east-1b</availabilityZone>
</placement>
<monitoring>
<state>enabled</state>
</monitoring>
<hypervisor>xen</hypervisor>
</item>
<item>
<instanceId>i-2bc64242</instanceId>
<imageId>ami-aecd60c7</imageId>
<instanceState>
<code>0</code>
<name>pending</name>
</instanceState>
<privateDnsName></privateDnsName>
<dnsName></dnsName>
<keyName>example-key-name</keyName>
<amiLaunchIndex>1</amiLaunchIndex>
<instanceType>m1.small</instanceType>
<launchTime>2007-08-07T11:51:50.000Z</launchTime>
<placement>
<availabilityZone>us-east-1b</availabilityZone>
</placement>
<monitoring>
<state>enabled</state>
</monitoring>
<hypervisor>xen</hypervisor>
</item>
<item>
<instanceId>i-2be64332</instanceId>
<imageId>ami-aecd60c7</imageId>
<instanceState>
<code>0</code>
<name>pending</name>
</instanceState>
<privateDnsName></privateDnsName>
<dnsName></dnsName>
<keyName>example-key-name</keyName>
<amiLaunchIndex>2</amiLaunchIndex>
<instanceType>m1.small</instanceType>
<launchTime>2007-08-07T11:51:50.000Z</launchTime>
<placement>
<availabilityZone>us-east-1b</availabilityZone>
</placement>
<monitoring>
<state>enabled</state>
</monitoring>
<hypervisor>xen</hypervisor>
</item>
</instancesSet>
</DescribeInstancesResponse>

View File

@ -0,0 +1,74 @@
<RunInstancesResponse xmlns="http://ec2.amazonaws.com/doc/2009-11-30/">
<reservationId>r-47a5402e</reservationId>
<ownerId>AIDADH4IGTRXXKCD</ownerId>
<groupSet>
<item>
<groupId>default</groupId>
</item>
</groupSet>
<instancesSet>
<item>
<instanceId>i-2ba64342</instanceId>
<imageId>ami-aecd60c7</imageId>
<instanceState>
<code>0</code>
<name>pending</name>
</instanceState>
<privateDnsName></privateDnsName>
<dnsName></dnsName>
<keyName>example-key-name</keyName>
<amiLaunchIndex>0</amiLaunchIndex>
<instanceType>m1.small</instanceType>
<launchTime>2007-08-07T11:51:50.000Z</launchTime>
<placement>
<availabilityZone>us-east-1b</availabilityZone>
</placement>
<monitoring>
<state>enabled</state>
</monitoring>
<hypervisor>xen</hypervisor>
</item>
<item>
<instanceId>i-2bc64242</instanceId>
<imageId>ami-aecd60c7</imageId>
<instanceState>
<code>0</code>
<name>pending</name>
</instanceState>
<privateDnsName></privateDnsName>
<dnsName></dnsName>
<keyName>example-key-name</keyName>
<amiLaunchIndex>1</amiLaunchIndex>
<instanceType>m1.small</instanceType>
<launchTime>2007-08-07T11:51:50.000Z</launchTime>
<placement>
<availabilityZone>us-east-1b</availabilityZone>
</placement>
<monitoring>
<state>enabled</state>
</monitoring>
<hypervisor>xen</hypervisor>
</item>
<item>
<instanceId>i-2be64332</instanceId>
<imageId>ami-aecd60c7</imageId>
<instanceState>
<code>0</code>
<name>pending</name>
</instanceState>
<privateDnsName></privateDnsName>
<dnsName></dnsName>
<keyName>example-key-name</keyName>
<amiLaunchIndex>2</amiLaunchIndex>
<instanceType>m1.small</instanceType>
<launchTime>2007-08-07T11:51:50.000Z</launchTime>
<placement>
<availabilityZone>us-east-1b</availabilityZone>
</placement>
<monitoring>
<state>enabled</state>
</monitoring>
<hypervisor>xen</hypervisor>
</item>
</instancesSet>
</RunInstancesResponse>