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.


Project: http://git-wip-us.apache.org/repos/asf/jclouds/repo
Commit: http://git-wip-us.apache.org/repos/asf/jclouds/commit/bf462601
Tree: http://git-wip-us.apache.org/repos/asf/jclouds/tree/bf462601
Diff: http://git-wip-us.apache.org/repos/asf/jclouds/diff/bf462601

Branch: refs/heads/cleanup-5976159-1.7.x
Commit: bf4626016b426d1fbc0e3e4d4e7499ea3aca9ac2
Parents: f9d4ac6
Author: Andrew Bayer <[email protected]>
Authored: Tue Feb 4 11:08:02 2014 -0800
Committer: Andrew Bayer <[email protected]>
Committed: Tue Feb 11 14:05:46 2014 -0800

----------------------------------------------------------------------
 .../ec2/compute/options/EC2TemplateOptions.java |  75 +++++++++--
 ...curityGroupsAsNeededAndReturnRunOptions.java |  24 ++--
 .../EC2CreateNodesInGroupThenAddToSet.java      |  48 ++++---
 .../ec2/options/RunInstancesOptions.java        |  17 +++
 .../compute/EC2ComputeServiceExpectTest.java    | 133 ++++++++++++++++++-
 .../ec2/compute/EC2ComputeServiceLiveTest.java  |   7 +
 .../BaseEC2ComputeServiceExpectTest.java        |  68 ++++++++--
 .../compute/options/EC2TemplateOptionsTest.java |  43 +++++-
 ...tyGroupsAsNeededAndReturnRunOptionsTest.java |  17 +--
 .../EC2CreateNodesInGroupThenAddToSetTest.java  |  16 +--
 .../ec2/options/RunInstancesOptionsTest.java    |  17 ++-
 .../resources/describe_instances_three_ids.xml  |  74 +++++++++++
 .../src/test/resources/run_instances_three.xml  |  74 +++++++++++
 13 files changed, 536 insertions(+), 77 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jclouds/blob/bf462601/apis/ec2/src/main/java/org/jclouds/ec2/compute/options/EC2TemplateOptions.java
----------------------------------------------------------------------
diff --git 
a/apis/ec2/src/main/java/org/jclouds/ec2/compute/options/EC2TemplateOptions.java
 
b/apis/ec2/src/main/java/org/jclouds/ec2/compute/options/EC2TemplateOptions.java
index 1d837fe..32c8124 100644
--- 
a/apis/ec2/src/main/java/org/jclouds/ec2/compute/options/EC2TemplateOptions.java
+++ 
b/apis/ec2/src/main/java/org/jclouds/ec2/compute/options/EC2TemplateOptions.java
@@ -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;
+   }
+
 }

http://git-wip-us.apache.org/repos/asf/jclouds/blob/bf462601/apis/ec2/src/main/java/org/jclouds/ec2/compute/strategy/CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.java
----------------------------------------------------------------------
diff --git 
a/apis/ec2/src/main/java/org/jclouds/ec2/compute/strategy/CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.java
 
b/apis/ec2/src/main/java/org/jclouds/ec2/compute/strategy/CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.java
index c54d983..f111f8a 100644
--- 
a/apis/ec2/src/main/java/org/jclouds/ec2/compute/strategy/CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.java
+++ 
b/apis/ec2/src/main/java/org/jclouds/ec2/compute/strategy/CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.java
@@ -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;
    }

http://git-wip-us.apache.org/repos/asf/jclouds/blob/bf462601/apis/ec2/src/main/java/org/jclouds/ec2/compute/strategy/EC2CreateNodesInGroupThenAddToSet.java
----------------------------------------------------------------------
diff --git 
a/apis/ec2/src/main/java/org/jclouds/ec2/compute/strategy/EC2CreateNodesInGroupThenAddToSet.java
 
