diff --git a/compute/src/main/clojure/org/jclouds/compute2.clj b/compute/src/main/clojure/org/jclouds/compute2.clj index 76f3ac8752..9033a64831 100644 --- a/compute/src/main/clojure/org/jclouds/compute2.clj +++ b/compute/src/main/clojure/org/jclouds/compute2.clj @@ -366,6 +366,7 @@ Here's an example of creating and running a small linux node in the group webser :key-pair ;; aws-ec2 :placement-group :subnet-id :spot-price :spot-options + :iam-instance-profile-name :iam-instance-profile-arn ;; cloudstack aws-ec2 :security-group-ids ;; softlayer diff --git a/providers/aws-ec2/pom.xml b/providers/aws-ec2/pom.xml index 9cc8d02fec..9759ee6d85 100644 --- a/providers/aws-ec2/pom.xml +++ b/providers/aws-ec2/pom.xml @@ -35,7 +35,7 @@ https://ec2.us-east-1.amazonaws.com - 2011-05-15 + 2012-06-01 ${test.aws.identity} ${test.aws.credential} @@ -68,6 +68,12 @@ test-jar test + + org.jclouds.labs + aws-iam + ${project.version} + test + org.jclouds.provider aws-cloudwatch diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/binders/BindLaunchSpecificationToFormParams.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/binders/BindLaunchSpecificationToFormParams.java index 588d8ab05c..8d8b75ccf3 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/binders/BindLaunchSpecificationToFormParams.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/binders/BindLaunchSpecificationToFormParams.java @@ -27,6 +27,7 @@ import java.util.Map.Entry; import javax.inject.Singleton; import org.jclouds.aws.ec2.domain.LaunchSpecification; +import org.jclouds.aws.ec2.domain.LaunchSpecification.IAMInstanceProfileRequest; import org.jclouds.aws.ec2.options.AWSRunInstancesOptions; import org.jclouds.http.HttpRequest; import org.jclouds.rest.Binder; @@ -78,7 +79,13 @@ public class BindLaunchSpecificationToFormParams implements Binder, Function entry : options.buildFormParameters().entries()) { builder.put("LaunchSpecification." + entry.getKey(), entry.getValue()); } diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/AWSEC2TemplateOptions.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/AWSEC2TemplateOptions.java index 3c11d7f15c..05ab6ab06d 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/AWSEC2TemplateOptions.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/AWSEC2TemplateOptions.java @@ -33,6 +33,7 @@ import org.jclouds.domain.LoginCredentials; import org.jclouds.ec2.compute.options.EC2TemplateOptions; import org.jclouds.ec2.domain.BlockDeviceMapping; import org.jclouds.javax.annotation.Nullable; +import org.jclouds.rest.annotations.SinceApiVersion; import org.jclouds.scriptbuilder.domain.Statement; import com.google.common.base.Objects; @@ -72,6 +73,10 @@ public class AWSEC2TemplateOptions extends EC2TemplateOptions implements Cloneab AWSEC2TemplateOptions eTo = AWSEC2TemplateOptions.class.cast(to); if (getSubnetId() != null) eTo.subnetId(getSubnetId()); + if (getIAMInstanceProfileArn() != null) + eTo.iamInstanceProfileArn(getIAMInstanceProfileArn()); + if (getIAMInstanceProfileName() != null) + eTo.iamInstanceProfileName(getIAMInstanceProfileName()); if (isMonitoringEnabled()) eTo.enableMonitoring(); if (!shouldAutomaticallyCreatePlacementGroup()) @@ -94,6 +99,8 @@ public class AWSEC2TemplateOptions extends EC2TemplateOptions implements Cloneab private Float spotPrice; private RequestSpotInstancesOptions spotOptions = RequestSpotInstancesOptions.NONE; private Set groupIds = ImmutableSet.of(); + private String iamInstanceProfileArn; + private String iamInstanceProfileName; @Override public boolean equals(Object o) { @@ -106,13 +113,14 @@ public class AWSEC2TemplateOptions extends EC2TemplateOptions implements Cloneab && equal(this.placementGroup, that.placementGroup) && equal(this.noPlacementGroup, that.noPlacementGroup) && equal(this.subnetId, that.subnetId) && equal(this.spotPrice, that.spotPrice) && equal(this.spotOptions, that.spotOptions) - && equal(this.groupIds, that.groupIds); + && equal(this.groupIds, that.groupIds) && equal(this.iamInstanceProfileArn, that.iamInstanceProfileArn) + && equal(this.iamInstanceProfileName, that.iamInstanceProfileName); } @Override public int hashCode() { return Objects.hashCode(super.hashCode(), monitoringEnabled, placementGroup, noPlacementGroup, subnetId, - spotPrice, spotOptions, groupIds); + spotPrice, spotOptions, groupIds, iamInstanceProfileArn, iamInstanceProfileName); } @Override @@ -129,6 +137,8 @@ public class AWSEC2TemplateOptions extends EC2TemplateOptions implements Cloneab toString.add("spotOptions", spotOptions); if (groupIds.size() != 0) toString.add("groupIds", groupIds); + toString.add("iamInstanceProfileArn", iamInstanceProfileArn); + toString.add("iamInstanceProfileName", iamInstanceProfileName); return toString; } @@ -171,6 +181,24 @@ public class AWSEC2TemplateOptions extends EC2TemplateOptions implements Cloneab return this; } + /** + * @see org.jclouds.aws.ec2.options.AWSRunInstancesOptions#withIAMInstanceProfileArn(String) + */ + @SinceApiVersion("2012-06-01") + public AWSEC2TemplateOptions iamInstanceProfileArn(String arn) { + this.iamInstanceProfileArn = checkNotNull(emptyToNull(arn), "arn must be defined"); + return this; + } + + /** + * @see org.jclouds.aws.ec2.options.AWSRunInstancesOptions#withIAMInstanceProfileName(String) + */ + @SinceApiVersion("2012-06-01") + public AWSEC2TemplateOptions iamInstanceProfileName(String name) { + this.iamInstanceProfileName = checkNotNull(emptyToNull(name), "name must be defined"); + return this; + } + /** * Specifies the maximum spot price to use */ @@ -393,7 +421,7 @@ public class AWSEC2TemplateOptions extends EC2TemplateOptions implements Cloneab } /** - * @see TemplateOptions#spotPrice + * @see AWSEC2TemplateOptions#subnetId */ public static AWSEC2TemplateOptions subnetId(String subnetId) { AWSEC2TemplateOptions options = new AWSEC2TemplateOptions(); @@ -401,7 +429,25 @@ public class AWSEC2TemplateOptions extends EC2TemplateOptions implements Cloneab } /** - * @see TemplateOptions#spotPrice + * @see AWSEC2TemplateOptions#iamInstanceProfileArn + */ + @SinceApiVersion("2012-06-01") + public static AWSEC2TemplateOptions iamInstanceProfileArn(String arn) { + AWSEC2TemplateOptions options = new AWSEC2TemplateOptions(); + return options.iamInstanceProfileArn(arn); + } + + /** + * @see AWSEC2TemplateOptions#iamInstanceProfileName + */ + @SinceApiVersion("2012-06-01") + public static AWSEC2TemplateOptions iamInstanceProfileName(String name) { + AWSEC2TemplateOptions options = new AWSEC2TemplateOptions(); + return options.iamInstanceProfileName(name); + } + + /** + * @see AWSEC2TemplateOptions#spotPrice */ public static AWSEC2TemplateOptions spotPrice(Float spotPrice) { AWSEC2TemplateOptions options = new AWSEC2TemplateOptions(); @@ -409,7 +455,7 @@ public class AWSEC2TemplateOptions extends EC2TemplateOptions implements Cloneab } /** - * @see TemplateOptions#spotOptions + * @see AWSEC2TemplateOptions#spotOptions */ public static AWSEC2TemplateOptions spotOptions(RequestSpotInstancesOptions spotOptions) { AWSEC2TemplateOptions options = new AWSEC2TemplateOptions(); @@ -431,6 +477,11 @@ public class AWSEC2TemplateOptions extends EC2TemplateOptions implements Cloneab AWSEC2TemplateOptions options = new AWSEC2TemplateOptions(); return AWSEC2TemplateOptions.class.cast(options.userMetadata(key, value)); } + + public static AWSEC2TemplateOptions blockUntilRunning(boolean blockUntilRunning) { + AWSEC2TemplateOptions options = new AWSEC2TemplateOptions(); + return options.blockUntilRunning(blockUntilRunning); + } } // methods that only facilitate returning the correct object type @@ -690,4 +741,23 @@ public class AWSEC2TemplateOptions extends EC2TemplateOptions implements Cloneab return spotOptions; } + /** + * The Amazon resource name (ARN) of the IAM Instance Profile (IIP) to associate with the instance. + * + * @see org.jclouds.aws.ec2.options.AWSRunInstancesOptions#withIAMInstanceProfileArn(String) + */ + @SinceApiVersion("2012-06-01") + public String getIAMInstanceProfileArn() { + return iamInstanceProfileArn; + } + + /** + * The name of the IAM Instance Profile (IIP) to associate with the instance. + * + * @see org.jclouds.aws.ec2.options.AWSRunInstancesOptions#withIAMInstanceProfileName(String) + */ + @SinceApiVersion("2012-06-01") + public String getIAMInstanceProfileName() { + return iamInstanceProfileName; + } } diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/strategy/AWSEC2CreateNodesInGroupThenAddToSet.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/strategy/AWSEC2CreateNodesInGroupThenAddToSet.java index fdd3f0e85c..35b05ee222 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/strategy/AWSEC2CreateNodesInGroupThenAddToSet.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/strategy/AWSEC2CreateNodesInGroupThenAddToSet.java @@ -99,7 +99,8 @@ public class AWSEC2CreateNodesInGroupThenAddToSet extends EC2CreateNodesInGroupT AWSEC2TemplateOptions awsOptions = AWSEC2TemplateOptions.class.cast(template.getOptions()); LaunchSpecification spec = AWSRunInstancesOptions.class.cast(instanceOptions).getLaunchSpecificationBuilder() .imageId(template.getImage().getProviderId()).availabilityZone(zone).subnetId(awsOptions.getSubnetId()) - .build(); + .iamInstanceProfileArn(awsOptions.getIAMInstanceProfileArn()) + .iamInstanceProfileName(awsOptions.getIAMInstanceProfileName()).build(); RequestSpotInstancesOptions options = awsOptions.getSpotOptions(); if (logger.isDebugEnabled()) logger.debug(">> requesting %d spot instances region(%s) price(%f) spec(%s) options(%s)", count, region, diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/strategy/CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/strategy/CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions.java index cbec492023..5b6cd3b32f 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/strategy/CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/strategy/CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions.java @@ -91,8 +91,13 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions if (placementGroupName != null) instanceOptions.inPlacementGroup(placementGroupName); - if (AWSEC2TemplateOptions.class.cast(template.getOptions()).isMonitoringEnabled()) + AWSEC2TemplateOptions awsTemplateOptions = AWSEC2TemplateOptions.class.cast(template.getOptions()); + if (awsTemplateOptions.isMonitoringEnabled()) instanceOptions.enableMonitoring(); + if (awsTemplateOptions.getIAMInstanceProfileArn() != null) + instanceOptions.withIAMInstanceProfileArn(awsTemplateOptions.getIAMInstanceProfileArn()); + if (awsTemplateOptions.getIAMInstanceProfileName() != null) + instanceOptions.withIAMInstanceProfileName(awsTemplateOptions.getIAMInstanceProfileName()); return instanceOptions; } diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/domain/AWSRunningInstance.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/domain/AWSRunningInstance.java index ad50dbe94e..6011f24c71 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/domain/AWSRunningInstance.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/domain/AWSRunningInstance.java @@ -18,6 +18,8 @@ */ package org.jclouds.aws.ec2.domain; +import static com.google.common.base.Objects.equal; +import static com.google.common.base.Objects.toStringHelper; import static com.google.common.base.Preconditions.checkNotNull; import java.util.Date; @@ -30,8 +32,11 @@ import org.jclouds.ec2.domain.InstanceState; import org.jclouds.ec2.domain.RootDeviceType; import org.jclouds.ec2.domain.RunningInstance; import org.jclouds.javax.annotation.Nullable; +import org.jclouds.rest.annotations.SinceApiVersion; +import com.google.common.base.Objects; import com.google.common.base.Objects.ToStringHelper; +import com.google.common.base.Optional; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Maps; @@ -64,6 +69,8 @@ public class AWSRunningInstance extends RunningInstance { private String vpcId; private Hypervisor hypervisor; private Map securityGroupIdToNames = Maps.newLinkedHashMap(); + private String iamInstanceProfileArn; + private String iamInstanceProfileId; public Builder securityGroupIdToNames(Map securityGroupIdToNames) { this.securityGroupIdToNames = ImmutableMap.copyOf(checkNotNull(securityGroupIdToNames, @@ -118,13 +125,34 @@ public class AWSRunningInstance extends RunningInstance { return this; } + /** + * @see AWSRunningInstance#getIAMInstanceProfile() + */ + public Builder iamInstanceProfileArn(String iamInstanceProfileArn) { + this.iamInstanceProfileArn = iamInstanceProfileArn; + return this; + } + + /** + * @see AWSRunningInstance#getIAMInstanceProfile() + */ + public Builder iamInstanceProfileId(String iamInstanceProfileId) { + this.iamInstanceProfileId = iamInstanceProfileId; + return this; + } + @Override public AWSRunningInstance build() { + Optional iamInstanceProfile = Optional.absent(); + if (iamInstanceProfileArn != null && iamInstanceProfileId != null) { + iamInstanceProfile = Optional.of(IAMInstanceProfile.forArnAndId(iamInstanceProfileArn, + iamInstanceProfileId)); + } return new AWSRunningInstance(region, securityGroupIdToNames, amiLaunchIndex, dnsName, imageId, instanceId, instanceState, rawState, instanceType, ipAddress, kernelId, keyName, launchTime, availabilityZone, virtualizationType, platform, privateDnsName, privateIpAddress, ramdiskId, reason, rootDeviceType, rootDeviceName, ebsBlockDevices, monitoringState, placementGroup, productCodes, subnetId, - spotInstanceRequestId, vpcId, hypervisor, tags); + spotInstanceRequestId, vpcId, hypervisor, tags, iamInstanceProfile); } @Override @@ -136,6 +164,10 @@ public class AWSRunningInstance extends RunningInstance { .productCodes(awsIn.productCodes).subnetId(awsIn.subnetId) .spotInstanceRequestId(awsIn.spotInstanceRequestId).vpcId(awsIn.vpcId).hypervisor(awsIn.hypervisor) .securityGroupIdToNames(awsIn.securityGroupIdToNames); + if (awsIn.getIAMInstanceProfile().isPresent()) { + iamInstanceProfileArn(awsIn.getIAMInstanceProfile().get().getArn()); + iamInstanceProfileId(awsIn.getIAMInstanceProfile().get().getId()); + } } return this; } @@ -159,6 +191,7 @@ public class AWSRunningInstance extends RunningInstance { private final String vpcId; private final Hypervisor hypervisor; private final Map securityGroupIdToNames; + private final Optional iamInstanceProfile; protected AWSRunningInstance(String region, Map securityGroupIdToNames, String amiLaunchIndex, String dnsName, String imageId, String instanceId, InstanceState instanceState, String rawState, @@ -167,7 +200,7 @@ public class AWSRunningInstance extends RunningInstance { String privateIpAddress, String ramdiskId, String reason, RootDeviceType rootDeviceType, String rootDeviceName, Map ebsBlockDevices, MonitoringState monitoringState, String placementGroup, Iterable productCodes, String subnetId, String spotInstanceRequestId, - String vpcId, Hypervisor hypervisor, Map tags) { + String vpcId, Hypervisor hypervisor, Map tags, Optional iamInstanceProfile) { super(region, securityGroupIdToNames.values(), amiLaunchIndex, dnsName, imageId, instanceId, instanceState, rawState, instanceType, ipAddress, kernelId, keyName, launchTime, availabilityZone, virtualizationType, platform, privateDnsName, privateIpAddress, ramdiskId, reason, rootDeviceType, rootDeviceName, @@ -181,6 +214,7 @@ public class AWSRunningInstance extends RunningInstance { this.hypervisor = checkNotNull(hypervisor, "hypervisor"); this.securityGroupIdToNames = ImmutableMap. copyOf(checkNotNull(securityGroupIdToNames, "securityGroupIdToNames")); + this.iamInstanceProfile = checkNotNull(iamInstanceProfile, "iamInstanceProfile of %s", instanceId); } public Map getSecurityGroupIdToNames() { @@ -240,11 +274,66 @@ public class AWSRunningInstance extends RunningInstance { return subnetId; } + /** + * The IAM Instance Profile (IIP) associated with the instance. + */ + @SinceApiVersion("2012-06-01") + public Optional getIAMInstanceProfile() { + return iamInstanceProfile; + } + @Override protected ToStringHelper string() { return super.string().add("monitoringState", monitoringState).add("placementGroup", placementGroup) .add("subnetId", subnetId).add("spotInstanceRequestId", spotInstanceRequestId).add("vpcId", vpcId) - .add("hypervisor", hypervisor); + .add("hypervisor", hypervisor).add("iamInstanceProfile", iamInstanceProfile.orNull()); } + public static class IAMInstanceProfile { + public static IAMInstanceProfile forArnAndId(String arn, String id) { + return new IAMInstanceProfile(arn, id); + } + + private final String arn; + private final String id; + + private IAMInstanceProfile(String arn, String id) { + this.arn = checkNotNull(arn, "arn"); + this.id = checkNotNull(id, "id for %s", arn); + } + + /** + * The Amazon resource name (ARN) of the IAM Instance Profile (IIP) to associate with the instance. + */ + public String getArn() { + return arn; + } + + /** + * The ID of the IAM Instance Profile ID (IIP) associated with the instance. + */ + public String getId() { + return id; + } + + @Override + public int hashCode() { + return Objects.hashCode(arn, id); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null || getClass() != obj.getClass()) + return false; + IAMInstanceProfile that = IAMInstanceProfile.class.cast(obj); + return equal(this.arn, that.arn) && equal(this.id, that.id); + } + + @Override + public String toString() { + return toStringHelper("").add("arn", arn).add("id", id).toString(); + } + } } diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/domain/LaunchSpecification.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/domain/LaunchSpecification.java index de6c7ea822..18e61eee28 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/domain/LaunchSpecification.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/domain/LaunchSpecification.java @@ -18,6 +18,8 @@ */ package org.jclouds.aws.ec2.domain; +import static com.google.common.base.Objects.equal; +import static com.google.common.base.Objects.toStringHelper; import static com.google.common.base.Preconditions.checkNotNull; import java.util.Arrays; @@ -29,7 +31,10 @@ import org.jclouds.ec2.domain.BlockDeviceMapping.MapEBSSnapshotToDevice; import org.jclouds.ec2.domain.BlockDeviceMapping.MapEphemeralDeviceToDevice; import org.jclouds.ec2.domain.BlockDeviceMapping.MapNewVolumeToDevice; import org.jclouds.javax.annotation.Nullable; +import org.jclouds.rest.annotations.SinceApiVersion; +import com.google.common.base.Objects; +import com.google.common.base.Optional; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSortedSet; @@ -62,6 +67,8 @@ public class LaunchSpecification { protected ImmutableSet.Builder securityGroupIds = ImmutableSet.builder(); protected ImmutableSet.Builder securityGroupNames = ImmutableSet.builder(); protected byte[] userData; + private String iamInstanceProfileArn; + private String iamInstanceProfileName; public void clear() { securityGroupIdToNames = ImmutableMap.builder(); @@ -77,6 +84,8 @@ public class LaunchSpecification { securityGroupIds = ImmutableSet.builder(); securityGroupNames = ImmutableSet.builder(); userData = null; + iamInstanceProfileArn = null; + iamInstanceProfileName = null; } public Builder securityGroupIdToNames(Map securityGroupIdToNames) { @@ -183,19 +192,52 @@ public class LaunchSpecification { return this; } + /** + * @see LaunchSpecification#getIAMInstanceProfile() + */ + public Builder iamInstanceProfileArn(String iamInstanceProfileArn) { + this.iamInstanceProfileArn = iamInstanceProfileArn; + return this; + } + + /** + * @see LaunchSpecification#getIAMInstanceProfile() + */ + public Builder iamInstanceProfileName(String iamInstanceProfileName) { + this.iamInstanceProfileName = iamInstanceProfileName; + return this; + } + public LaunchSpecification build() { + Optional iamInstanceProfile; + if (iamInstanceProfileArn != null && iamInstanceProfileName != null) { + iamInstanceProfile = Optional.of(IAMInstanceProfileRequest.forArnAndName(iamInstanceProfileArn, + iamInstanceProfileName)); + } else if (iamInstanceProfileArn != null) { + iamInstanceProfile = Optional.of(IAMInstanceProfileRequest.forArn(iamInstanceProfileArn)); + } else if (iamInstanceProfileName != null) { + iamInstanceProfile = Optional.of(IAMInstanceProfileRequest.forName(iamInstanceProfileName)); + } else { + iamInstanceProfile = Optional.absent(); + } return new LaunchSpecification(instanceType, imageId, kernelId, ramdiskId, availabilityZone, subnetId, keyName, securityGroupIdToNames.build(), blockDeviceMappings.build(), monitoringEnabled, - securityGroupIds.build(), securityGroupNames.build(), userData); + securityGroupIds.build(), securityGroupNames.build(), userData, iamInstanceProfile); } public static Builder fromLaunchSpecification(LaunchSpecification in) { - return new Builder().instanceType(in.getInstanceType()).imageId(in.getImageId()).kernelId(in.getKernelId()) + Builder builder = new Builder(); + builder.instanceType(in.getInstanceType()).imageId(in.getImageId()).kernelId(in.getKernelId()) .ramdiskId(in.getRamdiskId()).availabilityZone(in.getAvailabilityZone()).subnetId(in.getSubnetId()) .keyName(in.getKeyName()).securityGroupIdToNames(in.getSecurityGroupIdToNames()) .securityGroupIds(in.getSecurityGroupIds()).securityGroupNames(in.getSecurityGroupNames()) .blockDeviceMappings(in.getBlockDeviceMappings()).monitoringEnabled(in.isMonitoringEnabled()) .userData(in.getUserData()); + if (in.getIAMInstanceProfile().isPresent()) { + builder.iamInstanceProfileArn(in.getIAMInstanceProfile().get().getArn().orNull()); + builder.iamInstanceProfileName(in.getIAMInstanceProfile().get().getName().orNull()); + } + return builder; } } @@ -212,11 +254,13 @@ public class LaunchSpecification { protected final Set securityGroupNames; protected final Boolean monitoringEnabled; protected final byte[] userData; + protected final Optional iamInstanceProfile; public LaunchSpecification(String instanceType, String imageId, String kernelId, String ramdiskId, String availabilityZone, String subnetId, String keyName, Map securityGroupIdToNames, Iterable blockDeviceMappings, Boolean monitoringEnabled, - Set securityGroupIds, Set securityGroupNames, byte[] userData) { + Set securityGroupIds, Set securityGroupNames, byte[] userData, + Optional iamInstanceProfile) { this.instanceType = checkNotNull(instanceType, "instanceType"); this.imageId = checkNotNull(imageId, "imageId"); this.kernelId = kernelId; @@ -230,6 +274,7 @@ public class LaunchSpecification { this.securityGroupNames = ImmutableSortedSet.copyOf(checkNotNull(securityGroupNames, "securityGroupNames")); this.monitoringEnabled = monitoringEnabled; this.userData = userData; + this.iamInstanceProfile = checkNotNull(iamInstanceProfile, "iamInstanceProfile"); } public Map getSecurityGroupIdToNames() { @@ -322,6 +367,14 @@ public class LaunchSpecification { return userData; } + /** + * The IAM Instance Profile (IIP) associated with the instance. + */ + @SinceApiVersion("2012-06-01") + public Optional getIAMInstanceProfile() { + return iamInstanceProfile; + } + @Override public int hashCode() { final int prime = 31; @@ -338,6 +391,7 @@ public class LaunchSpecification { result = prime * result + ((securityGroupIdToNames == null) ? 0 : securityGroupIdToNames.hashCode()); result = prime * result + ((securityGroupIds == null) ? 0 : securityGroupIds.hashCode()); result = prime * result + ((securityGroupNames == null) ? 0 : securityGroupNames.hashCode()); + result = prime * result + ((!iamInstanceProfile.isPresent()) ? 0 : iamInstanceProfile.get().hashCode()); result = prime * result + Arrays.hashCode(userData); return result; } @@ -411,6 +465,11 @@ public class LaunchSpecification { return false; } else if (!securityGroupNames.equals(other.securityGroupNames)) return false; + if (!iamInstanceProfile.isPresent()) { + if (other.iamInstanceProfile.isPresent()) + return false; + } else if (!iamInstanceProfile.get().equals(other.iamInstanceProfile.orNull())) + return false; if (!Arrays.equals(userData, other.userData)) return false; return true; @@ -426,7 +485,65 @@ public class LaunchSpecification { + ramdiskId + ", availabilityZone=" + availabilityZone + ", subnetId=" + subnetId + ", keyName=" + keyName + ", securityGroupIdToNames=" + securityGroupIdToNames + ", blockDeviceMappings=" + blockDeviceMappings + ", securityGroupIds=" + securityGroupIds + ", securityGroupNames=" + securityGroupNames - + ", monitoringEnabled=" + monitoringEnabled + ", userData=" + Arrays.toString(userData) + "]"; + + ", monitoringEnabled=" + monitoringEnabled + ", userData=" + Arrays.toString(userData) + + ", iamInstanceProfile=" + iamInstanceProfile.orNull() + "]"; } + @SinceApiVersion("2012-06-01") + public static class IAMInstanceProfileRequest { + + public static IAMInstanceProfileRequest forArn(String arn) { + return new IAMInstanceProfileRequest(Optional.of(checkNotNull(arn, "arn")), Optional. absent()); + } + + public static IAMInstanceProfileRequest forName(String name) { + return new IAMInstanceProfileRequest(Optional. absent(), Optional.of(checkNotNull(name, "name"))); + } + + public static IAMInstanceProfileRequest forArnAndName(String arn, String name) { + return new IAMInstanceProfileRequest(Optional.of(checkNotNull(arn, "arn")), Optional.of(checkNotNull(name, "name"))); + } + + private final Optional arn; + private final Optional name; + + private IAMInstanceProfileRequest(Optional arn, Optional name) { + this.arn = checkNotNull(arn, "arn"); + this.name = checkNotNull(name, "name for %s", arn); + } + + /** + * The Amazon resource name (ARN) of the IAM Instance Profile (IIP) to associate with the instance. + */ + public Optional getArn() { + return arn; + } + + /** + * The name of the IAM Instance Profile (IIP) to associate with the instance. + */ + public Optional getName() { + return name; + } + + @Override + public int hashCode() { + return Objects.hashCode(arn, name); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null || getClass() != obj.getClass()) + return false; + IAMInstanceProfileRequest that = IAMInstanceProfileRequest.class.cast(obj); + return equal(this.arn, that.arn) && equal(this.name, that.name); + } + + @Override + public String toString() { + return toStringHelper("").omitNullValues().add("arn", arn.orNull()).add("name", name.orNull()).toString(); + } + } } diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/options/AWSRunInstancesOptions.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/options/AWSRunInstancesOptions.java index a2837cafc0..815adffedb 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/options/AWSRunInstancesOptions.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/options/AWSRunInstancesOptions.java @@ -24,7 +24,9 @@ import java.util.Set; import org.jclouds.aws.ec2.domain.LaunchSpecification; import org.jclouds.ec2.domain.BlockDeviceMapping; +import org.jclouds.ec2.domain.InstanceType; import org.jclouds.ec2.options.RunInstancesOptions; +import org.jclouds.rest.annotations.SinceApiVersion; import com.google.common.collect.ImmutableSet; @@ -93,7 +95,29 @@ public class AWSRunInstancesOptions extends RunInstancesOptions { public AWSRunInstancesOptions withSecurityGroupIds(String... securityGroupIds) { return withSecurityGroupIds(ImmutableSet.copyOf(securityGroupIds)); } - + + /** + * Amazon resource name (ARN) of the IAM Instance Profile (IIP) to associate with the instances. + * + * @see org.jclouds.aws.ec2.domain.AWSRunningInstance#getIAMInstanceProfile() + */ + @SinceApiVersion("2012-06-01") + public AWSRunInstancesOptions withIAMInstanceProfileArn(String arn) { + formParameters.put("IamInstanceProfile.Arn", checkNotNull(arn, "arn")); + return this; + } + + /** + * The name of the IAM Instance Profile (IIP) to associate with the instances. + * + * @see org.jclouds.aws.ec2.domain.AWSRunningInstance#getIAMInstanceProfile() + */ + @SinceApiVersion("2012-06-01") + public AWSRunInstancesOptions withIAMInstanceProfileName(String name) { + formParameters.put("IamInstanceProfile.Name", checkNotNull(name, "name")); + return this; + } + public static class Builder extends RunInstancesOptions.Builder { /** @@ -128,6 +152,22 @@ public class AWSRunInstancesOptions extends RunInstancesOptions { return options.withSubnetId(subnetId); } + /** + * @see AWSRunInstancesOptions#withIAMInstanceProfileArn(String) + */ + public static AWSRunInstancesOptions withIAMInstanceProfileArn(String arn) { + AWSRunInstancesOptions options = new AWSRunInstancesOptions(); + return options.withIAMInstanceProfileArn(arn); + } + + /** + * @see AWSRunInstancesOptions#withIAMInstanceProfileName(String) + */ + public static AWSRunInstancesOptions withIAMInstanceProfileName(String id) { + AWSRunInstancesOptions options = new AWSRunInstancesOptions(); + return options.withIAMInstanceProfileName(id); + } + /** * @see AWSRunInstancesOptions#withKeyName(String) */ diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/xml/BaseAWSReservationHandler.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/xml/BaseAWSReservationHandler.java index 85cf78e8f6..c470edf70f 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/xml/BaseAWSReservationHandler.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/xml/BaseAWSReservationHandler.java @@ -95,6 +95,7 @@ public abstract class BaseAWSReservationHandler extends HandlerForGeneratedRe private Set instances = Sets.newLinkedHashSet(); private boolean inPlacement; + private boolean inIamInstanceProfile; @Override public void startElement(String uri, String localName, String qName, Attributes attrs) throws SAXException { @@ -104,6 +105,8 @@ public abstract class BaseAWSReservationHandler extends HandlerForGeneratedRe inInstancesSet = true; } else if (equalsOrSuffix(qName, "placement")) { inPlacement = true; + } else if (equalsOrSuffix(qName, "iamInstanceProfile")) { + inIamInstanceProfile = true; } } @@ -117,6 +120,10 @@ public abstract class BaseAWSReservationHandler extends HandlerForGeneratedRe groupId = currentOrNull(currentText); } else if (equalsOrSuffix(qName, "groupName") && inPlacement) { builder.placementGroup(currentOrNull(currentText)); + } else if (equalsOrSuffix(qName, "arn") && inIamInstanceProfile) { + builder.iamInstanceProfileArn(currentOrNull(currentText)); + } else if (equalsOrSuffix(qName, "id") && inIamInstanceProfile) { + builder.iamInstanceProfileId(currentOrNull(currentText)); } else if (equalsOrSuffix(qName, "groupName")) { switch (itemDepth) { case 2: @@ -141,6 +148,8 @@ public abstract class BaseAWSReservationHandler extends HandlerForGeneratedRe inInstancesSet = false; } else if (equalsOrSuffix(qName, "placement")) { inPlacement = false; + } else if (equalsOrSuffix(qName, "iamInstanceProfile")) { + inIamInstanceProfile = false; } else if (equalsOrSuffix(qName, "ownerId")) { ownerId = currentOrNull(currentText); } else if (equalsOrSuffix(qName, "requesterId")) { diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/xml/LaunchSpecificationHandler.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/xml/LaunchSpecificationHandler.java index e8390ff98e..80030dc5d6 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/xml/LaunchSpecificationHandler.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/xml/LaunchSpecificationHandler.java @@ -18,6 +18,8 @@ */ package org.jclouds.aws.ec2.xml; +import static org.jclouds.util.SaxUtils.equalsOrSuffix; + import javax.annotation.Resource; import javax.inject.Inject; @@ -59,24 +61,33 @@ public class LaunchSpecificationHandler extends HandlerForGeneratedRequestWithRe protected StringBuilder currentText = new StringBuilder(); private boolean inBlockDeviceMapping; + private boolean inIamInstanceProfile; private String groupId; public void startElement(String uri, String name, String qName, Attributes attrs) { if (qName.equals("blockDeviceMapping")) { inBlockDeviceMapping = true; + } else if (equalsOrSuffix(qName, "iamInstanceProfile")) { + inIamInstanceProfile = true; } } public void endElement(String uri, String name, String qName) { if (qName.equals("blockDeviceMapping")) { inBlockDeviceMapping = false; + } else if (equalsOrSuffix(qName, "iamInstanceProfile")) { + inIamInstanceProfile = false; } else if (qName.equals("item") && inBlockDeviceMapping) { try { builder.blockDeviceMapping(blockDeviceMappingBuilder.build()); } finally { blockDeviceMappingBuilder.clear(); } + } else if (equalsOrSuffix(qName, "arn") && inIamInstanceProfile) { + builder.iamInstanceProfileArn(currentOrNull()); + } else if (equalsOrSuffix(qName, "name") && inIamInstanceProfile) { + builder.iamInstanceProfileName(currentOrNull()); } else if (qName.equals("deviceName")) { blockDeviceMappingBuilder.deviceName(currentOrNull()); } else if (qName.equals("virtualName")) { diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/binders/BindLaunchSpecificationToFormParamsTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/binders/BindLaunchSpecificationToFormParamsTest.java index bfdd423093..0aa8324c5f 100644 --- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/binders/BindLaunchSpecificationToFormParamsTest.java +++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/binders/BindLaunchSpecificationToFormParamsTest.java @@ -75,4 +75,26 @@ public class BindLaunchSpecificationToFormParamsTest { assertEquals(binder.apply(spec), ImmutableMap.of("LaunchSpecification.InstanceType", "t1.micro", "LaunchSpecification.ImageId", "ami-123", "LaunchSpecification.SubnetId", "subnet-xyz")); } + + @Test + public void testApplyWithIAMInstanceProfileArn() { + LaunchSpecification spec = LaunchSpecification.builder() + .instanceType(InstanceType.T1_MICRO) + .imageId("ami-123") + .iamInstanceProfileArn("arn:aws:iam::123456789012:instance-profile/application_abc/component_xyz/Webserver") + .build(); + + assertEquals(binder.apply(spec), ImmutableMap.of("LaunchSpecification.InstanceType", "t1.micro", + "LaunchSpecification.ImageId", "ami-123", "LaunchSpecification.IamInstanceProfile.Arn", + "arn:aws:iam::123456789012:instance-profile/application_abc/component_xyz/Webserver")); + } + + @Test + public void testApplyWithIAMInstanceProfileName() { + LaunchSpecification spec = LaunchSpecification.builder().instanceType(InstanceType.T1_MICRO).imageId("ami-123") + .iamInstanceProfileName("Webserver").build(); + + assertEquals(binder.apply(spec), ImmutableMap.of("LaunchSpecification.InstanceType", "t1.micro", + "LaunchSpecification.ImageId", "ami-123", "LaunchSpecification.IamInstanceProfile.Name", "Webserver")); + } } diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/AWSEC2ComputeServiceExpectTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/AWSEC2ComputeServiceExpectTest.java index f8fd9ea092..08c4c2ff82 100644 --- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/AWSEC2ComputeServiceExpectTest.java +++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/AWSEC2ComputeServiceExpectTest.java @@ -18,6 +18,7 @@ */ package org.jclouds.aws.ec2.compute; +import static org.jclouds.aws.ec2.compute.AWSEC2TemplateOptions.Builder.blockUntilRunning; import static org.testng.Assert.assertEquals; import javax.ws.rs.core.MediaType; @@ -29,6 +30,7 @@ import org.jclouds.compute.domain.Template; import org.jclouds.compute.predicates.NodePredicates; 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; @@ -43,7 +45,30 @@ import com.google.common.collect.Iterables; @Test(groups = "unit", testName = "AWSEC2ComputeServiceExpectTest") public class AWSEC2ComputeServiceExpectTest extends BaseAWSEC2ComputeServiceExpectTest { - public void testLaunchVPCSpotInstanceMissesVPCId() throws Exception { + private HttpResponse requestSpotInstancesResponse; + private HttpRequest describeSpotInstanceRequest; + private HttpResponse describeSpotInstanceResponse; + + @BeforeClass + @Override + protected void setupDefaultRequests() { + super.setupDefaultRequests(); + requestSpotInstancesResponse = HttpResponse.builder().statusCode(200) + .payload(payloadFromResourceWithContentType( + "/request_spot_instances-ebs.xml", MediaType.APPLICATION_XML)).build(); + + describeSpotInstanceRequest = formSigner.filter(HttpRequest.builder().method("POST") + .endpoint("https://ec2." + region + ".amazonaws.com/") + .addHeader("Host", "ec2." + region + ".amazonaws.com") + .addFormParam("Action", "DescribeSpotInstanceRequests") + .addFormParam("SpotInstanceRequestId.1", "sir-228e6406").build()); + + describeSpotInstanceResponse = HttpResponse.builder().statusCode(200) + .payload(payloadFromResourceWithContentType( + "/request_spot_instances-ebs.xml", MediaType.APPLICATION_XML)).build(); + } + + public void testLaunchVPCSpotInstanceSubnetId() throws Exception { HttpRequest requestSpotInstancesRequest = formSigner.filter(HttpRequest.builder().method("POST") .endpoint("https://ec2." + region + ".amazonaws.com/") .addHeader("Host", "ec2." + region + ".amazonaws.com") @@ -56,20 +81,6 @@ public class AWSEC2ComputeServiceExpectTest extends BaseAWSEC2ComputeServiceExpe .addFormParam("LaunchSpecification.SubnetId", "subnet-xyz") .addFormParam("LaunchSpecification.UserData", "I2Nsb3VkLWNvbmZpZwpyZXBvX3VwZ3JhZGU6IG5vbmUK") .addFormParam("SpotPrice", "1.0").build()); - - HttpResponse requestSpotInstancesResponse = HttpResponse.builder().statusCode(200) - .payload(payloadFromResourceWithContentType( - "/request_spot_instances-ebs.xml", MediaType.APPLICATION_XML)).build(); - - HttpRequest describeSpotInstanceRequest = formSigner.filter(HttpRequest.builder().method("POST") - .endpoint("https://ec2." + region + ".amazonaws.com/") - .addHeader("Host", "ec2." + region + ".amazonaws.com") - .addFormParam("Action", "DescribeSpotInstanceRequests") - .addFormParam("SpotInstanceRequestId.1", "sir-228e6406").build()); - - HttpResponse describeSpotInstanceResponse = HttpResponse.builder().statusCode(200) - .payload(payloadFromResourceWithContentType( - "/request_spot_instances-ebs.xml", MediaType.APPLICATION_XML)).build(); Builder requestResponseMap = ImmutableMap. builder(); requestResponseMap.put(describeRegionsRequest, describeRegionsResponse); @@ -89,10 +100,152 @@ public class AWSEC2ComputeServiceExpectTest extends BaseAWSEC2ComputeServiceExpe template.getOptions().as(AWSEC2TemplateOptions.class).spotPrice(1f).subnetId("subnet-xyz").keyPair("Demo").blockUntilRunning(false); - NodeMetadata node = Iterables.getOnlyElement(createsVPCSpotInstance.createNodesInGroup("demoGroup", 1, template)); + NodeMetadata node = Iterables.getOnlyElement(createsVPCSpotInstance.createNodesInGroup("test", 1, template)); assertEquals(node.getId(), "us-east-1/sir-228e6406"); } - + + String iamInstanceProfileArn = "arn:aws:iam::123456789012:instance-profile/application_abc/component_xyz/Webserver"; + + public void testLaunchSpotInstanceIAMInstanceProfileArn() throws Exception { + HttpRequest requestSpotInstancesRequest = formSigner.filter(HttpRequest.builder().method("POST") + .endpoint("https://ec2." + region + ".amazonaws.com/") + .addHeader("Host", "ec2." + region + ".amazonaws.com") + .addFormParam("Action", "RequestSpotInstances") + .addFormParam("InstanceCount", "1") + .addFormParam("LaunchSpecification.IamInstanceProfile.Arn", iamInstanceProfileArn) + .addFormParam("LaunchSpecification.ImageId", "ami-be3adfd7") + .addFormParam("LaunchSpecification.InstanceType", "m1.small") + .addFormParam("LaunchSpecification.Placement.AvailabilityZone", "us-east-1a") + .addFormParam("LaunchSpecification.SecurityGroup.1", "jclouds#test") + .addFormParam("LaunchSpecification.UserData", "I2Nsb3VkLWNvbmZpZwpyZXBvX3VwZ3JhZGU6IG5vbmUK") + .addFormParam("SpotPrice", "1.0").build()); + + Builder requestResponseMap = ImmutableMap. 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(requestSpotInstancesRequest, requestSpotInstancesResponse); + requestResponseMap.put(describeSpotInstanceRequest, describeSpotInstanceResponse); + + ComputeService createsSpotInstance = requestsSendResponses(requestResponseMap.build()); + + Template template = createsSpotInstance.templateBuilder().locationId("us-east-1a").build(); + + template.getOptions().as(AWSEC2TemplateOptions.class).spotPrice(1f).iamInstanceProfileArn(iamInstanceProfileArn) + .noKeyPair().blockUntilRunning(false); + + NodeMetadata node = Iterables.getOnlyElement(createsSpotInstance.createNodesInGroup("test", 1, template)); + assertEquals(node.getId(), "us-east-1/sir-228e6406"); + } + + public void testLaunchSpotInstanceIAMInstanceProfileName() throws Exception { + HttpRequest requestSpotInstancesRequest = formSigner.filter(HttpRequest.builder().method("POST") + .endpoint("https://ec2." + region + ".amazonaws.com/") + .addHeader("Host", "ec2." + region + ".amazonaws.com") + .addFormParam("Action", "RequestSpotInstances") + .addFormParam("InstanceCount", "1") + .addFormParam("LaunchSpecification.IamInstanceProfile.Name", "Webserver") + .addFormParam("LaunchSpecification.ImageId", "ami-be3adfd7") + .addFormParam("LaunchSpecification.InstanceType", "m1.small") + .addFormParam("LaunchSpecification.Placement.AvailabilityZone", "us-east-1a") + .addFormParam("LaunchSpecification.SecurityGroup.1", "jclouds#test") + .addFormParam("LaunchSpecification.UserData", "I2Nsb3VkLWNvbmZpZwpyZXBvX3VwZ3JhZGU6IG5vbmUK") + .addFormParam("SpotPrice", "1.0").build()); + + Builder requestResponseMap = ImmutableMap. 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(requestSpotInstancesRequest, requestSpotInstancesResponse); + requestResponseMap.put(describeSpotInstanceRequest, describeSpotInstanceResponse); + + ComputeService createsSpotInstance = requestsSendResponses(requestResponseMap.build()); + + Template template = createsSpotInstance.templateBuilder().locationId("us-east-1a").build(); + + template.getOptions().as(AWSEC2TemplateOptions.class).spotPrice(1f).iamInstanceProfileName("Webserver") + .noKeyPair().blockUntilRunning(false); + + NodeMetadata node = Iterables.getOnlyElement(createsSpotInstance.createNodesInGroup("test", 1, template)); + assertEquals(node.getId(), "us-east-1/sir-228e6406"); + } + + public void testCreateNodeWithIAMInstanceProfileArn() throws Exception { + HttpRequest runInstancesRequest = formSigner.filter(HttpRequest.builder().method("POST") + .endpoint("https://ec2." + region + ".amazonaws.com/") + .addHeader("Host", "ec2." + region + ".amazonaws.com") + .addFormParam("Action", "RunInstances") + .addFormParam("IamInstanceProfile.Arn", iamInstanceProfileArn) + .addFormParam("ImageId", "ami-be3adfd7") + .addFormParam("InstanceType", "m1.small") + .addFormParam("MaxCount", "1") + .addFormParam("MinCount", "1") + .addFormParam("SecurityGroup.1", "jclouds#test") + .addFormParam("UserData", "I2Nsb3VkLWNvbmZpZwpyZXBvX3VwZ3JhZGU6IG5vbmUK").build()); + + Builder requestResponseMap = ImmutableMap. 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(runInstancesRequest, runInstancesResponse); + requestResponseMap.put(describeInstanceRequest, describeInstanceResponse); + requestResponseMap.put(describeImageRequest, describeImagesResponse); + + ComputeService apiThatCreatesNode = requestsSendResponses(requestResponseMap.build()); + + NodeMetadata node = Iterables.getOnlyElement(apiThatCreatesNode.createNodesInGroup("test", 1, + blockUntilRunning(false).iamInstanceProfileArn(iamInstanceProfileArn).noKeyPair())); + assertEquals(node.getId(), "us-east-1/i-2baa5550"); + } + + public void testCreateNodeWithIAMInstanceProfileName() throws Exception { + HttpRequest runInstancesRequest = formSigner.filter(HttpRequest.builder().method("POST") + .endpoint("https://ec2." + region + ".amazonaws.com/") + .addHeader("Host", "ec2." + region + ".amazonaws.com") + .addFormParam("Action", "RunInstances") + .addFormParam("IamInstanceProfile.Name", "Webserver") + .addFormParam("ImageId", "ami-be3adfd7") + .addFormParam("InstanceType", "m1.small") + .addFormParam("MaxCount", "1") + .addFormParam("MinCount", "1") + .addFormParam("SecurityGroup.1", "jclouds#test") + .addFormParam("UserData", "I2Nsb3VkLWNvbmZpZwpyZXBvX3VwZ3JhZGU6IG5vbmUK").build()); + + Builder requestResponseMap = ImmutableMap. 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(runInstancesRequest, runInstancesResponse); + requestResponseMap.put(describeInstanceRequest, describeInstanceResponse); + requestResponseMap.put(describeImageRequest, describeImagesResponse); + + ComputeService apiThatCreatesNode = requestsSendResponses(requestResponseMap.build()); + + NodeMetadata node = Iterables.getOnlyElement(apiThatCreatesNode.createNodesInGroup("test", 1, + blockUntilRunning(false).iamInstanceProfileName("Webserver").noKeyPair())); + assertEquals(node.getId(), "us-east-1/i-2baa5550"); + } + public void testListNodesWhereImageDoesntExist() throws Exception { HttpRequest describeInstancesRequest = formSigner.filter(HttpRequest.builder().method("POST") .endpoint("https://ec2." + region + ".amazonaws.com/") diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/AWSEC2ComputeServiceLiveTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/AWSEC2ComputeServiceLiveTest.java index ded151b904..1f8df6f74d 100644 --- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/AWSEC2ComputeServiceLiveTest.java +++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/AWSEC2ComputeServiceLiveTest.java @@ -20,6 +20,7 @@ package org.jclouds.aws.ec2.compute; import static com.google.common.collect.Iterables.getOnlyElement; import static com.google.common.collect.Sets.newTreeSet; +import static java.util.logging.Logger.getAnonymousLogger; import static org.jclouds.compute.domain.OsFamily.AMZN_LINUX; import static org.jclouds.compute.options.RunScriptOptions.Builder.runAsRoot; import static org.jclouds.ec2.util.IpPermissions.permit; @@ -36,6 +37,7 @@ import org.jclouds.aws.ec2.AWSEC2Client; import org.jclouds.aws.ec2.domain.AWSRunningInstance; import org.jclouds.aws.ec2.domain.MonitoringState; import org.jclouds.aws.ec2.services.AWSSecurityGroupClient; +import org.jclouds.aws.iam.AWSIAMProviderMetadata; import org.jclouds.cloudwatch.CloudWatchApi; import org.jclouds.cloudwatch.CloudWatchAsyncApi; import org.jclouds.cloudwatch.domain.Dimension; @@ -56,6 +58,9 @@ import org.jclouds.ec2.domain.KeyPair; import org.jclouds.ec2.domain.SecurityGroup; import org.jclouds.ec2.services.InstanceClient; import org.jclouds.ec2.services.KeyPairClient; +import org.jclouds.iam.IAMApi; +import org.jclouds.iam.IAMAsyncApi; +import org.jclouds.iam.domain.InstanceProfile; import org.jclouds.rest.RestContext; import org.jclouds.scriptbuilder.domain.Statements; import org.testng.annotations.Test; @@ -82,6 +87,13 @@ public class AWSEC2ComputeServiceLiveTest extends EC2ComputeServiceLiveTest { public void testExtendedOptionsAndLogin() throws Exception { String region = "us-west-2"; + RestContext iamContext = ContextBuilder.newBuilder(new AWSIAMProviderMetadata()) + .credentials(identity, credential) + .modules(setupModules()).build(); + String profileName = "ec2Test"; + // Note this needs to wait an undefined amount of time before it is visible to ec2, seems to be about 15seconds + InstanceProfile profile = createIAMInstanceProfile(iamContext.getApi(), profileName); + AWSSecurityGroupClient securityGroupClient = AWSEC2Client.class.cast( view.unwrap(AWSEC2ApiMetadata.CONTEXT_TOKEN).getApi()).getSecurityGroupServices(); @@ -106,6 +118,7 @@ public class AWSEC2ComputeServiceLiveTest extends EC2ComputeServiceLiveTest { template.getOptions().tags(tags); template.getOptions().as(AWSEC2TemplateOptions.class).enableMonitoring(); template.getOptions().as(AWSEC2TemplateOptions.class).spotPrice(0.3f); + template.getOptions().as(AWSEC2TemplateOptions.class).iamInstanceProfileName(profileName); String startedId = null; try { @@ -146,6 +159,7 @@ public class AWSEC2ComputeServiceLiveTest extends EC2ComputeServiceLiveTest { assertEquals(instance.getKeyName(), group); assert instance.getSpotInstanceRequestId() != null; assertEquals(instance.getMonitoringState(), MonitoringState.ENABLED); + assertEquals(instance.getIAMInstanceProfile().get().getArn(), profile.getArn()); // generate some load ListenableFuture future = client.submitScriptOnNode(first.getId(), Statements @@ -199,9 +213,31 @@ public class AWSEC2ComputeServiceLiveTest extends EC2ComputeServiceLiveTest { assertEquals(keyPairClient.describeKeyPairsInRegion(region, group).size(), 1); assertEquals(securityGroupClient.describeSecurityGroupsInRegion(region, group).size(), 1); } + deleteIAMInstanceProfile(iamContext.getApi(), profileName); + iamContext.close(); cleanupExtendedStuffInRegion(region, securityGroupClient, keyPairClient, group); } - } + static String assumeRolePolicy = "{\"Version\":\"2008-10-17\",\"Statement\":[{\"Sid\":\"\",\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"ec2.amazonaws.com\"},\"Action\":\"sts:AssumeRole\"}]}"; + static String route53Policy = "{\"Statement\":[{\"Effect\":\"Allow\",\"Action\":\"route53:*\",\"Resource\":\"*\"}]}"; + + static InstanceProfile createIAMInstanceProfile(IAMApi api, String name) { + String roleName = name + "route53"; + api.getRoleApi().createWithPolicy(roleName, assumeRolePolicy); + api.getPolicyApiForRole(roleName).create("route53-readonly", route53Policy); + api.getInstanceProfileApi().create(name); + api.getInstanceProfileApi().addRole(name, roleName); + InstanceProfile updated = api.getInstanceProfileApi().get(name); + getAnonymousLogger().info("created instanceProfile: " + updated); + return updated; + } + + static void deleteIAMInstanceProfile(IAMApi api, String name) { + String roleName = name + "route53"; + api.getInstanceProfileApi().removeRole(name, roleName); + api.getPolicyApiForRole(roleName).delete("route53-readonly"); + api.getRoleApi().delete(roleName); + api.getInstanceProfileApi().delete(name); + } } diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/options/AWSEC2TemplateOptionsTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/options/AWSEC2TemplateOptionsTest.java index 669f4eb316..1a919a1895 100644 --- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/options/AWSEC2TemplateOptionsTest.java +++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/options/AWSEC2TemplateOptionsTest.java @@ -21,6 +21,8 @@ package org.jclouds.aws.ec2.compute.options; import static org.jclouds.aws.ec2.compute.AWSEC2TemplateOptions.Builder.authorizePublicKey; import static org.jclouds.aws.ec2.compute.AWSEC2TemplateOptions.Builder.blockOnPort; import static org.jclouds.aws.ec2.compute.AWSEC2TemplateOptions.Builder.enableMonitoring; +import static org.jclouds.aws.ec2.compute.AWSEC2TemplateOptions.Builder.iamInstanceProfileArn; +import static org.jclouds.aws.ec2.compute.AWSEC2TemplateOptions.Builder.iamInstanceProfileName; import static org.jclouds.aws.ec2.compute.AWSEC2TemplateOptions.Builder.inboundPorts; import static org.jclouds.aws.ec2.compute.AWSEC2TemplateOptions.Builder.installPrivateKey; import static org.jclouds.aws.ec2.compute.AWSEC2TemplateOptions.Builder.keyPair; @@ -367,4 +369,52 @@ public class AWSEC2TemplateOptionsTest { assertEquals(options.getInboundPorts()[0], 22); assertEquals(options.getInboundPorts()[1], 30); } + + @Test + public void testIAMInstanceProfileArn() { + AWSEC2TemplateOptions options = new AWSEC2TemplateOptions(); + options.iamInstanceProfileArn("arn:aws:iam::123456789012:instance-profile/application_abc/component_xyz/Webserver"); + assertEquals(options.getIAMInstanceProfileArn(), "arn:aws:iam::123456789012:instance-profile/application_abc/component_xyz/Webserver"); + } + + @Test + public void testNullIAMInstanceProfileArn() { + AWSEC2TemplateOptions options = new AWSEC2TemplateOptions(); + assertEquals(options.getIAMInstanceProfileArn(), null); + } + + @Test + public void testIAMInstanceProfileArnStatic() { + AWSEC2TemplateOptions options = iamInstanceProfileArn("arn:aws:iam::123456789012:instance-profile/application_abc/component_xyz/Webserver"); + assertEquals(options.getIAMInstanceProfileArn(), "arn:aws:iam::123456789012:instance-profile/application_abc/component_xyz/Webserver"); + } + + @Test(expectedExceptions = NullPointerException.class) + public void testIAMInstanceProfileArnNPE() { + iamInstanceProfileArn(null); + } + + @Test + public void testIAMInstanceProfileName() { + AWSEC2TemplateOptions options = new AWSEC2TemplateOptions(); + options.iamInstanceProfileName("Webserver"); + assertEquals(options.getIAMInstanceProfileName(), "Webserver"); + } + + @Test + public void testNullIAMInstanceProfileName() { + AWSEC2TemplateOptions options = new AWSEC2TemplateOptions(); + assertEquals(options.getIAMInstanceProfileName(), null); + } + + @Test + public void testIAMInstanceProfileNameStatic() { + AWSEC2TemplateOptions options = iamInstanceProfileName("Webserver"); + assertEquals(options.getIAMInstanceProfileName(), "Webserver"); + } + + @Test(expectedExceptions = NullPointerException.class) + public void testIAMInstanceProfileNameNPE() { + iamInstanceProfileName(null); + } } diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/options/AWSRunInstancesOptionsTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/options/AWSRunInstancesOptionsTest.java index 5114881821..9a704c6cae 100644 --- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/options/AWSRunInstancesOptionsTest.java +++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/options/AWSRunInstancesOptionsTest.java @@ -21,6 +21,8 @@ package org.jclouds.aws.ec2.options; import static org.jclouds.aws.ec2.options.AWSRunInstancesOptions.Builder.asType; import static org.jclouds.aws.ec2.options.AWSRunInstancesOptions.Builder.enableMonitoring; import static org.jclouds.aws.ec2.options.AWSRunInstancesOptions.Builder.withBlockDeviceMappings; +import static org.jclouds.aws.ec2.options.AWSRunInstancesOptions.Builder.withIAMInstanceProfileArn; +import static org.jclouds.aws.ec2.options.AWSRunInstancesOptions.Builder.withIAMInstanceProfileName; import static org.jclouds.aws.ec2.options.AWSRunInstancesOptions.Builder.withKernelId; import static org.jclouds.aws.ec2.options.AWSRunInstancesOptions.Builder.withKeyName; import static org.jclouds.aws.ec2.options.AWSRunInstancesOptions.Builder.withRamdisk; @@ -250,6 +252,57 @@ public class AWSRunInstancesOptionsTest { withSubnetId(null); } + @Test + public void testWithIAMInstanceProfileArn() { + AWSRunInstancesOptions options = new AWSRunInstancesOptions(); + options + .withIAMInstanceProfileArn("arn:aws:iam::123456789012:instance-profile/application_abc/component_xyz/Webserver"); + assertEquals(options.buildFormParameters().get("IamInstanceProfile.Arn"), + ImmutableList.of("arn:aws:iam::123456789012:instance-profile/application_abc/component_xyz/Webserver")); + } + + @Test + public void testNullWithIAMInstanceProfileArn() { + AWSRunInstancesOptions options = new AWSRunInstancesOptions(); + assertEquals(options.buildFormParameters().get("IamInstanceProfile.Arn"), ImmutableList.of()); + } + + @Test + public void testWithIAMInstanceProfileArnStatic() { + AWSRunInstancesOptions options = withIAMInstanceProfileArn("arn:aws:iam::123456789012:instance-profile/application_abc/component_xyz/Webserver"); + assertEquals(options.buildFormParameters().get("IamInstanceProfile.Arn"), + ImmutableList.of("arn:aws:iam::123456789012:instance-profile/application_abc/component_xyz/Webserver")); + } + + @Test(expectedExceptions = NullPointerException.class) + public void testWithIAMInstanceProfileArnNPE() { + withIAMInstanceProfileArn(null); + } + + @Test + public void testWithIAMInstanceProfileName() { + AWSRunInstancesOptions options = new AWSRunInstancesOptions(); + options.withIAMInstanceProfileName("Webserver"); + assertEquals(options.buildFormParameters().get("IamInstanceProfile.Name"), ImmutableList.of("Webserver")); + } + + @Test + public void testNullWithIAMInstanceProfileName() { + AWSRunInstancesOptions options = new AWSRunInstancesOptions(); + assertEquals(options.buildFormParameters().get("IamInstanceProfile.Name"), ImmutableList.of()); + } + + @Test + public void testWithIAMInstanceProfileNameStatic() { + AWSRunInstancesOptions options = withIAMInstanceProfileName("Webserver"); + assertEquals(options.buildFormParameters().get("IamInstanceProfile.Name"), ImmutableList.of("Webserver")); + } + + @Test(expectedExceptions = NullPointerException.class) + public void testWithIAMInstanceProfileNameNPE() { + withIAMInstanceProfileName(null); + } + @Test public void testWithRamdisk() { AWSRunInstancesOptions options = new AWSRunInstancesOptions(); diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/xml/AWSDescribeInstancesResponseHandlerTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/xml/AWSDescribeInstancesResponseHandlerTest.java index c247bf23a0..10ddc1c8fe 100644 --- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/xml/AWSDescribeInstancesResponseHandlerTest.java +++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/xml/AWSDescribeInstancesResponseHandlerTest.java @@ -130,7 +130,10 @@ public class AWSDescribeInstancesResponseHandlerTest extends BaseEC2HandlerTest new BlockDevice("vol-5029fc3a", Attachment.Status.ATTACHED, dateService .iso8601DateParse("2011-08-16T13:41:19.000Z"), true)) .hypervisor(Hypervisor.XEN) - .virtualizationType("paravirtual").build()).build()); + .virtualizationType("paravirtual") + .iamInstanceProfileArn("arn:aws:iam::123456789012:instance-profile/application_abc/component_xyz/Webserver") + .iamInstanceProfileId("AIPAD5ARO2C5EXAMPLE3G") + .build()).build()); Set> result = parseAWSRunningInstances("/describe_instances_latest.xml"); diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/xml/SpotInstanceHandlerTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/xml/SpotInstanceHandlerTest.java index 9e88b4fbee..7f85c9a3dc 100644 --- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/xml/SpotInstanceHandlerTest.java +++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/xml/SpotInstanceHandlerTest.java @@ -85,7 +85,9 @@ public class SpotInstanceHandlerTest extends BaseEC2HandlerTest { LaunchSpecification.builder().imageId("ami-595a0a1c").securityGroupIdToName("sg-83e1c4ea", "default") .instanceType("m1.large").mapNewVolumeToDevice("/dev/sda1", 1, true) .mapEBSSnapshotToDevice("/dev/sda2", "snap-1ea27576", 1, true) - .mapEphemeralDeviceToDevice("/dev/sda3", "vre1").monitoringEnabled(false).build()) + .mapEphemeralDeviceToDevice("/dev/sda3", "vre1").monitoringEnabled(false) + .iamInstanceProfileArn("arn:aws:iam::123456789012:instance-profile/application_abc/component_xyz/Webserver") + .iamInstanceProfileName("Webserver").build()) .createTime(new SimpleDateFormatDateService().iso8601DateParse("2011-03-08T03:30:36.000Z")) .productDescription("Linux/UNIX").build(); SpotInstanceHandler handler = injector.getInstance(SpotInstanceHandler.class); @@ -94,7 +96,9 @@ public class SpotInstanceHandlerTest extends BaseEC2HandlerTest { assertEquals(result.toString(), expected.toString()); assertEquals(result.getState(), State.OPEN); assertEquals(result.getRawState(), "open"); - + assertEquals(result.getLaunchSpecification().getIAMInstanceProfile().get().getArn().get(), + "arn:aws:iam::123456789012:instance-profile/application_abc/component_xyz/Webserver"); + assertEquals(result.getLaunchSpecification().getIAMInstanceProfile().get().getName().get(), "Webserver"); } public void testApplyInputStream1() { diff --git a/providers/aws-ec2/src/test/resources/describe_instances_1.xml b/providers/aws-ec2/src/test/resources/describe_instances_1.xml index 4b9f840ee3..5d6bf687eb 100644 --- a/providers/aws-ec2/src/test/resources/describe_instances_1.xml +++ b/providers/aws-ec2/src/test/resources/describe_instances_1.xml @@ -1,4 +1,4 @@ - + b3e1c7ee-1f34-4582-9493-695c9425c679 diff --git a/providers/aws-ec2/src/test/resources/describe_instances_2.xml b/providers/aws-ec2/src/test/resources/describe_instances_2.xml index 2974fae21d..380a5bfbcc 100644 --- a/providers/aws-ec2/src/test/resources/describe_instances_2.xml +++ b/providers/aws-ec2/src/test/resources/describe_instances_2.xml @@ -1,4 +1,4 @@ - + b2238f71-750f-4eed-8f5c-eb4e6f66b687 diff --git a/providers/aws-ec2/src/test/resources/describe_instances_3.xml b/providers/aws-ec2/src/test/resources/describe_instances_3.xml index d80627182c..1e15ce9eb5 100644 --- a/providers/aws-ec2/src/test/resources/describe_instances_3.xml +++ b/providers/aws-ec2/src/test/resources/describe_instances_3.xml @@ -1,5 +1,5 @@ - + 440faed2-0331-488d-a04d-d8c9aba85307 diff --git a/providers/aws-ec2/src/test/resources/describe_instances_latest.xml b/providers/aws-ec2/src/test/resources/describe_instances_latest.xml index f082f23afc..9d606b8db0 100644 --- a/providers/aws-ec2/src/test/resources/describe_instances_latest.xml +++ b/providers/aws-ec2/src/test/resources/describe_instances_latest.xml @@ -1,5 +1,5 @@ - + a03c1896-0543-485f-a732-ebc83873a3ca @@ -70,7 +70,7 @@ Empty - + i-931444f2 @@ -121,6 +121,10 @@ paravirtual xen + + arn:aws:iam::123456789012:instance-profile/application_abc/component_xyz/Webserver + AIPAD5ARO2C5EXAMPLE3G + diff --git a/providers/aws-ec2/src/test/resources/describe_instances_pending.xml b/providers/aws-ec2/src/test/resources/describe_instances_pending.xml index 92f9f4ebe4..0013106a57 100644 --- a/providers/aws-ec2/src/test/resources/describe_instances_pending.xml +++ b/providers/aws-ec2/src/test/resources/describe_instances_pending.xml @@ -1,5 +1,5 @@ - + dcd37ecf-e5b6-462b-99a8-112427b3e3a2 diff --git a/providers/aws-ec2/src/test/resources/describe_security_groups_vpc.xml b/providers/aws-ec2/src/test/resources/describe_security_groups_vpc.xml index cd635b4e71..e444aa51d6 100644 --- a/providers/aws-ec2/src/test/resources/describe_security_groups_vpc.xml +++ b/providers/aws-ec2/src/test/resources/describe_security_groups_vpc.xml @@ -1,4 +1,4 @@ - + xxxxxxxxxxxxxxxx diff --git a/providers/aws-ec2/src/test/resources/describe_spot_instance.xml b/providers/aws-ec2/src/test/resources/describe_spot_instance.xml index a3f158fdbd..964b246b81 100644 --- a/providers/aws-ec2/src/test/resources/describe_spot_instance.xml +++ b/providers/aws-ec2/src/test/resources/describe_spot_instance.xml @@ -1,5 +1,5 @@ - + d9da716a-5cd4-492e-83b9-6777ac16d6cf diff --git a/providers/aws-ec2/src/test/resources/describe_spot_instance_requests.xml b/providers/aws-ec2/src/test/resources/describe_spot_instance_requests.xml index eb21f31477..e6598ee808 100644 --- a/providers/aws-ec2/src/test/resources/describe_spot_instance_requests.xml +++ b/providers/aws-ec2/src/test/resources/describe_spot_instance_requests.xml @@ -1,4 +1,4 @@ - + 7c4dd2bd-106d-4cd3-987c-35ee819180a6 diff --git a/providers/aws-ec2/src/test/resources/describe_spot_instance_tags.xml b/providers/aws-ec2/src/test/resources/describe_spot_instance_tags.xml index 8c1a22e484..2b1a1a1c8b 100644 --- a/providers/aws-ec2/src/test/resources/describe_spot_instance_tags.xml +++ b/providers/aws-ec2/src/test/resources/describe_spot_instance_tags.xml @@ -1,5 +1,5 @@ - + f2247378-7df0-4725-b55f-8ef58b557dcd diff --git a/providers/aws-ec2/src/test/resources/describe_spot_instances_1.xml b/providers/aws-ec2/src/test/resources/describe_spot_instances_1.xml index 5fc3f34bbd..5cb54baf7b 100644 --- a/providers/aws-ec2/src/test/resources/describe_spot_instances_1.xml +++ b/providers/aws-ec2/src/test/resources/describe_spot_instances_1.xml @@ -1,5 +1,5 @@ - + f2247378-7df0-4725-b55f-8ef58b557dcd diff --git a/providers/aws-ec2/src/test/resources/describe_spot_price_history.xml b/providers/aws-ec2/src/test/resources/describe_spot_price_history.xml index 27545a6aae..b71570a645 100644 --- a/providers/aws-ec2/src/test/resources/describe_spot_price_history.xml +++ b/providers/aws-ec2/src/test/resources/describe_spot_price_history.xml @@ -1,5 +1,5 @@ - + 99777a75-2a2b-4296-a305-650c442d2d63 diff --git a/providers/aws-ec2/src/test/resources/request_spot_instances-ebs.xml b/providers/aws-ec2/src/test/resources/request_spot_instances-ebs.xml index ddc60ccf8f..7f64608823 100644 --- a/providers/aws-ec2/src/test/resources/request_spot_instances-ebs.xml +++ b/providers/aws-ec2/src/test/resources/request_spot_instances-ebs.xml @@ -1,5 +1,5 @@ - + 02401e8e-a4f5-4285-8ea8-6d742fbaadd8 @@ -40,6 +40,10 @@ false + + arn:aws:iam::123456789012:instance-profile/application_abc/component_xyz/Webserver + Webserver + 2011-03-08T03:30:36.000Z Linux/UNIX diff --git a/providers/aws-ec2/src/test/resources/request_spot_instances.xml b/providers/aws-ec2/src/test/resources/request_spot_instances.xml index f1f11ccd9b..deca9e55f7 100644 --- a/providers/aws-ec2/src/test/resources/request_spot_instances.xml +++ b/providers/aws-ec2/src/test/resources/request_spot_instances.xml @@ -1,5 +1,5 @@ - + 2ffc645f-6835-4d23-bd18-f6f53c253067 diff --git a/providers/aws-ec2/src/test/resources/run_instances_1.xml b/providers/aws-ec2/src/test/resources/run_instances_1.xml index 88f39b441f..50ff562bad 100644 --- a/providers/aws-ec2/src/test/resources/run_instances_1.xml +++ b/providers/aws-ec2/src/test/resources/run_instances_1.xml @@ -1,5 +1,5 @@ - + 7faf9500-67ef-484b-9fa5-73b6df638bc8 r-d3b815bc 993194456877