b/apis/ec2/src/main/java/org/jclouds/ec2/compute/strategy/EC2CreateNodesInGroupThenAddToSet.java
index e40abe9..483a29b 100644
--- 
a/apis/ec2/src/main/java/org/jclouds/ec2/compute/strategy/EC2CreateNodesInGroupThenAddToSet.java
+++ 
b/apis/ec2/src/main/java/org/jclouds/ec2/compute/strategy/EC2CreateNodesInGroupThenAddToSet.java
@@ -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)

http://git-wip-us.apache.org/repos/asf/jclouds/blob/bf462601/apis/ec2/src/main/java/org/jclouds/ec2/options/RunInstancesOptions.java
----------------------------------------------------------------------
diff --git 
a/apis/ec2/src/main/java/org/jclouds/ec2/options/RunInstancesOptions.java 
b/apis/ec2/src/main/java/org/jclouds/ec2/options/RunInstancesOptions.java
index 7ccb209..427b222 100644
--- a/apis/ec2/src/main/java/org/jclouds/ec2/options/RunInstancesOptions.java
+++ b/apis/ec2/src/main/java/org/jclouds/ec2/options/RunInstancesOptions.java
@@ -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);
+      }
+
    }
 }

http://git-wip-us.apache.org/repos/asf/jclouds/blob/bf462601/apis/ec2/src/test/java/org/jclouds/ec2/compute/EC2ComputeServiceExpectTest.java
----------------------------------------------------------------------
diff --git 
a/apis/ec2/src/test/java/org/jclouds/ec2/compute/EC2ComputeServiceExpectTest.java
 
b/apis/ec2/src/test/java/org/jclouds/ec2/compute/EC2ComputeServiceExpectTest.java
index 24e03cf..cdc832d 100644
--- 
a/apis/ec2/src/test/java/org/jclouds/ec2/compute/EC2ComputeServiceExpectTest.java
+++ 
b/apis/ec2/src/test/java/org/jclouds/ec2/compute/EC2ComputeServiceExpectTest.java
@@ -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()

http://git-wip-us.apache.org/repos/asf/jclouds/blob/bf462601/apis/ec2/src/test/java/org/jclouds/ec2/compute/EC2ComputeServiceLiveTest.java
----------------------------------------------------------------------
diff --git 
a/apis/ec2/src/test/java/org/jclouds/ec2/compute/EC2ComputeServiceLiveTest.java 
b/apis/ec2/src/test/java/org/jclouds/ec2/compute/EC2ComputeServiceLiveTest.java
index e37e881..70f5ef9 100644
--- 
a/apis/ec2/src/test/java/org/jclouds/ec2/compute/EC2ComputeServiceLiveTest.java
+++ 
b/apis/ec2/src/test/java/org/jclouds/ec2/compute/EC2ComputeServiceLiveTest.java
@@ -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);

http://git-wip-us.apache.org/repos/asf/jclouds/blob/bf462601/apis/ec2/src/test/java/org/jclouds/ec2/compute/internal/BaseEC2ComputeServiceExpectTest.java
----------------------------------------------------------------------
diff --git 
a/apis/ec2/src/test/java/org/jclouds/ec2/compute/internal/BaseEC2ComputeServiceExpectTest.java
 
b/apis/ec2/src/test/java/org/jclouds/ec2/compute/internal/BaseEC2ComputeServiceExpectTest.java
index 610cfb7..4178e74 100644
--- 
a/apis/ec2/src/test/java/org/jclouds/ec2/compute/internal/BaseEC2ComputeServiceExpectTest.java
+++ 
b/apis/ec2/src/test/java/org/jclouds/ec2/compute/internal/BaseEC2ComputeServiceExpectTest.java
@@ -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()

http://git-wip-us.apache.org/repos/asf/jclouds/blob/bf462601/apis/ec2/src/test/java/org/jclouds/ec2/compute/options/EC2TemplateOptionsTest.java
----------------------------------------------------------------------
diff --git 
a/apis/ec2/src/test/java/org/jclouds/ec2/compute/options/EC2TemplateOptionsTest.java
 
b/apis/ec2/src/test/java/org/jclouds/ec2/compute/options/EC2TemplateOptionsTest.java
index 90b92c3..3025ca0 100644
--- 
a/apis/ec2/src/test/java/org/jclouds/ec2/compute/options/EC2TemplateOptionsTest.java
+++ 
b/apis/ec2/src/test/java/org/jclouds/ec2/compute/options/EC2TemplateOptionsTest.java
@@ -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);
+   }
 }

http://git-wip-us.apache.org/repos/asf/jclouds/blob/bf462601/apis/ec2/src/test/java/org/jclouds/ec2/compute/strategy/CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest.java
----------------------------------------------------------------------
diff --git 
a/apis/ec2/src/test/java/org/jclouds/ec2/compute/strategy/CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest.java
 
b/apis/ec2/src/test/java/org/jclouds/ec2/compute/strategy/CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest.java
index 2babb8d..0c57409 100644
--- 
a/apis/ec2/src/test/java/org/jclouds/ec2/compute/strategy/CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest.java
+++ 
b/apis/ec2/src/test/java/org/jclouds/ec2/compute/strategy/CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest.java
@@ -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);

http://git-wip-us.apache.org/repos/asf/jclouds/blob/bf462601/apis/ec2/src/test/java/org/jclouds/ec2/compute/strategy/EC2CreateNodesInGroupThenAddToSetTest.java
----------------------------------------------------------------------
diff --git 
a/apis/ec2/src/test/java/org/jclouds/ec2/compute/strategy/EC2CreateNodesInGroupThenAddToSetTest.java
 
b/apis/ec2/src/test/java/org/jclouds/ec2/compute/strategy/EC2CreateNodesInGroupThenAddToSetTest.java
index f0201ad..1be1c8c 100644
--- 
a/apis/ec2/src/test/java/org/jclouds/ec2/compute/strategy/EC2CreateNodesInGroupThenAddToSetTest.java
+++ 
b/apis/ec2/src/test/java/org/jclouds/ec2/compute/strategy/EC2CreateNodesInGroupThenAddToSetTest.java
@@ -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(

http://git-wip-us.apache.org/repos/asf/jclouds/blob/bf462601/apis/ec2/src/test/java/org/jclouds/ec2/options/RunInstancesOptionsTest.java
----------------------------------------------------------------------
diff --git 
a/apis/ec2/src/test/java/org/jclouds/ec2/options/RunInstancesOptionsTest.java 
b/apis/ec2/src/test/java/org/jclouds/ec2/options/RunInstancesOptionsTest.java
index f9c292a..d46535d 100644
--- 
a/apis/ec2/src/test/java/org/jclouds/ec2/options/RunInstancesOptionsTest.java
+++ 
b/apis/ec2/src/test/java/org/jclouds/ec2/options/RunInstancesOptionsTest.java
@@ -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);
+   }
+
 }

http://git-wip-us.apache.org/repos/asf/jclouds/blob/bf462601/apis/ec2/src/test/resources/describe_instances_three_ids.xml
----------------------------------------------------------------------
diff --git a/apis/ec2/src/test/resources/describe_instances_three_ids.xml 
b/apis/ec2/src/test/resources/describe_instances_three_ids.xml
new file mode 100644
index 0000000..dbf3b71
--- /dev/null
+++ b/apis/ec2/src/test/resources/describe_instances_three_ids.xml
@@ -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>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jclouds/blob/bf462601/apis/ec2/src/test/resources/run_instances_three.xml
----------------------------------------------------------------------
diff --git a/apis/ec2/src/test/resources/run_instances_three.xml 
b/apis/ec2/src/test/resources/run_instances_three.xml
new file mode 100644
index 0000000..6530a7f
--- /dev/null
+++ b/apis/ec2/src/test/resources/run_instances_three.xml
@@ -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>
\ No newline at end of file

Reply via email to