From bd4f5cfba2d34a6e995e1c29cffc827979961cff Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Tue, 1 Jan 2013 18:48:22 -0800 Subject: [PATCH] moved off deprecated TagClient -> TagApi; refactored tagging to occur at end of run --- .../java/org/jclouds/ec2/EC2ApiMetadata.java | 2 + .../ec2/compute/EC2ComputeService.java | 93 +++++++-- .../ec2/compute/domain/RegionAndName.java | 79 ++++++-- .../compute/functions/PresentInstances.java | 84 ++++++++ .../RunningInstanceToNodeMetadata.java | 10 +- .../compute/options/EC2TemplateOptions.java | 4 +- .../compute/predicates/InstancePresent.java | 70 ------- .../EC2CreateNodesInGroupThenAddToSet.java | 188 +++++++++--------- .../jclouds/ec2/domain/RunningInstance.java | 182 ++++++++++------- .../jclouds/ec2/reference/EC2Constants.java | 9 + .../main/java/org/jclouds/ec2/util/Tags.java | 103 ++++++++++ .../ec2/xml/BaseReservationHandler.java | 35 ++-- .../xml/DescribeInstancesResponseHandler.java | 57 +++++- .../ec2/xml/RunInstancesResponseHandler.java | 6 +- .../org/jclouds/ec2/xml/TagSetHandler.java | 80 ++++++++ .../compute/EC2ComputeServiceLiveTest.java | 11 +- .../functions/PresentInstancesTest.java | 70 +++++++ ...EC2CreateNodesInGroupThenAddToSetTest.java | 30 +-- .../java/org/jclouds/ec2/util/TagsTest.java | 57 ++++++ .../DescribeInstancesResponseHandlerTest.java | 10 +- .../xml/RunInstancesResponseHandlerTest.java | 8 +- .../src/test/resources/describe_instances.xml | 10 + .../jclouds/aws/ec2/AWSEC2ApiMetadata.java | 3 - .../jclouds/aws/ec2/AWSEC2AsyncClient.java | 7 - .../org/jclouds/aws/ec2/AWSEC2Client.java | 7 - .../aws/ec2/compute/AWSEC2ComputeService.java | 137 ++++--------- .../AWSEC2ComputeServiceContextModule.java | 8 +- .../AWSRunningInstanceToNodeMetadata.java | 11 +- .../PresentSpotRequestsAndInstances.java | 96 +++++++++ .../predicates/AWSEC2InstancePresent.java | 55 ----- .../AWSEC2CreateNodesInGroupThenAddToSet.java | 82 +++----- .../ec2/config/AWSEC2RestClientModule.java | 23 +-- .../aws/ec2/domain/AWSRunningInstance.java | 177 +++-------------- ...otInstanceRequestToAWSRunningInstance.java | 3 - .../aws/ec2/reference/AWSEC2Constants.java | 5 - .../aws/ec2/services/TagAsyncClient.java | 97 --------- .../jclouds/aws/ec2/services/TagClient.java | 86 -------- .../AWSDescribeInstancesResponseHandler.java | 46 +++-- .../xml/AWSRunInstancesResponseHandler.java | 7 +- .../ec2/xml/BaseAWSReservationHandler.java | 19 +- .../ec2/xml/DescribeTagsResponseHandler.java | 73 ------- .../aws/ec2/xml/SpotInstanceHandler.java | 7 +- .../org/jclouds/aws/ec2/xml/TagHandler.java | 58 ------ .../jclouds/aws/ec2/xml/TagSetHandler.java | 85 -------- .../aws/ec2/AWSEC2AsyncClientTest.java | 2 - .../compute/AWSEC2ComputeServiceLiveTest.java | 14 +- ...cidentalResourcesGetCleanedUpLiveTest.java | 168 ---------------- .../PresentSpotRequestsAndInstancesTest.java | 101 ++++++++++ .../aws/ec2/services/TagAsyncClientTest.java | 108 ---------- .../aws/ec2/services/TagClientLiveTest.java | 118 ----------- 50 files changed, 1188 insertions(+), 1613 deletions(-) create mode 100644 apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/PresentInstances.java delete mode 100644 apis/ec2/src/main/java/org/jclouds/ec2/compute/predicates/InstancePresent.java create mode 100644 apis/ec2/src/main/java/org/jclouds/ec2/util/Tags.java create mode 100644 apis/ec2/src/main/java/org/jclouds/ec2/xml/TagSetHandler.java create mode 100644 apis/ec2/src/test/java/org/jclouds/ec2/compute/functions/PresentInstancesTest.java create mode 100644 apis/ec2/src/test/java/org/jclouds/ec2/util/TagsTest.java create mode 100644 providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/functions/PresentSpotRequestsAndInstances.java delete mode 100644 providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/predicates/AWSEC2InstancePresent.java delete mode 100644 providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/TagAsyncClient.java delete mode 100644 providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/TagClient.java delete mode 100644 providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/xml/DescribeTagsResponseHandler.java delete mode 100644 providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/xml/TagHandler.java delete mode 100644 providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/xml/TagSetHandler.java delete mode 100644 providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/IncidentalResourcesGetCleanedUpLiveTest.java create mode 100644 providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/functions/PresentSpotRequestsAndInstancesTest.java delete mode 100644 providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/TagAsyncClientTest.java delete mode 100644 providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/TagClientLiveTest.java diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/EC2ApiMetadata.java b/apis/ec2/src/main/java/org/jclouds/ec2/EC2ApiMetadata.java index 6284c1f516..7aa655bd9f 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/EC2ApiMetadata.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/EC2ApiMetadata.java @@ -25,6 +25,7 @@ import static org.jclouds.aws.reference.AWSConstants.PROPERTY_HEADER_TAG; import static org.jclouds.compute.config.ComputeServiceProperties.RESOURCENAME_DELIMITER; import static org.jclouds.ec2.reference.EC2Constants.PROPERTY_EC2_AMI_OWNERS; import static org.jclouds.ec2.reference.EC2Constants.PROPERTY_EC2_AUTO_ALLOCATE_ELASTIC_IPS; +import static org.jclouds.ec2.reference.EC2Constants.PROPERTY_EC2_GENERATE_INSTANCE_NAMES; import static org.jclouds.ec2.reference.EC2Constants.PROPERTY_EC2_TIMEOUT_SECURITYGROUP_PRESENT; import java.net.URI; @@ -88,6 +89,7 @@ public class EC2ApiMetadata extends BaseRestApiMetadata { properties.setProperty(PROPERTY_EC2_TIMEOUT_SECURITYGROUP_PRESENT, "500"); properties.setProperty(PROPERTY_EC2_AUTO_ALLOCATE_ELASTIC_IPS, "false"); properties.setProperty(RESOURCENAME_DELIMITER, "#"); + properties.setProperty(PROPERTY_EC2_GENERATE_INSTANCE_NAMES, "true"); return properties; } diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/EC2ComputeService.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/EC2ComputeService.java index fd286b344b..b3a5e72a6c 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/EC2ComputeService.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/EC2ComputeService.java @@ -24,11 +24,15 @@ import static org.jclouds.compute.config.ComputeServiceProperties.RESOURCENAME_D import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_RUNNING; import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_SUSPENDED; import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_TERMINATED; +import static org.jclouds.compute.util.ComputeServiceUtils.addMetadataAndParseTagsFromValuesOfEmptyString; +import static org.jclouds.compute.util.ComputeServiceUtils.metadataAndTagsAsValuesOfEmptyString; +import static org.jclouds.ec2.reference.EC2Constants.PROPERTY_EC2_GENERATE_INSTANCE_NAMES; +import static org.jclouds.ec2.util.Tags.resourceToTagsAsMap; import static org.jclouds.util.Preconditions2.checkNotEmpty; import java.util.Map; -import java.util.Set; import java.util.Map.Entry; +import java.util.Set; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.atomic.AtomicReference; @@ -41,10 +45,13 @@ import org.jclouds.Constants; import org.jclouds.aws.util.AWSUtils; import org.jclouds.collect.Memoized; import org.jclouds.compute.ComputeServiceContext; +import org.jclouds.compute.RunNodesException; import org.jclouds.compute.callables.RunScriptOnNode; import org.jclouds.compute.domain.Hardware; import org.jclouds.compute.domain.Image; import org.jclouds.compute.domain.NodeMetadata; +import org.jclouds.compute.domain.NodeMetadataBuilder; +import org.jclouds.compute.domain.Template; import org.jclouds.compute.domain.TemplateBuilder; import org.jclouds.compute.extensions.ImageExtension; import org.jclouds.compute.functions.GroupNamingConvention; @@ -70,6 +77,8 @@ import org.jclouds.ec2.compute.domain.RegionNameAndIngressRules; import org.jclouds.ec2.compute.options.EC2TemplateOptions; import org.jclouds.ec2.domain.KeyPair; import org.jclouds.ec2.domain.RunningInstance; +import org.jclouds.ec2.domain.Tag; +import org.jclouds.ec2.util.TagFilterBuilder; import org.jclouds.predicates.Retryables; import org.jclouds.scriptbuilder.functions.InitAdminAccess; @@ -79,9 +88,13 @@ import com.google.common.base.Optional; import com.google.common.base.Predicate; import com.google.common.base.Supplier; import com.google.common.cache.LoadingCache; +import com.google.common.collect.FluentIterable; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMultimap; -import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableMultimap.Builder; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; import com.google.inject.Inject; /** @@ -89,10 +102,11 @@ import com.google.inject.Inject; */ @Singleton public class EC2ComputeService extends BaseComputeService { - private final EC2Client ec2Client; + private final EC2Client client; private final ConcurrentMap credentialsMap; private final LoadingCache securityGroupMap; private final Factory namingConvention; + private final boolean generateInstanceNames; @Inject protected EC2ComputeService(ComputeServiceContext context, Map credentialStore, @@ -109,19 +123,74 @@ public class EC2ComputeService extends BaseComputeService { InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory, RunScriptOnNode.Factory runScriptOnNodeFactory, InitAdminAccess initAdminAccess, PersistNodeCredentials persistNodeCredentials, Timeouts timeouts, - @Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor, EC2Client ec2Client, + @Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor, EC2Client client, ConcurrentMap credentialsMap, @Named("SECURITY") LoadingCache securityGroupMap, - Optional imageExtension, GroupNamingConvention.Factory namingConvention) { + Optional imageExtension, GroupNamingConvention.Factory namingConvention, + @Named(PROPERTY_EC2_GENERATE_INSTANCE_NAMES) boolean generateInstanceNames) { super(context, credentialStore, images, sizes, locations, listNodesStrategy, getImageStrategy, getNodeMetadataStrategy, runNodesAndAddToSetStrategy, rebootNodeStrategy, destroyNodeStrategy, startNodeStrategy, stopNodeStrategy, templateBuilderProvider, templateOptionsProvider, nodeRunning, nodeTerminated, nodeSuspended, initScriptRunnerFactory, initAdminAccess, runScriptOnNodeFactory, persistNodeCredentials, timeouts, executor, imageExtension); - this.ec2Client = ec2Client; + this.client = client; this.credentialsMap = credentialsMap; this.securityGroupMap = securityGroupMap; this.namingConvention = namingConvention; + this.generateInstanceNames = generateInstanceNames; + } + + @Override + public Set createNodesInGroup(String group, int count, final Template template) + throws RunNodesException { + Set nodes = super.createNodesInGroup(group, count, template); + String region = AWSUtils.getRegionFromLocationOrNull(template.getLocation()); + if (client.getTagApiForRegion(region).isPresent()) { + Map common = metadataAndTagsAsValuesOfEmptyString(template.getOptions()); + if (common.size() > 0 || generateInstanceNames) { + return addTagsToInstancesInRegion(common, nodes, region, group); + } + } + return nodes; + } + + private static final Function instanceId = new Function() { + @Override + public String apply(NodeMetadata in) { + return in.getProviderId(); + } + }; + + private Set addTagsToInstancesInRegion(Map common, Set input, + String region, String group) { + Map instancesById = Maps.uniqueIndex(input, instanceId); + ImmutableSet.Builder builder = ImmutableSet. builder(); + if (generateInstanceNames && !common.containsKey("Name")) { + for (String id : instancesById.keySet()) { + Map tags = ImmutableMap. builder().putAll(common) + .put("Name", id.replaceAll(".*-", group + "-")).build(); + logger.debug(">> applying tags %s to instance %s in region %s", tags, id, region); + client.getTagApiForRegion(region).get().applyToResources(tags, ImmutableSet.of(id)); + builder.add(addTagsForInstance(tags, instancesById.get(id))); + } + } else { + Iterable ids = instancesById.keySet(); + logger.debug(">> applying tags %s to instances %s in region %s", common, ids, region); + client.getTagApiForRegion(region).get().applyToResources(common, ids); + for (NodeMetadata in : input) + builder.add(addTagsForInstance(common, in)); + } + if (logger.isDebugEnabled()) { + Multimap filter = new TagFilterBuilder().resourceIds(instancesById.keySet()).build(); + FluentIterable tags = client.getTagApiForRegion(region).get().filter(filter); + logger.debug("<< applied tags in region %s: %s", region, resourceToTagsAsMap(tags)); + } + return builder.build(); + } + + private static NodeMetadata addTagsForInstance(Map tags, NodeMetadata input) { + NodeMetadataBuilder builder = NodeMetadataBuilder.fromNodeMetadata(input).name(tags.get("Name")); + return addMetadataAndParseTagsFromValuesOfEmptyString(builder, tags).build(); } @Inject(optional = true) @@ -137,9 +206,9 @@ public class EC2ComputeService extends BaseComputeService { checkNotEmpty(group, "group"); String groupName = namingConvention.create().sharedNameForGroup(group); - if (ec2Client.getSecurityGroupServices().describeSecurityGroupsInRegion(region, groupName).size() > 0) { + if (client.getSecurityGroupServices().describeSecurityGroupsInRegion(region, groupName).size() > 0) { logger.debug(">> deleting securityGroup(%s)", groupName); - ec2Client.getSecurityGroupServices().deleteSecurityGroupInRegion(region, groupName); + client.getSecurityGroupServices().deleteSecurityGroupInRegion(region, groupName); // TODO: test this clear happens securityGroupMap.invalidate(new RegionNameAndIngressRules(region, groupName, null, false)); logger.debug("<< deleted securityGroup(%s)", groupName); @@ -148,20 +217,20 @@ public class EC2ComputeService extends BaseComputeService { @VisibleForTesting void deleteKeyPair(String region, String group) { - for (KeyPair keyPair : ec2Client.getKeyPairServices().describeKeyPairsInRegion(region)) { + for (KeyPair keyPair : client.getKeyPairServices().describeKeyPairsInRegion(region)) { String keyName = keyPair.getKeyName(); Predicate keyNameMatcher = namingConvention.create().containsGroup(group); String oldKeyNameRegex = String.format("jclouds#%s#%s#%s", group, region, "[0-9a-f]+").replace('#', delimiter); // old keypair pattern too verbose as it has an unnecessary region qualifier if (keyNameMatcher.apply(keyName) || keyName.matches(oldKeyNameRegex)) { - Set instancesUsingKeyPair = extractIdsFromInstances(filter(concat(ec2Client.getInstanceServices() + Set instancesUsingKeyPair = extractIdsFromInstances(filter(concat(client.getInstanceServices() .describeInstancesInRegion(region)), usingKeyPairAndNotDead(keyPair))); if (instancesUsingKeyPair.size() > 0) { logger.debug("<< inUse keyPair(%s), by (%s)", keyPair.getKeyName(), instancesUsingKeyPair); } else { logger.debug(">> deleting keyPair(%s)", keyPair.getKeyName()); - ec2Client.getKeyPairServices().deleteKeyPairInRegion(region, keyPair.getKeyName()); + client.getKeyPairServices().deleteKeyPairInRegion(region, keyPair.getKeyName()); // TODO: test this clear happens credentialsMap.remove(new RegionAndName(region, keyPair.getKeyName())); credentialsMap.remove(new RegionAndName(region, group)); @@ -184,7 +253,6 @@ public class EC2ComputeService extends BaseComputeService { protected Predicate usingKeyPairAndNotDead(final KeyPair keyPair) { return new Predicate() { - @Override public boolean apply(RunningInstance input) { switch (input.getInstanceState()) { @@ -194,7 +262,6 @@ public class EC2ComputeService extends BaseComputeService { } return keyPair.getKeyName().equals(input.getKeyName()); } - }; } diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/domain/RegionAndName.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/domain/RegionAndName.java index e0efe4c94c..e7ddab7f9d 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/domain/RegionAndName.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/domain/RegionAndName.java @@ -18,26 +18,33 @@ */ package org.jclouds.ec2.compute.domain; +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.base.Function; +import com.google.common.base.Objects; +import com.google.common.base.Objects.ToStringHelper; + /** * * @author Adrian Cole */ public class RegionAndName { + protected final String region; protected final String name; - public RegionAndName(String region, String name) { - this.region = region; - this.name = name; + public String slashEncode() { + return new StringBuilder(region).append('/').append(name).toString(); } + public RegionAndName(String region, String name) { + this.region = checkNotNull(region, "region"); + this.name = checkNotNull(name, "name"); + } + @Override public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((region == null) ? 0 : region.hashCode()); - result = prime * result + ((name == null) ? 0 : name.hashCode()); - return result; + return Objects.hashCode(region, name); } @Override @@ -46,20 +53,10 @@ public class RegionAndName { return true; if (obj == null) return false; - if (getClass() != obj.getClass()) + if (!(obj instanceof RegionAndName)) return false; - RegionAndName other = (RegionAndName) obj; - if (region == null) { - if (other.region != null) - return false; - } else if (!region.equals(other.region)) - return false; - if (name == null) { - if (other.name != null) - return false; - } else if (!name.equals(other.name)) - return false; - return true; + RegionAndName other = RegionAndName.class.cast(obj); + return Objects.equal(region, other.region) && Objects.equal(name, other.name); } public String getRegion() { @@ -72,7 +69,45 @@ public class RegionAndName { @Override public String toString() { - return "[region=" + region + ", name=" + name + "]"; + return string().toString(); } + protected ToStringHelper string() { + return Objects.toStringHelper("").add("region", region).add("name", name); + } + + private static enum RegionFunction implements Function { + INSTANCE; + @Override + public String apply(RegionAndName input) { + return input.getRegion(); + } + + @Override + public String toString() { + return "getRegion()"; + } + }; + + public static Function regionFunction() { + return RegionFunction.INSTANCE; + } + + private static enum NameFunction implements Function { + INSTANCE; + @Override + public String apply(RegionAndName input) { + return input.getName(); + } + + @Override + public String toString() { + return "getName()"; + } + }; + + public static Function nameFunction() { + return NameFunction.INSTANCE; + } + } diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/PresentInstances.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/PresentInstances.java new file mode 100644 index 0000000000..dada30949e --- /dev/null +++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/PresentInstances.java @@ -0,0 +1,84 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.jclouds.ec2.compute.functions; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.Iterables.concat; +import static com.google.common.collect.Iterables.toArray; +import static com.google.common.collect.Multimaps.index; +import static com.google.common.collect.Multimaps.transformValues; +import static org.jclouds.ec2.compute.domain.RegionAndName.nameFunction; +import static org.jclouds.ec2.compute.domain.RegionAndName.regionFunction; + +import java.util.Collection; +import java.util.Set; + +import javax.annotation.Resource; +import javax.inject.Singleton; + +import org.jclouds.ec2.EC2Client; +import org.jclouds.ec2.compute.domain.RegionAndName; +import org.jclouds.ec2.domain.RunningInstance; +import org.jclouds.logging.Logger; + +import com.google.common.base.Function; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableSet.Builder; +import com.google.common.collect.Multimap; +import com.google.inject.Inject; + +/** + * returns the instances present in the list. Makes a single rest call per aggregate on region. + * + * @author Adrian Cole + */ +@Singleton +public class PresentInstances implements Function, Set> { + + @Resource + protected Logger logger = Logger.NULL; + + private final EC2Client client; + + @Inject + public PresentInstances(EC2Client client) { + this.client = checkNotNull(client, "client"); + } + + @Override + public Set apply(Set regionAndIds) { + if (checkNotNull(regionAndIds, "regionAndIds").isEmpty()) + return ImmutableSet.of(); + Builder builder = ImmutableSet. builder(); + Multimap regionToInstanceIds = transformValues(index(regionAndIds, regionFunction()), + nameFunction()); + for (String region : regionToInstanceIds.keySet()) { + Collection instanceIds = regionToInstanceIds.get(region); + logger.trace("looking for instances %s in region %s", instanceIds, region); + builder.addAll(concat(client.getInstanceServices().describeInstancesInRegion(region, + toArray(instanceIds, String.class)))); + } + return builder.build(); + } + + @Override + public String toString(){ + return "presentInstances()"; + } +} diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/RunningInstanceToNodeMetadata.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/RunningInstanceToNodeMetadata.java index 8dd3315379..1053c25760 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/RunningInstanceToNodeMetadata.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/RunningInstanceToNodeMetadata.java @@ -22,6 +22,7 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Predicates.not; import static com.google.common.base.Strings.emptyToNull; import static com.google.common.collect.Iterables.filter; +import static org.jclouds.compute.util.ComputeServiceUtils.addMetadataAndParseTagsFromValuesOfEmptyString; import java.util.List; import java.util.Map; @@ -101,11 +102,8 @@ public class RunningInstanceToNodeMetadata implements Function 0) + toString.add("userDataCksum", Hashing.crc32().hashBytes(Bytes.toArray(userData))); ImmutableSet mappings = blockDeviceMappings.build(); if (mappings.size() != 0) toString.add("blockDeviceMappings", mappings); diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/predicates/InstancePresent.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/predicates/InstancePresent.java deleted file mode 100644 index f17c4f1a9f..0000000000 --- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/predicates/InstancePresent.java +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Licensed to jclouds, Inc. (jclouds) under one or more - * contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. jclouds licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.jclouds.ec2.compute.predicates; - -import static com.google.common.base.Preconditions.checkNotNull; - -import java.util.NoSuchElementException; - -import javax.annotation.Resource; -import javax.inject.Singleton; - -import org.jclouds.ec2.EC2Client; -import org.jclouds.ec2.compute.domain.RegionAndName; -import org.jclouds.logging.Logger; -import org.jclouds.rest.ResourceNotFoundException; - -import com.google.common.base.Predicate; -import com.google.common.collect.Iterables; -import com.google.inject.Inject; - -/** - * - * @author Adrian Cole - */ -@Singleton -public class InstancePresent implements Predicate { - - private final EC2Client client; - - @Resource - protected Logger logger = Logger.NULL; - - @Inject - public InstancePresent(EC2Client client) { - this.client = checkNotNull(client, "client"); - } - - public boolean apply(RegionAndName instance) { - logger.trace("looking for instance %s/%s", instance.getRegion(), instance.getName()); - try { - refresh(instance); - return true; - } catch (ResourceNotFoundException e) { - return false; - } catch (NoSuchElementException e) { - return false; - } - } - - protected void refresh(RegionAndName instance) { - Iterables.getOnlyElement(Iterables.getOnlyElement(client.getInstanceServices().describeInstancesInRegion( - instance.getRegion(), instance.getName()))); - } -} 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 b40ac86755..ce0133b4bd 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 @@ -17,9 +17,13 @@ * under the License. */ package org.jclouds.ec2.compute.strategy; + import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.collect.Iterables.all; +import static com.google.common.collect.Iterables.concat; +import static com.google.common.collect.Iterables.size; import static com.google.common.collect.Iterables.transform; +import static com.google.common.collect.Sets.difference; +import static com.google.common.util.concurrent.Atomics.newReference; import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_RUNNING; import static org.jclouds.compute.functions.DefaultCredentialsFromImageOrOverridingCredentials.overrideDefaultCredentialsWithOptionsIfPresent; import static org.jclouds.ec2.compute.util.EC2ComputeUtils.getZoneFromLocationOrNull; @@ -32,14 +36,12 @@ import java.util.concurrent.atomic.AtomicReference; import javax.annotation.Resource; import javax.inject.Inject; import javax.inject.Named; -import javax.inject.Provider; import javax.inject.Singleton; import org.jclouds.aws.util.AWSUtils; import org.jclouds.compute.config.CustomizationResponse; import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.compute.domain.Template; -import org.jclouds.compute.domain.TemplateBuilder; import org.jclouds.compute.options.TemplateOptions; import org.jclouds.compute.reference.ComputeServiceConstants; import org.jclouds.compute.strategy.CreateNodesInGroupThenAddToSet; @@ -48,7 +50,7 @@ import org.jclouds.domain.Credentials; import org.jclouds.domain.LoginCredentials; import org.jclouds.ec2.EC2Client; import org.jclouds.ec2.compute.domain.RegionAndName; -import org.jclouds.ec2.compute.predicates.InstancePresent; +import org.jclouds.ec2.compute.functions.PresentInstances; import org.jclouds.ec2.domain.RunningInstance; import org.jclouds.ec2.options.RunInstancesOptions; import org.jclouds.ec2.reference.EC2Constants; @@ -56,14 +58,12 @@ import org.jclouds.logging.Logger; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Function; -import com.google.common.base.Joiner; 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.ImmutableSet.Builder; -import com.google.common.collect.Iterables; +import com.google.common.collect.Maps; import com.google.common.collect.Multimap; -import com.google.common.util.concurrent.Atomics; /** * creates futures that correlate to @@ -94,30 +94,26 @@ public class EC2CreateNodesInGroupThenAddToSet implements CreateNodesInGroupThen final Function runningInstanceToNodeMetadata; @VisibleForTesting final ComputeUtils utils; - final InstancePresent instancePresent; + final PresentInstances presentInstances; final LoadingCache instanceToCredentials; final Map credentialStore; - final Provider templateBuilderProvider; @Inject protected EC2CreateNodesInGroupThenAddToSet( - EC2Client client, - @Named("ELASTICIP") - LoadingCache elasticIpCache, - @Named(TIMEOUT_NODE_RUNNING) Predicate> nodeRunning, - Provider templateBuilderProvider, - CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions createKeyPairAndSecurityGroupsAsNeededAndReturncustomize, - InstancePresent instancePresent, Function runningInstanceToNodeMetadata, - LoadingCache instanceToCredentials, Map credentialStore, - ComputeUtils utils) { + EC2Client client, + @Named("ELASTICIP") LoadingCache elasticIpCache, + @Named(TIMEOUT_NODE_RUNNING) Predicate> nodeRunning, + CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions createKeyPairAndSecurityGroupsAsNeededAndReturncustomize, + PresentInstances presentInstances, Function runningInstanceToNodeMetadata, + LoadingCache instanceToCredentials, + Map credentialStore, ComputeUtils utils) { this.client = checkNotNull(client, "client"); this.elasticIpCache = checkNotNull(elasticIpCache, "elasticIpCache"); this.nodeRunning = checkNotNull(nodeRunning, "nodeRunning"); - this.templateBuilderProvider = checkNotNull(templateBuilderProvider, "templateBuilderProvider"); - this.instancePresent = checkNotNull(instancePresent, "instancePresent"); + this.presentInstances = checkNotNull(presentInstances, "presentInstances"); this.createKeyPairAndSecurityGroupsAsNeededAndReturncustomize = checkNotNull( - createKeyPairAndSecurityGroupsAsNeededAndReturncustomize, - "createKeyPairAndSecurityGroupsAsNeededAndReturncustomize"); + createKeyPairAndSecurityGroupsAsNeededAndReturncustomize, + "createKeyPairAndSecurityGroupsAsNeededAndReturncustomize"); this.runningInstanceToNodeMetadata = checkNotNull(runningInstanceToNodeMetadata, "runningInstanceToNodeMetadata"); this.instanceToCredentials = checkNotNull(instanceToCredentials, "instanceToCredentials"); this.credentialStore = checkNotNull(credentialStore, "credentialStore"); @@ -133,109 +129,111 @@ public class EC2CreateNodesInGroupThenAddToSet implements CreateNodesInGroupThen @Override public Map> execute(String group, int count, Template template, Set goodNodes, - Map badNodes, Multimap customizationResponses) { - + Map badNodes, Multimap customizationResponses) { + Template mutableTemplate = template.clone(); - Iterable ips = allocateElasticIpsInRegion(count, mutableTemplate); - - Iterable started = createKeyPairAndSecurityGroupsAsNeededThenRunInstances(group, - count, mutableTemplate); - - Iterable ids = transform(started, instanceToRegionAndName); - - String idsString = Joiner.on(',').join(ids); - if (Iterables.size(ids) > 0) { - logger.debug("<< started instances(%s)", idsString); - all(ids, instancePresent); - logger.debug("<< present instances(%s)", idsString); - populateCredentials(started, template.getOptions()); + Set started = runInstancesAndWarnOnInvisible(group, count, mutableTemplate); + if (started.size() == 0) { + logger.warn("<< unable to start instances(%s)", mutableTemplate); + return ImmutableMap.of(); } - - assignElasticIpsToInstances(ips, started); - - return utils.customizeNodesAndAddToGoodMapOrPutExceptionIntoBadMap(mutableTemplate.getOptions(), transform(started, - runningInstanceToNodeMetadata), goodNodes, badNodes, customizationResponses); + populateCredentials(started, template.getOptions()); + + if (autoAllocateElasticIps) // before customization as the elastic ips may be needed + blockUntilRunningAndAssignElasticIpsToInstancesOrPutIntoBadMap(started, badNodes); + + return utils.customizeNodesAndAddToGoodMapOrPutExceptionIntoBadMap(mutableTemplate.getOptions(), + transform(started, runningInstanceToNodeMetadata), goodNodes, badNodes, customizationResponses); } - protected void populateCredentials(Iterable started, TemplateOptions options) { + /** + * attempts to start the specified count of instances. eventual consistency might cause a problem where instances + * aren't immediately visible to the api. This method will warn when that occurs. + */ + private Set runInstancesAndWarnOnInvisible(String group, int count, Template mutableTemplate) { + Set started = createKeyPairAndSecurityGroupsAsNeededThenRunInstances(group, count, + mutableTemplate); + Set startedIds = ImmutableSet.copyOf(transform(started, instanceToRegionAndName)); + if (startedIds.size() == 0) { + return ImmutableSet.copyOf(started); + } + logger.debug("<< started instances(%s)", startedIds); + Set visible = presentInstances.apply(startedIds); + Set visibleIds = ImmutableSet.copyOf(transform(visible, instanceToRegionAndName)); + logger.trace("<< visible instances(%s)", visibleIds); + + // add an exception for each of the nodes we cannot customize + Set invisibleIds = difference(startedIds, visibleIds); + if (invisibleIds.size() > 0) { + logger.warn("<< not api visible instances(%s)", invisibleIds); + } + return started; + } + + private void populateCredentials(Set input, TemplateOptions options) { LoginCredentials credentials = null; - for (RunningInstance instance : started) { + for (RunningInstance instance : input) { credentials = instanceToCredentials.apply(instance); if (credentials != null) break; } credentials = overrideDefaultCredentialsWithOptionsIfPresent(credentials, options); if (credentials != null) - for (RunningInstance instance : started) - credentialStore.put("node#" + instance.getRegion() + "/" + instance.getId(), credentials); + for (RegionAndName instance : transform(input, instanceToRegionAndName)) + credentialStore.put("node#" + instance.slashEncode(), credentials); } - protected Iterable allocateElasticIpsInRegion(int count, Template template) { - - Builder ips = ImmutableSet.builder(); - if (!autoAllocateElasticIps) - return ips.build(); - - String region = AWSUtils.getRegionFromLocationOrNull(template.getLocation()); - logger.debug("<< allocating %d elastic IPs for nodes in region (%s)", count, region); - - for (int i = 0; i < count; ++i) { - ips.add(client.getElasticIPAddressServices().allocateAddressInRegion(region)); - } - return ips.build(); - } - - protected void assignElasticIpsToInstances(Iterable ips, Iterable startedInstances) { - - if (!autoAllocateElasticIps) - return; - - // TODO parallel - int i = 0; - for (RunningInstance startedInstance : startedInstances) { - String ip = Iterables.get(ips, i); - String region = startedInstance.getRegion(); - String id = startedInstance.getId(); - RegionAndName coordinates = new RegionAndName(region, id); - - // block until instance is running - logger.debug(">> awaiting status running instance(%s)", coordinates); - AtomicReference node = Atomics.newReference(runningInstanceToNodeMetadata.apply(startedInstance)); - nodeRunning.apply(node); - logger.trace("<< running instance(%s)", coordinates); - logger.debug(">> associating elastic IP %s to instance %s", ip, coordinates); - client.getElasticIPAddressServices().associateAddressInRegion(region, ip, id); - logger.trace("<< associated elastic IP %s to instance %s", ip, coordinates); - // add mapping of instance to ip into the cache - elasticIpCache.put(coordinates, ip); + private void blockUntilRunningAndAssignElasticIpsToInstancesOrPutIntoBadMap(Set input, + Map badNodes) { + Map instancesById = Maps.uniqueIndex(input, instanceToRegionAndName); + for (RegionAndName id : instancesById.keySet()) { + try { + logger.debug("<< allocating elastic IP instance(%s)", id); + String ip = client.getElasticIPAddressServices().allocateAddressInRegion(id.getRegion()); + // block until instance is running + logger.debug(">> awaiting status running instance(%s)", id); + AtomicReference node = newReference(runningInstanceToNodeMetadata + .apply(instancesById.get(id))); + nodeRunning.apply(node); + logger.trace("<< running instance(%s)", id); + logger.debug(">> associating elastic IP %s to instance %s", ip, id); + client.getElasticIPAddressServices().associateAddressInRegion(id.getRegion(), ip, id.getName()); + logger.trace("<< associated elastic IP %s to instance %s", ip, id); + // add mapping of instance to ip into the cache + elasticIpCache.put(id, ip); + } catch (RuntimeException e) { + badNodes.put(runningInstanceToNodeMetadata.apply(instancesById.get(id)), e); + } } } - protected Iterable createKeyPairAndSecurityGroupsAsNeededThenRunInstances(String group, - int count, Template template) { + private Set createKeyPairAndSecurityGroupsAsNeededThenRunInstances(String group, int count, + Template template) { String region = AWSUtils.getRegionFromLocationOrNull(template.getLocation()); String zone = getZoneFromLocationOrNull(template.getLocation()); RunInstancesOptions instanceOptions = createKeyPairAndSecurityGroupsAsNeededAndReturncustomize.execute(region, - group, template); + group, template); return createNodesInRegionAndZone(region, zone, group, count, template, instanceOptions); } - protected Iterable createNodesInRegionAndZone(String region, String zone, String group, - int count, Template template, RunInstancesOptions instanceOptions) { + protected Set createNodesInRegionAndZone(String region, String zone, String group, int count, + Template template, RunInstancesOptions instanceOptions) { int countStarted = 0; int tries = 0; - Iterable started = ImmutableSet. of(); + Set started = ImmutableSet. of(); while (countStarted < count && tries++ < count) { if (logger.isDebugEnabled()) logger.debug(">> running %d instance region(%s) zone(%s) ami(%s) params(%s)", count - countStarted, region, - zone, template.getImage().getProviderId(), instanceOptions.buildFormParameters()); + zone, template.getImage().getProviderId(), instanceOptions.buildFormParameters()); - started = Iterables.concat(started, client.getInstanceServices().runInstancesInRegion(region, zone, - template.getImage().getProviderId(), 1, count - countStarted, instanceOptions)); + started = ImmutableSet.copyOf(concat( + started, + client.getInstanceServices().runInstancesInRegion(region, zone, template.getImage().getProviderId(), 1, + count - countStarted, instanceOptions))); - countStarted = Iterables.size(started); + countStarted = size(started); if (countStarted < count) logger.debug(">> not enough instances (%d/%d) started, attempting again", countStarted, count); } diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/domain/RunningInstance.java b/apis/ec2/src/main/java/org/jclouds/ec2/domain/RunningInstance.java index bb4613bff2..f062c93448 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/domain/RunningInstance.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/domain/RunningInstance.java @@ -28,6 +28,7 @@ import org.jclouds.javax.annotation.Nullable; import com.google.common.base.Objects; import com.google.common.base.Objects.ToStringHelper; +import com.google.common.base.Strings; import com.google.common.collect.ComparisonChain; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; @@ -42,11 +43,18 @@ import com.google.common.collect.Sets; * @author Adrian Cole */ public class RunningInstance implements Comparable { - public static Builder builder() { - return new Builder(); + + public static Builder builder() { + return new ConcreteBuilder(); } - public static class Builder { + public Builder toBuilder() { + return new ConcreteBuilder().fromRunningInstance(this); + } + + public abstract static class Builder> { + protected abstract T self(); + protected String region; protected Set groupNames = Sets.newLinkedHashSet(); protected String amiLaunchIndex; @@ -70,157 +78,174 @@ public class RunningInstance implements Comparable { protected RootDeviceType rootDeviceType = RootDeviceType.INSTANCE_STORE; protected String rootDeviceName; protected Map ebsBlockDevices = Maps.newLinkedHashMap(); + protected Map tags = Maps.newLinkedHashMap(); - public Builder region(String region) { + public T tags(Map tags) { + this.tags = ImmutableMap.copyOf(checkNotNull(tags, "tags")); + return self(); + } + + public T tag(String key, String value) { + if (key != null) + this.tags.put(key, Strings.nullToEmpty(value)); + return self(); + } + + public T region(String region) { this.region = region; - return this; + return self(); } - public Builder groupNames(Iterable groupNames) { + public T groupNames(Iterable groupNames) { this.groupNames = ImmutableSet.copyOf(checkNotNull(groupNames, "groupNames")); - return this; + return self(); } - public Builder groupName(String groupName) { + public T groupName(String groupName) { if (groupName != null) this.groupNames.add(groupName); - return this; + return self(); } - public Builder amiLaunchIndex(String amiLaunchIndex) { + public T amiLaunchIndex(String amiLaunchIndex) { this.amiLaunchIndex = amiLaunchIndex; - return this; + return self(); } - public Builder dnsName(String dnsName) { + public T dnsName(String dnsName) { this.dnsName = dnsName; - return this; + return self(); } - public Builder imageId(String imageId) { + public T imageId(String imageId) { this.imageId = imageId; - return this; + return self(); } - public Builder instanceId(String instanceId) { + public T instanceId(String instanceId) { this.instanceId = instanceId; - return this; + return self(); } - public Builder instanceState(InstanceState instanceState) { + public T instanceState(InstanceState instanceState) { this.instanceState = instanceState; - return this; + return self(); } - public Builder rawState(String rawState) { + public T rawState(String rawState) { this.rawState = rawState; - return this; + return self(); } - public Builder instanceType(String instanceType) { + public T instanceType(String instanceType) { this.instanceType = instanceType; - return this; + return self(); } - public Builder ipAddress(String ipAddress) { + public T ipAddress(String ipAddress) { this.ipAddress = ipAddress; - return this; + return self(); } - public Builder kernelId(String kernelId) { + public T kernelId(String kernelId) { this.kernelId = kernelId; - return this; + return self(); } - public Builder keyName(String keyName) { + public T keyName(String keyName) { this.keyName = keyName; - return this; + return self(); } - public Builder launchTime(Date launchTime) { + public T launchTime(Date launchTime) { this.launchTime = launchTime; - return this; + return self(); } - public Builder availabilityZone(String availabilityZone) { + public T availabilityZone(String availabilityZone) { this.availabilityZone = availabilityZone; - return this; + return self(); } - public Builder virtualizationType(String virtualizationType) { + public T virtualizationType(String virtualizationType) { this.virtualizationType = virtualizationType; - return this; + return self(); } - public Builder platform(String platform) { + public T platform(String platform) { this.platform = platform; - return this; + return self(); } - public Builder privateDnsName(String privateDnsName) { + public T privateDnsName(String privateDnsName) { this.privateDnsName = privateDnsName; - return this; + return self(); } - public Builder privateIpAddress(String privateIpAddress) { + public T privateIpAddress(String privateIpAddress) { this.privateIpAddress = privateIpAddress; - return this; + return self(); } - public Builder ramdiskId(String ramdiskId) { + public T ramdiskId(String ramdiskId) { this.ramdiskId = ramdiskId; - return this; + return self(); } - public Builder reason(String reason) { + public T reason(String reason) { this.reason = reason; - return this; + return self(); } - public Builder rootDeviceType(RootDeviceType rootDeviceType) { + public T rootDeviceType(RootDeviceType rootDeviceType) { this.rootDeviceType = rootDeviceType; - return this; + return self(); } - public Builder rootDeviceName(String rootDeviceName) { + public T rootDeviceName(String rootDeviceName) { this.rootDeviceName = rootDeviceName; - return this; + return self(); } - public Builder devices(Map ebsBlockDevices) { + public T devices(Map ebsBlockDevices) { this.ebsBlockDevices = ImmutableMap.copyOf(checkNotNull(ebsBlockDevices, "ebsBlockDevices")); - return this; + return self(); } - public Builder device(String key, BlockDevice value) { + public T device(String key, BlockDevice value) { if (key != null && value != null) this.ebsBlockDevices.put(key, value); + return self(); + } + + public T fromRunningInstance(RunningInstance in) { + return region(in.region).groupNames(in.groupNames).amiLaunchIndex(in.amiLaunchIndex).dnsName(in.dnsName) + .imageId(in.imageId).instanceId(in.instanceId).instanceState(in.instanceState).rawState(in.rawState) + .instanceType(in.instanceType).ipAddress(in.ipAddress).kernelId(in.kernelId).keyName(in.keyName) + .launchTime(in.launchTime).availabilityZone(in.availabilityZone) + .virtualizationType(in.virtualizationType).platform(in.platform).privateDnsName(in.privateDnsName) + .privateIpAddress(in.privateIpAddress).ramdiskId(in.ramdiskId).reason(in.reason) + .rootDeviceType(in.rootDeviceType).rootDeviceName(in.rootDeviceName).devices(in.ebsBlockDevices) + .tags(in.tags); + } + + public abstract RunningInstance build(); + + } + + private static class ConcreteBuilder extends Builder { + @Override + protected ConcreteBuilder self() { return this; } + @Override public RunningInstance build() { return new RunningInstance(region, groupNames, amiLaunchIndex, dnsName, imageId, instanceId, instanceState, - rawState, instanceType, ipAddress, kernelId, keyName, launchTime, availabilityZone, - virtualizationType, platform, privateDnsName, privateIpAddress, ramdiskId, reason, rootDeviceType, - rootDeviceName, ebsBlockDevices); + rawState, instanceType, ipAddress, kernelId, keyName, launchTime, availabilityZone, virtualizationType, + platform, privateDnsName, privateIpAddress, ramdiskId, reason, rootDeviceType, rootDeviceName, + ebsBlockDevices, tags); } - - public String getDnsName() { - return dnsName; - } - - public String getIpAddress() { - return ipAddress; - } - - public String getPrivateDnsName() { - return privateDnsName; - } - - public String getPrivateIpAddress() { - return privateIpAddress; - } - } protected final String region; @@ -256,6 +281,7 @@ public class RunningInstance implements Comparable { @Nullable protected final String rootDeviceName; protected final Map ebsBlockDevices; + protected final Map tags; protected RunningInstance(String region, Iterable groupNames, @Nullable String amiLaunchIndex, @Nullable String dnsName, String imageId, String instanceId, InstanceState instanceState, String rawState, @@ -263,7 +289,7 @@ public class RunningInstance implements Comparable { Date launchTime, String availabilityZone, String virtualizationType, @Nullable String platform, @Nullable String privateDnsName, @Nullable String privateIpAddress, @Nullable String ramdiskId, @Nullable String reason, RootDeviceType rootDeviceType, @Nullable String rootDeviceName, - Map ebsBlockDevices) { + Map ebsBlockDevices, Map tags) { this.region = checkNotNull(region, "region"); this.amiLaunchIndex = amiLaunchIndex; // nullable on runinstances. this.dnsName = dnsName; // nullable on runinstances. @@ -287,6 +313,7 @@ public class RunningInstance implements Comparable { this.rootDeviceName = rootDeviceName; this.ebsBlockDevices = ImmutableMap.copyOf(checkNotNull(ebsBlockDevices, "ebsBlockDevices for %s/%s", region, instanceId)); this.groupNames = ImmutableSet.copyOf(checkNotNull(groupNames, "groupNames for %s/%s", region, instanceId)); + this.tags = ImmutableMap. copyOf(checkNotNull(tags, "tags")); } /** @@ -462,6 +489,13 @@ public class RunningInstance implements Comparable { return groupNames; } + /** + * tags that are present in the instance + */ + public Map getTags() { + return tags; + } + @Override public int compareTo(RunningInstance other) { return ComparisonChain.start().compare(region, other.region).compare(instanceId, other.instanceId, Ordering.natural().nullsLast()).result(); @@ -489,7 +523,7 @@ public class RunningInstance implements Comparable { .add("ipAddress", ipAddress).add("dnsName", dnsName).add("privateIpAddress", privateIpAddress) .add("privateDnsName", privateDnsName).add("keyName", keyName).add("groupNames", groupNames) .add("platform", platform).add("launchTime", launchTime).add("rootDeviceName", rootDeviceName) - .add("rootDeviceType", rootDeviceType).add("ebsBlockDevices", ebsBlockDevices); + .add("rootDeviceType", rootDeviceType).add("ebsBlockDevices", ebsBlockDevices).add("tags", tags); } @Override diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/reference/EC2Constants.java b/apis/ec2/src/main/java/org/jclouds/ec2/reference/EC2Constants.java index eb57280447..ee103e62af 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/reference/EC2Constants.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/reference/EC2Constants.java @@ -18,6 +18,8 @@ */ package org.jclouds.ec2.reference; +import org.jclouds.compute.ComputeService; + /** * Configuration properties and constants used in EC2 connections. * @@ -40,4 +42,11 @@ public interface EC2Constants { * deallocate when the node is destroyed. */ public static final String PROPERTY_EC2_AUTO_ALLOCATE_ELASTIC_IPS = "jclouds.ec2.auto-allocate-elastic-ips"; + + /** + * If this property is set to true(default), jclouds generate a name for each instance based on the group. ex. + * i-ef34ae2 becomes hadoop-ef34ae2. Note that this depends on {@link EC2Api#getTagApi} returning present. + */ + public static final String PROPERTY_EC2_GENERATE_INSTANCE_NAMES = "jclouds.ec2.generate-instance-names"; + } diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/util/Tags.java b/apis/ec2/src/main/java/org/jclouds/ec2/util/Tags.java new file mode 100644 index 0000000000..4469c7d4ea --- /dev/null +++ b/apis/ec2/src/main/java/org/jclouds/ec2/util/Tags.java @@ -0,0 +1,103 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.jclouds.ec2.util; + +import static com.google.common.collect.Multimaps.index; + +import java.util.Map; + +import org.jclouds.ec2.domain.Tag; + +import com.google.common.base.Function; +import com.google.common.collect.Maps; + +/** + * + * @author Adrian Cole + */ +public class Tags { + private Tags() { + } + + /** + * maps the input on {@link Tag#getResourceId()} with a value of a map of its {@link Tag#getKey()} to + * {@link Tag#getValue()} + */ + public static Map> resourceToTagsAsMap(Iterable tags) { + return Maps.transformValues(index(tags, resourceIdFunction()).asMap(), + new Function, Map>() { + @Override + public Map apply(Iterable in) { + return Maps.transformValues(Maps.uniqueIndex(in, keyFunction()), valueFunction()); + } + }); + } + + public static enum ValueFunction implements Function { + INSTANCE; + @Override + public String apply(Tag in) { + return in.getValue().or(""); + } + + @Override + public String toString() { + return "getValue()"; + } + } + + public static Function valueFunction() { + return ValueFunction.INSTANCE; + } + + public static enum KeyFunction implements Function { + INSTANCE; + @Override + public String apply(Tag in) { + return in.getKey(); + } + + @Override + public String toString() { + return "getKey()"; + } + } + + public static Function keyFunction() { + return KeyFunction.INSTANCE; + } + + public static enum ResourceIdFunction implements Function { + INSTANCE; + @Override + public String apply(Tag in) { + return in.getResourceId(); + } + + @Override + public String toString() { + return "getResourceId()"; + } + } + + public static Function resourceIdFunction() { + return ResourceIdFunction.INSTANCE; + } + +} diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/xml/BaseReservationHandler.java b/apis/ec2/src/main/java/org/jclouds/ec2/xml/BaseReservationHandler.java index c01805cd77..03f09e4150 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/xml/BaseReservationHandler.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/xml/BaseReservationHandler.java @@ -42,7 +42,6 @@ import org.xml.sax.Attributes; import com.google.common.base.Supplier; import com.google.common.collect.Sets; -import com.google.inject.Provider; /** * @@ -52,21 +51,28 @@ public abstract class BaseReservationHandler extends HandlerForGeneratedReque protected final DateCodec dateCodec; protected final Supplier defaultRegion; - protected final Provider builderProvider; @Inject - public BaseReservationHandler(DateCodecFactory dateCodecFactory, @Region Supplier defaultRegion, - Provider builderProvider) { + public BaseReservationHandler(DateCodecFactory dateCodecFactory, @Region Supplier defaultRegion) { this.dateCodec = dateCodecFactory.iso8601(); this.defaultRegion = defaultRegion; - this.builderProvider = builderProvider; - this.builder = builderProvider.get(); + } + + protected Builder builder = newBuilder(); + + protected Builder newBuilder() { + return RunningInstance.builder(); + } + + protected void inItem() { + if (endOfInstanceItem()) { + refineBuilderBeforeAddingInstance(); + instances.add(builder.build()); + builder = newBuilder(); + } } protected StringBuilder currentText = new StringBuilder(); - - protected Builder builder; - protected int itemDepth; protected boolean inInstancesSet; protected boolean inProductCodes; @@ -181,13 +187,6 @@ public abstract class BaseReservationHandler extends HandlerForGeneratedReque currentText = new StringBuilder(); } - protected void inItem() { - if (endOfInstanceItem()) { - refineBuilderBeforeAddingInstance(); - instances.add(builder.build()); - builder = builderProvider.get(); - } - } protected void refineBuilderBeforeAddingInstance() { String region = getRequest() != null ? AWSUtils.findRegionInArgsOrNull(getRequest()) : null; @@ -195,10 +194,6 @@ public abstract class BaseReservationHandler extends HandlerForGeneratedReque builder.groupNames(groupNames); } - protected Builder builder() { - return builder; - } - protected boolean endOfInstanceItem() { return itemDepth <= 2 && inInstancesSet && !inProductCodes && !inGroupSet; } diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/xml/DescribeInstancesResponseHandler.java b/apis/ec2/src/main/java/org/jclouds/ec2/xml/DescribeInstancesResponseHandler.java index 68f35fd634..b81a953bf9 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/xml/DescribeInstancesResponseHandler.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/xml/DescribeInstancesResponseHandler.java @@ -18,6 +18,8 @@ */ package org.jclouds.ec2.xml; +import static org.jclouds.util.SaxUtils.equalsOrSuffix; + import java.util.Set; import javax.inject.Inject; @@ -26,10 +28,11 @@ import org.jclouds.date.DateCodecFactory; import org.jclouds.ec2.domain.Reservation; import org.jclouds.ec2.domain.RunningInstance; import org.jclouds.location.Region; +import org.xml.sax.Attributes; import com.google.common.base.Supplier; -import com.google.common.collect.Sets; -import com.google.inject.Provider; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableSet.Builder; /** * Parses the following XML document: @@ -40,18 +43,53 @@ import com.google.inject.Provider; * @see > getResult() { - return reservations; + return reservations.build(); } protected boolean endOfReservationItem() { @@ -67,4 +105,7 @@ public class DescribeInstancesResponseHandler extends } } + protected boolean endOfInstanceItem() { + return itemDepth == 2 && inInstancesSet; + } } diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/xml/RunInstancesResponseHandler.java b/apis/ec2/src/main/java/org/jclouds/ec2/xml/RunInstancesResponseHandler.java index 9f9e3f2285..f9527620e8 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/xml/RunInstancesResponseHandler.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/xml/RunInstancesResponseHandler.java @@ -26,7 +26,6 @@ import org.jclouds.ec2.domain.RunningInstance; import org.jclouds.location.Region; import com.google.common.base.Supplier; -import com.google.inject.Provider; /** * Parses the following XML document: @@ -39,9 +38,8 @@ import com.google.inject.Provider; public class RunInstancesResponseHandler extends BaseReservationHandler> { @Inject - public RunInstancesResponseHandler(DateCodecFactory dateCodecFactory, @Region Supplier defaultRegion, - Provider builderProvider) { - super(dateCodecFactory, defaultRegion, builderProvider); + public RunInstancesResponseHandler(DateCodecFactory dateCodecFactory, @Region Supplier defaultRegion) { + super(dateCodecFactory, defaultRegion); } @Override diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/xml/TagSetHandler.java b/apis/ec2/src/main/java/org/jclouds/ec2/xml/TagSetHandler.java new file mode 100644 index 0000000000..9ef44c5d53 --- /dev/null +++ b/apis/ec2/src/main/java/org/jclouds/ec2/xml/TagSetHandler.java @@ -0,0 +1,80 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.jclouds.ec2.xml; + +import static org.jclouds.util.SaxUtils.currentOrNull; +import static org.jclouds.util.SaxUtils.equalsOrSuffix; + +import java.util.Map; + +import org.jclouds.http.functions.ParseSax; +import org.xml.sax.Attributes; + +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableMap; + +/** + * @author grkvlt@apache.org + */ +public class TagSetHandler extends ParseSax.HandlerForGeneratedRequestWithResult> { + private StringBuilder currentText = new StringBuilder(); + + private ImmutableMap.Builder result; + private boolean inItem = false; + private String key; + private String value; + + public Map getResult() { + return result.build(); + } + + @Override + public void startElement(String uri, String localName, String qName, Attributes attributes) { + if (equalsOrSuffix(qName, "tagSet")) { + result = ImmutableMap.builder(); + } else if (qName.equals("item")) { + inItem = true; + key = null; + value = null; + } + currentText = new StringBuilder(); + } + + @Override + public void endElement(String uri, String localName, String qName) { + if (qName.equals("item")) { + inItem = false; + if (key != null) { + result.put(key, Strings.nullToEmpty(value)); + } + } + if (inItem) { + if (qName.equals("key")) { + key = currentOrNull(currentText); + } else if (qName.equals("value")) { + value = currentOrNull(currentText); + } + } + } + + @Override + public void characters(char ch[], int start, int length) { + currentText.append(ch, start, length); + } +} 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 4d51a91cda..d6d84f9296 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 @@ -85,13 +85,14 @@ public class EC2ComputeServiceLiveTest extends BaseComputeServiceLiveTest { return new SshjSshClientModule(); } - // normal ec2 does not support metadata @Override protected void checkUserMetadataContains(NodeMetadata node, ImmutableMap userMetadata) { - assert node.getUserMetadata().equals(ImmutableMap. of()) : String.format( - "node userMetadata did not match %s %s", userMetadata, node); + if (view.unwrap(EC2ApiMetadata.CONTEXT_TOKEN).getApi().getTagApi().isPresent()) { + super.checkUserMetadataContains(node, userMetadata); + } else { + assertTrue(node.getUserMetadata().isEmpty(), "not expecting metadata when tag extension isn't present" + node); + } } - @Test(enabled = true, dependsOnMethods = "testCorrectAuthException") public void testImagesResolveCorrectly() { @@ -324,7 +325,7 @@ public class EC2ComputeServiceLiveTest extends BaseComputeServiceLiveTest { return instance; } - public static void cleanupExtendedStuffInRegion(String region, SecurityGroupClient securityGroupClient, + protected static void cleanupExtendedStuffInRegion(String region, SecurityGroupClient securityGroupClient, KeyPairClient keyPairClient, String group) throws InterruptedException { try { for (SecurityGroup secgroup : securityGroupClient.describeSecurityGroupsInRegion(region)) diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/compute/functions/PresentInstancesTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/compute/functions/PresentInstancesTest.java new file mode 100644 index 0000000000..8dc61904b1 --- /dev/null +++ b/apis/ec2/src/test/java/org/jclouds/ec2/compute/functions/PresentInstancesTest.java @@ -0,0 +1,70 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.jclouds.ec2.compute.functions; + +import static org.easymock.EasyMock.createMock; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.replay; +import static org.easymock.EasyMock.verify; +import static org.testng.Assert.assertEquals; + +import java.util.Set; + +import org.jclouds.ec2.EC2Client; +import org.jclouds.ec2.compute.domain.RegionAndName; +import org.jclouds.ec2.domain.Reservation; +import org.jclouds.ec2.domain.RunningInstance; +import org.jclouds.ec2.services.InstanceClient; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableSet; + +/** + * @author Adrian Cole + */ +@Test(groups = "unit") +public class PresentInstancesTest { + RunningInstance instance1 = createMock(RunningInstance.class); + RunningInstance instance2 = createMock(RunningInstance.class); + + @SuppressWarnings("unchecked") + @Test + public void testWhenInstancesPresentSingleCall() { + + EC2Client client = createMock(EC2Client.class); + InstanceClient instanceClient = createMock(InstanceClient.class); + + expect(client.getInstanceServices()).andReturn(instanceClient); + + // avoid imatcher fail. if you change this, be sure to check multiple jres + expect(instanceClient.describeInstancesInRegion("us-east-1", "i-aaaa", "i-bbbb")).andReturn( + Set.class.cast(ImmutableSet.of(Reservation.builder().region("us-east-1") + .instances(ImmutableSet.of(instance1, instance2)).build()))); + + replay(client, instanceClient); + + PresentInstances fn = new PresentInstances(client); + + assertEquals(fn.apply(ImmutableSet.of(new RegionAndName("us-east-1", "i-aaaa"), new RegionAndName("us-east-1", + "i-bbbb"))), ImmutableSet.of(instance1, instance2)); + + verify(client, instanceClient); + } + +} 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 7c2c023b6a..23d21a670d 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 @@ -36,7 +36,6 @@ import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.compute.domain.NodeMetadata.Status; import org.jclouds.compute.domain.NodeMetadataBuilder; import org.jclouds.compute.domain.Template; -import org.jclouds.compute.domain.TemplateBuilder; import org.jclouds.compute.predicates.AtomicNodeRunning; import org.jclouds.compute.strategy.GetNodeMetadataStrategy; import org.jclouds.compute.util.ComputeUtils; @@ -47,9 +46,9 @@ import org.jclouds.domain.LocationScope; import org.jclouds.domain.LoginCredentials; import org.jclouds.ec2.EC2Client; import org.jclouds.ec2.compute.domain.RegionAndName; +import org.jclouds.ec2.compute.functions.PresentInstances; import org.jclouds.ec2.compute.functions.RunningInstanceToNodeMetadata; import org.jclouds.ec2.compute.options.EC2TemplateOptions; -import org.jclouds.ec2.compute.predicates.InstancePresent; import org.jclouds.ec2.domain.Reservation; import org.jclouds.ec2.domain.RunningInstance; import org.jclouds.ec2.options.RunInstancesOptions; @@ -62,7 +61,6 @@ 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 com.google.inject.util.Providers; /** * @author Adrian Cole @@ -81,8 +79,7 @@ public class EC2CreateNodesInGroupThenAddToSetTest { NodeMetadata nodeMetadata = new NodeMetadataBuilder().id(region + "/" + instanceCreatedId) .providerId(instanceCreatedId).status(Status.RUNNING).build(); // setup mocks - TemplateBuilder templateBuilder = createMock(TemplateBuilder.class); - EC2CreateNodesInGroupThenAddToSet strategy = setupStrategy(templateBuilder, nodeMetadata); + EC2CreateNodesInGroupThenAddToSet strategy = setupStrategy(nodeMetadata); InputParams input = new InputParams(location); InstanceClient instanceClient = createMock(InstanceClient.class); ElasticIPAddressClient ipClient = createMock(ElasticIPAddressClient.class); @@ -122,7 +119,7 @@ public class EC2CreateNodesInGroupThenAddToSetTest { expect(instance.getRegion()).andReturn(region).atLeastOnce(); expect(strategy.credentialStore.put("node#" + region + "/" + instanceCreatedId, creds)).andReturn(null); - expect(strategy.instancePresent.apply(new RegionAndName(region, instanceCreatedId))).andReturn(true); + expect(strategy.presentInstances.apply(ImmutableSet.of(new RegionAndName(region, instanceCreatedId)))).andReturn(ImmutableSet.of(instance)); expect(input.template.getOptions()).andReturn(input.options).atLeastOnce(); expect(input.options.getLoginUser()).andReturn(null); expect(input.options.getLoginPassword()).andReturn(null); @@ -135,7 +132,6 @@ public class EC2CreateNodesInGroupThenAddToSetTest { .andReturn(null); // replay mocks - replay(templateBuilder); replay(instanceClient); replay(ipClient); replay(ec2Options); @@ -147,7 +143,6 @@ public class EC2CreateNodesInGroupThenAddToSetTest { strategy.execute(input.tag, input.count, input.template, input.nodes, input.badNodes, input.customization); // verify mocks - verify(templateBuilder); verify(instanceClient); verify(ipClient); verify(ec2Options); @@ -195,8 +190,7 @@ public class EC2CreateNodesInGroupThenAddToSetTest { .providerId(instanceCreatedId).status(Status.RUNNING).build(); // setup mocks - TemplateBuilder templateBuilder = createMock(TemplateBuilder.class); - EC2CreateNodesInGroupThenAddToSet strategy = setupStrategy(templateBuilder, nodeMetadata); + EC2CreateNodesInGroupThenAddToSet strategy = setupStrategy(nodeMetadata); InputParams input = new InputParams(location); InstanceClient instanceClient = createMock(InstanceClient.class); RunInstancesOptions ec2Options = createMock(RunInstancesOptions.class); @@ -223,7 +217,7 @@ public class EC2CreateNodesInGroupThenAddToSetTest { expect(instance.getRegion()).andReturn(region).atLeastOnce(); expect(strategy.credentialStore.put("node#" + region + "/" + instanceCreatedId, creds)).andReturn(null); - expect(strategy.instancePresent.apply(new RegionAndName(region, instanceCreatedId))).andReturn(true); + expect(strategy.presentInstances.apply(ImmutableSet.of(new RegionAndName(region, instanceCreatedId)))).andReturn(ImmutableSet.of(instance)); expect(input.template.getOptions()).andReturn(input.options).atLeastOnce(); expect(input.options.getLoginUser()).andReturn(null); expect(input.options.getLoginPassword()).andReturn(null); @@ -238,7 +232,6 @@ public class EC2CreateNodesInGroupThenAddToSetTest { .andReturn(null); // replay mocks - replay(templateBuilder); replay(instanceClient); replay(ec2Options); replay(instance); @@ -249,7 +242,6 @@ public class EC2CreateNodesInGroupThenAddToSetTest { strategy.execute(input.tag, input.count, input.template, input.nodes, input.badNodes, input.customization); // verify mocks - verify(templateBuilder); verify(instanceClient); verify(ec2Options); verify(instance); @@ -307,7 +299,7 @@ public class EC2CreateNodesInGroupThenAddToSetTest { verify(strategy.createKeyPairAndSecurityGroupsAsNeededAndReturncustomize); verify(strategy.client); verify(strategy.elasticIpCache); - verify(strategy.instancePresent); + verify(strategy.presentInstances); verify(strategy.runningInstanceToNodeMetadata); verify(strategy.instanceToCredentials); verify(strategy.credentialStore); @@ -315,10 +307,10 @@ public class EC2CreateNodesInGroupThenAddToSetTest { } @SuppressWarnings("unchecked") - private EC2CreateNodesInGroupThenAddToSet setupStrategy(TemplateBuilder template, final NodeMetadata node) { + private EC2CreateNodesInGroupThenAddToSet setupStrategy(final NodeMetadata node) { EC2Client client = createMock(EC2Client.class); CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions createKeyPairAndSecurityGroupsAsNeededAndReturncustomize = createMock(CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.class); - InstancePresent instancePresent = createMock(InstancePresent.class); + PresentInstances presentInstances = createMock(PresentInstances.class); RunningInstanceToNodeMetadata runningInstanceToNodeMetadata = createMock(RunningInstanceToNodeMetadata.class); LoadingCache instanceToCredentials = createMock(LoadingCache.class); LoadingCache elasticIpCache = createMock(LoadingCache.class); @@ -334,15 +326,15 @@ public class EC2CreateNodesInGroupThenAddToSetTest { Map credentialStore = createMock(Map.class); ComputeUtils utils = createMock(ComputeUtils.class); return new EC2CreateNodesInGroupThenAddToSet(client, elasticIpCache, new AtomicNodeRunning(nodeRunning), - Providers. of(template), createKeyPairAndSecurityGroupsAsNeededAndReturncustomize, - instancePresent, runningInstanceToNodeMetadata, instanceToCredentials, credentialStore, utils); + createKeyPairAndSecurityGroupsAsNeededAndReturncustomize, presentInstances, runningInstanceToNodeMetadata, + instanceToCredentials, credentialStore, utils); } private void replayStrategy(EC2CreateNodesInGroupThenAddToSet strategy) { replay(strategy.createKeyPairAndSecurityGroupsAsNeededAndReturncustomize); replay(strategy.client); replay(strategy.elasticIpCache); - replay(strategy.instancePresent); + replay(strategy.presentInstances); replay(strategy.runningInstanceToNodeMetadata); replay(strategy.instanceToCredentials); replay(strategy.credentialStore); diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/util/TagsTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/util/TagsTest.java new file mode 100644 index 0000000000..a9ffef1fa1 --- /dev/null +++ b/apis/ec2/src/test/java/org/jclouds/ec2/util/TagsTest.java @@ -0,0 +1,57 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.jclouds.ec2.util; + +import static org.jclouds.ec2.domain.Tag.ResourceType.IMAGE; +import static org.jclouds.ec2.domain.Tag.ResourceType.INSTANCE; +import static org.testng.Assert.assertEquals; + +import org.jclouds.ec2.domain.Tag; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; + +/** + * @author Adrian Cole + */ +@Test +public class TagsTest { + Tag resourceTag1 = Tag.builder().resourceType(IMAGE).resourceId("1").key("key").value("value").build(); + + public void testValueFunction() { + assertEquals(Tags.valueFunction().apply(resourceTag1), "value"); + } + + public void testKeyFunction() { + assertEquals(Tags.keyFunction().apply(resourceTag1), "key"); + } + + Tag resourceTag2 = Tag.builder().resourceType(IMAGE).resourceId("1").key("foo").value("bar").build(); + Tag resource2Tag1 = Tag.builder().resourceType(INSTANCE).resourceId("2").key("absent").build(); + Tag resource2Tag2 = Tag.builder().resourceType(INSTANCE).resourceId("2").key("hello").value("world").build(); + + public void testResourceToTagsAsMap() { + assertEquals( + Tags.resourceToTagsAsMap(ImmutableSet.of(resourceTag1, resourceTag2, resource2Tag1, resource2Tag2)), + ImmutableMap.of("1", ImmutableMap.of("key", "value", "foo", "bar"), + "2", ImmutableMap.of("absent", "", "hello", "world"))); + } + +} diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/xml/DescribeInstancesResponseHandlerTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/xml/DescribeInstancesResponseHandlerTest.java index c1ef8e2967..2faa01bf3e 100644 --- a/apis/ec2/src/test/java/org/jclouds/ec2/xml/DescribeInstancesResponseHandlerTest.java +++ b/apis/ec2/src/test/java/org/jclouds/ec2/xml/DescribeInstancesResponseHandlerTest.java @@ -41,6 +41,7 @@ import org.testng.annotations.Test; import com.google.common.base.Supplier; import com.google.common.base.Suppliers; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.inject.AbstractModule; import com.google.inject.Guice; @@ -69,7 +70,7 @@ public class DescribeInstancesResponseHandlerTest extends BaseEC2HandlerTest { public void testWhenRunning() throws UnknownHostException { Set> contents = ImmutableSet.of(new Reservation(defaultRegion, - ImmutableSet.of("adriancole.ec2ingress"), ImmutableSet.of(new RunningInstance.Builder().region( + ImmutableSet.of("adriancole.ec2ingress"), ImmutableSet.of(RunningInstance.builder().region( defaultRegion).groupName("adriancole.ec2ingress").amiLaunchIndex("0").dnsName( "ec2-174-129-81-68.compute-1.amazonaws.com").imageId("ami-82e4b5c7").instanceId("i-0799056f") .instanceState(InstanceState.RUNNING).rawState("running").instanceType(InstanceType.M1_SMALL) @@ -91,7 +92,7 @@ public class DescribeInstancesResponseHandlerTest extends BaseEC2HandlerTest { public void testApplyInputStream() { Set> contents = ImmutableSet.of(new Reservation(defaultRegion, - ImmutableSet.of("default"), ImmutableSet.of(new RunningInstance.Builder().region(defaultRegion).groupName( + ImmutableSet.of("default"), ImmutableSet.of(RunningInstance.builder().region(defaultRegion).groupName( "default").amiLaunchIndex("23").dnsName("ec2-72-44-33-4.compute-1.amazonaws.com").imageId( "ami-6ea54007").instanceId("i-28a64341").instanceState(InstanceState.RUNNING).rawState( "running").instanceType(InstanceType.M1_LARGE).kernelId("aki-ba3adfd3").keyName( @@ -100,8 +101,9 @@ public class DescribeInstancesResponseHandlerTest extends BaseEC2HandlerTest { .availabilityZone("us-east-1b").virtualizationType("paravirtual").privateDnsName( "10-251-50-132.ec2.internal")// product codes // ImmutableSet.of("774F4FF8") + .tags(ImmutableMap.of("Name","ec2-o", "Empty","")) .ramdiskId("ari-badbad00").rootDeviceType(RootDeviceType.INSTANCE_STORE).build(), - new RunningInstance.Builder().region(defaultRegion).groupName("default").amiLaunchIndex("23") + RunningInstance.builder().region(defaultRegion).groupName("default").amiLaunchIndex("23") .dnsName("ec2-72-44-33-6.compute-1.amazonaws.com").imageId("ami-6ea54007").instanceId( "i-28a64435").instanceState(InstanceState.RUNNING).rawState("running") .instanceType(InstanceType.M1_LARGE).kernelId("aki-ba3adfd3").keyName( @@ -125,7 +127,7 @@ public class DescribeInstancesResponseHandlerTest extends BaseEC2HandlerTest { public void testEBS() throws UnknownHostException { Set> contents = ImmutableSet.of(new Reservation(defaultRegion, - ImmutableSet.of("adriancole.ec2ebsingress"), ImmutableSet.of(new RunningInstance.Builder().region( + ImmutableSet.of("adriancole.ec2ebsingress"), ImmutableSet.of(RunningInstance.builder().region( defaultRegion).groupName("adriancole.ec2ebsingress").amiLaunchIndex("0").dnsName( "ec2-75-101-203-146.compute-1.amazonaws.com").imageId("ami-849875ed").instanceId("i-e564438d") .instanceState(InstanceState.RUNNING).rawState("running").instanceType(InstanceType.M1_SMALL) diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/xml/RunInstancesResponseHandlerTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/xml/RunInstancesResponseHandlerTest.java index 1b37c5396e..9b34e36d46 100644 --- a/apis/ec2/src/test/java/org/jclouds/ec2/xml/RunInstancesResponseHandlerTest.java +++ b/apis/ec2/src/test/java/org/jclouds/ec2/xml/RunInstancesResponseHandlerTest.java @@ -65,19 +65,19 @@ public class RunInstancesResponseHandlerTest extends BaseEC2HandlerTest { Reservation expected = new Reservation(defaultRegion, ImmutableSet .of("default"), ImmutableSet.of( - new RunningInstance.Builder().region(defaultRegion).groupName("default").amiLaunchIndex("0") + RunningInstance.builder().region(defaultRegion).groupName("default").amiLaunchIndex("0") .imageId("ami-60a54009").instanceId("i-2ba64342").instanceState(InstanceState.PENDING).rawState( "pending").instanceType(InstanceType.M1_SMALL).keyName("example-key-name").launchTime( dateService.iso8601DateParse("2007-08-07T11:51:50.000Z"))// MonitoringState.ENABLED, .availabilityZone("us-east-1b").build(), - new RunningInstance.Builder().region(defaultRegion).groupName("default").amiLaunchIndex("1") + RunningInstance.builder().region(defaultRegion).groupName("default").amiLaunchIndex("1") .imageId("ami-60a54009").instanceId("i-2bc64242").instanceState(InstanceState.PENDING).rawState( "pending").instanceType(InstanceType.M1_SMALL).keyName("example-key-name").launchTime( dateService.iso8601DateParse("2007-08-07T11:51:50.000Z"))// MonitoringState.ENABLED, .availabilityZone("us-east-1b").build(), - new RunningInstance.Builder().region(defaultRegion).groupName("default").amiLaunchIndex("2") + RunningInstance.builder().region(defaultRegion).groupName("default").amiLaunchIndex("2") .imageId("ami-60a54009").instanceId("i-2be64332").instanceState(InstanceState.PENDING).rawState( "pending").instanceType(InstanceType.M1_SMALL).keyName("example-key-name").launchTime( dateService.iso8601DateParse("2007-08-07T11:51:50.000Z"))// MonitoringState.ENABLED, @@ -98,7 +98,7 @@ public class RunInstancesResponseHandlerTest extends BaseEC2HandlerTest { Reservation expected = new Reservation(defaultRegion, ImmutableSet .of("jclouds#greenqloud-computeblock"), ImmutableSet.of( - new RunningInstance.Builder().region(defaultRegion).groupName("jclouds#greenqloud-computeblock").amiLaunchIndex("0") + RunningInstance.builder().region(defaultRegion).groupName("jclouds#greenqloud-computeblock").amiLaunchIndex("0") .imageId("qmi-9ac92558").instanceId("i-01b0dac3").instanceState(InstanceState.PENDING).rawState( "pending").instanceType(InstanceType.M1_SMALL).keyName("jclouds#greenqloud-computeblock#35") .launchTime(dateService.iso8601DateParse("2012-06-15T19:06:35.000+00:00")) diff --git a/apis/ec2/src/test/resources/describe_instances.xml b/apis/ec2/src/test/resources/describe_instances.xml index 524a81c740..ac146ed983 100644 --- a/apis/ec2/src/test/resources/describe_instances.xml +++ b/apis/ec2/src/test/resources/describe_instances.xml @@ -38,6 +38,16 @@ aki-ba3adfd3 ari-badbad00 xen + + + Name + ec2-o + + + Empty + + + i-28a64435 diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/AWSEC2ApiMetadata.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/AWSEC2ApiMetadata.java index 2c498149d2..a232d3b08a 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/AWSEC2ApiMetadata.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/AWSEC2ApiMetadata.java @@ -21,7 +21,6 @@ package org.jclouds.aws.ec2; import static java.util.concurrent.TimeUnit.MINUTES; import static java.util.concurrent.TimeUnit.SECONDS; import static org.jclouds.Constants.PROPERTY_TIMEOUTS_PREFIX; -import static org.jclouds.aws.ec2.reference.AWSEC2Constants.PROPERTY_EC2_GENERATE_INSTANCE_NAMES; import static org.jclouds.ec2.reference.EC2Constants.PROPERTY_EC2_AMI_OWNERS; import java.util.Properties; @@ -66,13 +65,11 @@ public class AWSEC2ApiMetadata extends EC2ApiMetadata { Properties properties = EC2ApiMetadata.defaultProperties(); properties.setProperty(PROPERTY_TIMEOUTS_PREFIX + "default", SECONDS.toMillis(90) + ""); properties.setProperty(PROPERTY_TIMEOUTS_PREFIX + "SpotInstanceClient.describeSpotPriceHistoryInRegion", MINUTES.toMillis(2) + ""); - properties.remove(PROPERTY_EC2_AMI_OWNERS); // auth fail sometimes happens in EC2, as the rc.local script that injects the // authorized key executes after ssh has started. properties.setProperty("jclouds.ssh.max-retries", "7"); properties.setProperty("jclouds.ssh.retry-auth", "true"); - properties.setProperty(PROPERTY_EC2_GENERATE_INSTANCE_NAMES, "true"); return properties; } diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/AWSEC2AsyncClient.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/AWSEC2AsyncClient.java index ec58e541d7..b3ce640953 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/AWSEC2AsyncClient.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/AWSEC2AsyncClient.java @@ -25,7 +25,6 @@ import org.jclouds.aws.ec2.services.AWSSecurityGroupAsyncClient; import org.jclouds.aws.ec2.services.MonitoringAsyncClient; import org.jclouds.aws.ec2.services.PlacementGroupAsyncClient; import org.jclouds.aws.ec2.services.SpotInstanceAsyncClient; -import org.jclouds.aws.ec2.services.TagAsyncClient; import org.jclouds.ec2.EC2AsyncClient; import org.jclouds.rest.annotations.Delegate; @@ -82,10 +81,4 @@ public interface AWSEC2AsyncClient extends EC2AsyncClient { */ @Delegate SpotInstanceAsyncClient getSpotInstanceServices(); - - /** - * Provides asynchronous access to SpotInstance services. - */ - @Delegate - TagAsyncClient getTagServices(); } diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/AWSEC2Client.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/AWSEC2Client.java index 37e8c58448..0b36cd3ed9 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/AWSEC2Client.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/AWSEC2Client.java @@ -25,7 +25,6 @@ import org.jclouds.aws.ec2.services.AWSSecurityGroupClient; import org.jclouds.aws.ec2.services.MonitoringClient; import org.jclouds.aws.ec2.services.PlacementGroupClient; import org.jclouds.aws.ec2.services.SpotInstanceClient; -import org.jclouds.aws.ec2.services.TagClient; import org.jclouds.ec2.EC2Client; import org.jclouds.rest.annotations.Delegate; @@ -81,10 +80,4 @@ public interface AWSEC2Client extends EC2Client { */ @Delegate SpotInstanceClient getSpotInstanceServices(); - - /** - * Provides synchronous access to Tag services. - */ - @Delegate - TagClient getTagServices(); } diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/AWSEC2ComputeService.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/AWSEC2ComputeService.java index 23d5c8775f..e593bb5da3 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/AWSEC2ComputeService.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/AWSEC2ComputeService.java @@ -18,13 +18,11 @@ */ package org.jclouds.aws.ec2.compute; -import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; -import static com.google.common.collect.Iterables.transform; -import static org.jclouds.aws.ec2.reference.AWSEC2Constants.PROPERTY_EC2_GENERATE_INSTANCE_NAMES; import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_RUNNING; import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_SUSPENDED; import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_TERMINATED; +import static org.jclouds.ec2.reference.EC2Constants.PROPERTY_EC2_GENERATE_INSTANCE_NAMES; import java.util.Map; import java.util.Set; @@ -38,20 +36,15 @@ import javax.inject.Provider; import javax.inject.Singleton; import org.jclouds.Constants; -import org.jclouds.aws.ec2.AWSEC2AsyncClient; import org.jclouds.aws.ec2.AWSEC2Client; import org.jclouds.aws.ec2.domain.PlacementGroup; import org.jclouds.aws.ec2.domain.PlacementGroup.State; -import org.jclouds.aws.util.AWSUtils; import org.jclouds.collect.Memoized; import org.jclouds.compute.ComputeServiceContext; -import org.jclouds.compute.RunNodesException; import org.jclouds.compute.callables.RunScriptOnNode; import org.jclouds.compute.domain.Hardware; import org.jclouds.compute.domain.Image; import org.jclouds.compute.domain.NodeMetadata; -import org.jclouds.compute.domain.NodeMetadataBuilder; -import org.jclouds.compute.domain.Template; import org.jclouds.compute.domain.TemplateBuilder; import org.jclouds.compute.extensions.ImageExtension; import org.jclouds.compute.functions.GroupNamingConvention; @@ -71,20 +64,15 @@ import org.jclouds.domain.Credentials; import org.jclouds.domain.Location; import org.jclouds.ec2.compute.EC2ComputeService; import org.jclouds.ec2.compute.domain.RegionAndName; -import org.jclouds.ec2.compute.options.EC2TemplateOptions; import org.jclouds.ec2.domain.KeyPair; import org.jclouds.scriptbuilder.functions.InitAdminAccess; import org.jclouds.util.Preconditions2; 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.base.Supplier; import com.google.common.cache.LoadingCache; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Iterables; /** * @author Adrian Cole @@ -94,96 +82,39 @@ public class AWSEC2ComputeService extends EC2ComputeService { private final LoadingCache placementGroupMap; private final Predicate placementGroupDeleted; - private final AWSEC2Client ec2Client; - private final AWSEC2AsyncClient aclient; - private final boolean generateInstanceNames; + private final AWSEC2Client client; @Inject protected AWSEC2ComputeService(ComputeServiceContext context, Map credentialStore, - @Memoized Supplier> images, @Memoized Supplier> sizes, - @Memoized Supplier> locations, ListNodesStrategy listNodesStrategy, - GetImageStrategy getImageStrategy, GetNodeMetadataStrategy getNodeMetadataStrategy, - CreateNodesInGroupThenAddToSet runNodesAndAddToSetStrategy, RebootNodeStrategy rebootNodeStrategy, - DestroyNodeStrategy destroyNodeStrategy, ResumeNodeStrategy startNodeStrategy, - SuspendNodeStrategy stopNodeStrategy, Provider templateBuilderProvider, - @Named("DEFAULT") Provider templateOptionsProvider, - @Named(TIMEOUT_NODE_RUNNING) Predicate> nodeRunning, - @Named(TIMEOUT_NODE_TERMINATED) Predicate> nodeTerminated, - @Named(TIMEOUT_NODE_SUSPENDED) Predicate> nodeSuspended, - InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory, - RunScriptOnNode.Factory runScriptOnNodeFactory, InitAdminAccess initAdminAccess, - PersistNodeCredentials persistNodeCredentials, Timeouts timeouts, - @Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor, AWSEC2Client ec2Client, - ConcurrentMap credentialsMap, - @Named("SECURITY") LoadingCache securityGroupMap, - @Named("PLACEMENT") LoadingCache placementGroupMap, - @Named("DELETED") Predicate placementGroupDeleted, - @Named(PROPERTY_EC2_GENERATE_INSTANCE_NAMES) boolean generateInstanceNames, AWSEC2AsyncClient aclient, - Optional imageExtension, GroupNamingConvention.Factory namingConvention) { - super(context, credentialStore, images, sizes, locations, listNodesStrategy, getImageStrategy, getNodeMetadataStrategy, - runNodesAndAddToSetStrategy, rebootNodeStrategy, destroyNodeStrategy, startNodeStrategy, - stopNodeStrategy, templateBuilderProvider, templateOptionsProvider, nodeRunning, nodeTerminated, - nodeSuspended, initScriptRunnerFactory, runScriptOnNodeFactory, initAdminAccess, persistNodeCredentials, - timeouts, executor, ec2Client, credentialsMap, securityGroupMap, imageExtension, namingConvention); - this.ec2Client = ec2Client; + @Memoized Supplier> images, @Memoized Supplier> sizes, + @Memoized Supplier> locations, ListNodesStrategy listNodesStrategy, + GetImageStrategy getImageStrategy, GetNodeMetadataStrategy getNodeMetadataStrategy, + CreateNodesInGroupThenAddToSet runNodesAndAddToSetStrategy, RebootNodeStrategy rebootNodeStrategy, + DestroyNodeStrategy destroyNodeStrategy, ResumeNodeStrategy startNodeStrategy, + SuspendNodeStrategy stopNodeStrategy, Provider templateBuilderProvider, + @Named("DEFAULT") Provider templateOptionsProvider, + @Named(TIMEOUT_NODE_RUNNING) Predicate> nodeRunning, + @Named(TIMEOUT_NODE_TERMINATED) Predicate> nodeTerminated, + @Named(TIMEOUT_NODE_SUSPENDED) Predicate> nodeSuspended, + InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory, + RunScriptOnNode.Factory runScriptOnNodeFactory, InitAdminAccess initAdminAccess, + PersistNodeCredentials persistNodeCredentials, Timeouts timeouts, + @Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor, AWSEC2Client client, + ConcurrentMap credentialsMap, + @Named("SECURITY") LoadingCache securityGroupMap, + @Named("PLACEMENT") LoadingCache placementGroupMap, + @Named("DELETED") Predicate placementGroupDeleted, Optional imageExtension, + GroupNamingConvention.Factory namingConvention, + @Named(PROPERTY_EC2_GENERATE_INSTANCE_NAMES) boolean generateInstanceNames) { + super(context, credentialStore, images, sizes, locations, listNodesStrategy, getImageStrategy, + getNodeMetadataStrategy, runNodesAndAddToSetStrategy, rebootNodeStrategy, destroyNodeStrategy, + startNodeStrategy, stopNodeStrategy, templateBuilderProvider, templateOptionsProvider, nodeRunning, + nodeTerminated, nodeSuspended, initScriptRunnerFactory, runScriptOnNodeFactory, initAdminAccess, + persistNodeCredentials, timeouts, executor, client, credentialsMap, securityGroupMap, imageExtension, + namingConvention, generateInstanceNames); + this.client = client; this.placementGroupMap = placementGroupMap; this.placementGroupDeleted = placementGroupDeleted; - this.generateInstanceNames = generateInstanceNames; - this.aclient = checkNotNull(aclient, "aclient"); - } - - @Override - public Set createNodesInGroup(String group, int count, final Template template) - throws RunNodesException { - Set nodes = super.createNodesInGroup(group, count, template); - // tags from spot requests do not propagate to running instances - // automatically - if (templateWasASpotRequestWithUserMetadata(template)) { - addTagsToNodesFromUserMetadataInTemplate(nodes, group, template); - nodes = addUserMetadataFromTemplateOptionsToNodes(template, group, nodes); - } - return nodes; - } - - protected void addTagsToNodesFromUserMetadataInTemplate(Set nodes, String group, - final Template template) { - String region = AWSUtils.getRegionFromLocationOrNull(template.getLocation()); - if (template.getOptions().getUserMetadata().size() > 0 || generateInstanceNames) { - for (String id : transform(nodes, new Function() { - - @Override - public String apply(NodeMetadata arg0) { - return arg0.getProviderId(); - } - - })) - aclient.getTagServices().createTagsInRegion(region, ImmutableSet.of(id), - metadataForId(id, group, template.getOptions().getUserMetadata())); - } - } - - private Map metadataForId(String id, String group, Map metadata) { - return generateInstanceNames && !metadata.containsKey("Name") ? ImmutableMap. builder().putAll( - metadata).put("Name", id.replaceAll(".*-", group + "-")).build() : metadata; - } - - protected boolean templateWasASpotRequestWithUserMetadata(final Template template) { - return template.getOptions().getUserMetadata().size() > 0 - && AWSEC2TemplateOptions.class.cast(template.getOptions()).getSpotPrice() != null; - } - - protected Set addUserMetadataFromTemplateOptionsToNodes(final Template template, - final String group, Set nodes) { - nodes = ImmutableSet.copyOf(Iterables.transform(nodes, new Function() { - - @Override - public NodeMetadata apply(NodeMetadata arg0) { - Map md = metadataForId(arg0.getProviderId(), group, template.getOptions().getUserMetadata()); - return NodeMetadataBuilder.fromNodeMetadata(arg0).name(md.get("Name")).userMetadata(md).build(); - } - - })); - return nodes; } @VisibleForTesting @@ -193,10 +124,10 @@ public class AWSEC2ComputeService extends EC2ComputeService { // http://docs.amazonwebservices.com/AWSEC2/latest/UserGuide/index.html?using_cluster_computing.html String placementGroup = String.format("jclouds#%s#%s", group, region); try { - if (ec2Client.getPlacementGroupServices().describePlacementGroupsInRegion(region, placementGroup).size() > 0) { + if (client.getPlacementGroupServices().describePlacementGroupsInRegion(region, placementGroup).size() > 0) { logger.debug(">> deleting placementGroup(%s)", placementGroup); try { - ec2Client.getPlacementGroupServices().deletePlacementGroupInRegion(region, placementGroup); + client.getPlacementGroupServices().deletePlacementGroupInRegion(region, placementGroup); checkState(placementGroupDeleted.apply(new PlacementGroup(region, placementGroup, "cluster", State.PENDING)), String.format("placementGroup region(%s) name(%s) failed to delete", region, placementGroup)); @@ -218,11 +149,11 @@ public class AWSEC2ComputeService extends EC2ComputeService { } /** - * returns template options, except of type {@link EC2TemplateOptions}. + * returns template options, except of type {@link AWSEC2TemplateOptions}. */ @Override - public EC2TemplateOptions templateOptions() { - return EC2TemplateOptions.class.cast(super.templateOptions()); + public AWSEC2TemplateOptions templateOptions() { + return AWSEC2TemplateOptions.class.cast(super.templateOptions()); } } diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceContextModule.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceContextModule.java index fa632a9a9b..c7274804f8 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceContextModule.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceContextModule.java @@ -30,7 +30,7 @@ import javax.inject.Singleton; import org.jclouds.aws.ec2.compute.AWSEC2TemplateBuilderImpl; import org.jclouds.aws.ec2.compute.functions.AWSRunningInstanceToNodeMetadata; -import org.jclouds.aws.ec2.compute.predicates.AWSEC2InstancePresent; +import org.jclouds.aws.ec2.compute.functions.PresentSpotRequestsAndInstances; import org.jclouds.aws.ec2.compute.strategy.AWSEC2CreateNodesInGroupThenAddToSet; import org.jclouds.aws.ec2.compute.strategy.AWSEC2DestroyNodeStrategy; import org.jclouds.aws.ec2.compute.strategy.AWSEC2GetNodeMetadataStrategy; @@ -44,11 +44,11 @@ import org.jclouds.compute.extensions.ImageExtension; import org.jclouds.compute.options.TemplateOptions; import org.jclouds.ec2.compute.config.EC2BindComputeStrategiesByClass; import org.jclouds.ec2.compute.domain.RegionAndName; +import org.jclouds.ec2.compute.functions.PresentInstances; import org.jclouds.ec2.compute.functions.RunningInstanceToNodeMetadata; import org.jclouds.ec2.compute.internal.EC2TemplateBuilderImpl; import org.jclouds.ec2.compute.loaders.RegionAndIdToImage; import org.jclouds.ec2.compute.options.EC2TemplateOptions; -import org.jclouds.ec2.compute.predicates.InstancePresent; import org.jclouds.ec2.compute.strategy.CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions; import org.jclouds.ec2.compute.strategy.EC2CreateNodesInGroupThenAddToSet; import org.jclouds.ec2.compute.strategy.EC2DestroyNodeStrategy; @@ -92,7 +92,7 @@ public class AWSEC2ComputeServiceContextModule extends BaseComputeServiceContext bind(EC2GetNodeMetadataStrategy.class).to(AWSEC2GetNodeMetadataStrategy.class); bind(EC2ListNodesStrategy.class).to(AWSEC2ListNodesStrategy.class); bind(EC2DestroyNodeStrategy.class).to(AWSEC2DestroyNodeStrategy.class); - bind(InstancePresent.class).to(AWSEC2InstancePresent.class); + bind(PresentInstances.class).to(PresentSpotRequestsAndInstances.class); bind(EC2CreateNodesInGroupThenAddToSet.class).to(AWSEC2CreateNodesInGroupThenAddToSet.class); bind(RunningInstanceToNodeMetadata.class).to(AWSRunningInstanceToNodeMetadata.class); } @@ -100,7 +100,7 @@ public class AWSEC2ComputeServiceContextModule extends BaseComputeServiceContext protected void installDependencies() { install(new AWSEC2ComputeServiceDependenciesModule()); } - + @Override protected boolean shouldEagerlyParseImages(Injector injector) { Map queries = injector.getInstance(Key.get(new TypeLiteral>() { diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/functions/AWSRunningInstanceToNodeMetadata.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/functions/AWSRunningInstanceToNodeMetadata.java index f9b12fc84c..6712f4a732 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/functions/AWSRunningInstanceToNodeMetadata.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/functions/AWSRunningInstanceToNodeMetadata.java @@ -18,8 +18,6 @@ */ package org.jclouds.aws.ec2.compute.functions; -import static org.jclouds.compute.util.ComputeServiceUtils.addMetadataAndParseTagsFromValuesOfEmptyString; - import java.util.Map; import java.util.Set; @@ -31,8 +29,8 @@ import org.jclouds.collect.Memoized; import org.jclouds.compute.domain.Hardware; import org.jclouds.compute.domain.HardwareBuilder; import org.jclouds.compute.domain.Image; -import org.jclouds.compute.domain.NodeMetadataBuilder; import org.jclouds.compute.domain.NodeMetadata.Status; +import org.jclouds.compute.domain.NodeMetadataBuilder; import org.jclouds.compute.functions.GroupNamingConvention; import org.jclouds.domain.Credentials; import org.jclouds.domain.Location; @@ -82,11 +80,4 @@ public class AWSRunningInstanceToNodeMetadata extends RunningInstanceToNodeMetad return HardwareBuilder.fromHardware(in).hypervisor(awsInstance.getHypervisor().toString()).build(); } - @Override - protected NodeMetadataBuilder buildInstance(RunningInstance instance, NodeMetadataBuilder builder) { - AWSRunningInstance awsInstance = AWSRunningInstance.class.cast(instance); - builder.name(awsInstance.getTags().get("Name")); - addMetadataAndParseTagsFromValuesOfEmptyString(builder, awsInstance.getTags()); - return super.buildInstance(instance, builder); - } } diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/functions/PresentSpotRequestsAndInstances.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/functions/PresentSpotRequestsAndInstances.java new file mode 100644 index 0000000000..855a1c1a3a --- /dev/null +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/functions/PresentSpotRequestsAndInstances.java @@ -0,0 +1,96 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.jclouds.aws.ec2.compute.functions; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Predicates.compose; +import static com.google.common.base.Predicates.containsPattern; +import static com.google.common.collect.Iterables.any; +import static com.google.common.collect.Iterables.toArray; +import static com.google.common.collect.Iterables.transform; +import static com.google.common.collect.Multimaps.index; +import static com.google.common.collect.Multimaps.transformValues; +import static org.jclouds.ec2.compute.domain.RegionAndName.nameFunction; +import static org.jclouds.ec2.compute.domain.RegionAndName.regionFunction; + +import java.util.Collection; +import java.util.Set; + +import javax.inject.Singleton; + +import org.jclouds.aws.ec2.AWSEC2Client; +import org.jclouds.aws.ec2.domain.AWSRunningInstance; +import org.jclouds.aws.ec2.domain.SpotInstanceRequest; +import org.jclouds.aws.ec2.functions.SpotInstanceRequestToAWSRunningInstance; +import org.jclouds.ec2.compute.domain.RegionAndName; +import org.jclouds.ec2.compute.functions.PresentInstances; +import org.jclouds.ec2.domain.RunningInstance; + +import com.google.common.base.Function; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableSet.Builder; +import com.google.common.collect.Multimap; +import com.google.inject.Inject; + +/** + * returns either the instances present in the list, or spot instances, if they ids start with {@code sir-}. Makes a + * single rest call per aggregate on region. + * + * @author Adrian Cole + */ +@Singleton +public class PresentSpotRequestsAndInstances extends PresentInstances { + + private final AWSEC2Client client; + private final Function spotConverter; + + @Inject + public PresentSpotRequestsAndInstances(AWSEC2Client client, Function spotConverter) { + super(client); + this.client = checkNotNull(client, "client"); + this.spotConverter = checkNotNull(spotConverter, "spotConverter"); + } + + @Override + public Set apply(Set regionAndIds) { + if (checkNotNull(regionAndIds, "regionAndIds").isEmpty()) + return ImmutableSet.of(); + if (any(regionAndIds, compose(containsPattern("sir-"), nameFunction()))) + return getSpots(regionAndIds); + return super.apply(regionAndIds); + } + + protected Set getSpots(Set regionAndIds) { + Builder builder = ImmutableSet. builder(); + Multimap regionToSpotIds = transformValues(index(regionAndIds, regionFunction()), nameFunction()); + for (String region : regionToSpotIds.keySet()) { + Collection spotIds = regionToSpotIds.get(region); + logger.trace("looking for spots %s in region %s", spotIds, region); + builder.addAll(transform( + client.getSpotInstanceServices().describeSpotInstanceRequestsInRegion(region, + toArray(spotIds, String.class)), spotConverter)); + } + return builder.build(); + } + + @Override + public String toString() { + return "presentSpotRequestsAndInstances()"; + } +} diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/predicates/AWSEC2InstancePresent.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/predicates/AWSEC2InstancePresent.java deleted file mode 100644 index 8644e820f1..0000000000 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/predicates/AWSEC2InstancePresent.java +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Licensed to jclouds, Inc. (jclouds) under one or more - * contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. jclouds licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.jclouds.aws.ec2.compute.predicates; - -import static com.google.common.base.Preconditions.checkNotNull; - -import javax.inject.Singleton; - -import org.jclouds.aws.ec2.AWSEC2Client; -import org.jclouds.ec2.compute.domain.RegionAndName; -import org.jclouds.ec2.compute.predicates.InstancePresent; - -import com.google.common.collect.Iterables; -import com.google.inject.Inject; - -/** - * - * @author Adrian Cole - */ -@Singleton -public class AWSEC2InstancePresent extends InstancePresent { - - private final AWSEC2Client client; - - @Inject - public AWSEC2InstancePresent(AWSEC2Client client) { - super(client); - this.client = checkNotNull(client, "client"); - } - - @Override - protected void refresh(RegionAndName instance) { - if (instance.getName().indexOf("sir-") != 0) - super.refresh(instance); - else - Iterables.getOnlyElement(client.getSpotInstanceServices().describeSpotInstanceRequestsInRegion( - instance.getRegion(), instance.getName())); - } -} 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 e2939a316c..ea08151396 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 @@ -19,30 +19,27 @@ package org.jclouds.aws.ec2.compute.strategy; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.collect.Iterables.transform; -import static org.jclouds.aws.ec2.reference.AWSEC2Constants.PROPERTY_EC2_GENERATE_INSTANCE_NAMES; import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_RUNNING; -import static org.jclouds.compute.util.ComputeServiceUtils.metadataAndTagsAsValuesOfEmptyString; +import static org.jclouds.ec2.reference.EC2Constants.PROPERTY_EC2_GENERATE_INSTANCE_NAMES; 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.Provider; import javax.inject.Singleton; -import org.jclouds.aws.ec2.AWSEC2AsyncClient; import org.jclouds.aws.ec2.AWSEC2Client; import org.jclouds.aws.ec2.compute.AWSEC2TemplateOptions; -import org.jclouds.aws.ec2.compute.predicates.AWSEC2InstancePresent; +import org.jclouds.aws.ec2.compute.functions.PresentSpotRequestsAndInstances; import org.jclouds.aws.ec2.domain.LaunchSpecification; import org.jclouds.aws.ec2.functions.SpotInstanceRequestToAWSRunningInstance; import org.jclouds.aws.ec2.options.AWSRunInstancesOptions; import org.jclouds.aws.ec2.options.RequestSpotInstancesOptions; import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.compute.domain.Template; -import org.jclouds.compute.domain.TemplateBuilder; import org.jclouds.compute.options.TemplateOptions; import org.jclouds.compute.reference.ComputeServiceConstants; import org.jclouds.compute.util.ComputeUtils; @@ -58,7 +55,6 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Function; import com.google.common.base.Predicate; import com.google.common.cache.LoadingCache; -import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; /** @@ -70,39 +66,33 @@ public class AWSEC2CreateNodesInGroupThenAddToSet extends EC2CreateNodesInGroupT @Resource @Named(ComputeServiceConstants.COMPUTE_LOGGER) - protected Logger logger = Logger.NULL; + private Logger logger = Logger.NULL; @VisibleForTesting - final AWSEC2Client client; - final SpotInstanceRequestToAWSRunningInstance spotConverter; - final AWSEC2AsyncClient aclient; - final boolean generateInstanceNames; + private final AWSEC2Client client; + private final SpotInstanceRequestToAWSRunningInstance spotConverter; @Inject protected AWSEC2CreateNodesInGroupThenAddToSet( - AWSEC2Client client, - @Named("ELASTICIP") LoadingCache elasticIpCache, - @Named(TIMEOUT_NODE_RUNNING) Predicate> nodeRunning, - AWSEC2AsyncClient aclient, - @Named(PROPERTY_EC2_GENERATE_INSTANCE_NAMES) boolean generateInstanceNames, - Provider templateBuilderProvider, - CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions createKeyPairAndSecurityGroupsAsNeededAndReturncustomize, - AWSEC2InstancePresent instancePresent, - Function runningInstanceToNodeMetadata, - LoadingCache instanceToCredentials, Map credentialStore, - ComputeUtils utils, SpotInstanceRequestToAWSRunningInstance spotConverter) { - super(client, elasticIpCache, nodeRunning, templateBuilderProvider, createKeyPairAndSecurityGroupsAsNeededAndReturncustomize, - instancePresent, runningInstanceToNodeMetadata, instanceToCredentials, credentialStore, utils); + AWSEC2Client client, + @Named("ELASTICIP") LoadingCache elasticIpCache, + @Named(TIMEOUT_NODE_RUNNING) Predicate> nodeRunning, + @Named(PROPERTY_EC2_GENERATE_INSTANCE_NAMES) boolean generateInstanceNames, + CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions createKeyPairAndSecurityGroupsAsNeededAndReturncustomize, + PresentSpotRequestsAndInstances instancePresent, + Function runningInstanceToNodeMetadata, + LoadingCache instanceToCredentials, + Map credentialStore, ComputeUtils utils, + SpotInstanceRequestToAWSRunningInstance spotConverter) { + super(client, elasticIpCache, nodeRunning, createKeyPairAndSecurityGroupsAsNeededAndReturncustomize, + instancePresent, runningInstanceToNodeMetadata, instanceToCredentials, credentialStore, utils); this.client = checkNotNull(client, "client"); - this.aclient = checkNotNull(aclient, "aclient"); this.spotConverter = checkNotNull(spotConverter, "spotConverter"); - this.generateInstanceNames = generateInstanceNames; } @Override - protected Iterable createNodesInRegionAndZone(String region, String zone, String group, + protected Set createNodesInRegionAndZone(String region, String zone, String group, int count, Template template, RunInstancesOptions instanceOptions) { - Map tags = metadataAndTagsAsValuesOfEmptyString(template.getOptions()); Float spotPrice = getSpotPriceOrNull(template.getOptions()); if (spotPrice != null) { LaunchSpecification spec = AWSRunInstancesOptions.class.cast(instanceOptions).getLaunchSpecificationBuilder() @@ -111,38 +101,12 @@ public class AWSEC2CreateNodesInGroupThenAddToSet extends EC2CreateNodesInGroupT if (logger.isDebugEnabled()) logger.debug(">> requesting %d spot instances region(%s) price(%f) spec(%s) options(%s)", count, region, spotPrice, spec, options); - return addTagsToInstancesInRegion(tags, transform(client - .getSpotInstanceServices().requestSpotInstancesInRegion(region, spotPrice, count, spec, options), - spotConverter), region, group); - } else { - return addTagsToInstancesInRegion(tags, super.createNodesInRegionAndZone( - region, zone, group, count, template, instanceOptions), region, group); + return ImmutableSet. copyOf(transform(client.getSpotInstanceServices() + .requestSpotInstancesInRegion(region, spotPrice, count, spec, options), spotConverter)); } - + return super.createNodesInRegionAndZone(region, zone, group, count, template, instanceOptions); } - - public Iterable addTagsToInstancesInRegion(Map metadata, - Iterable iterable, String region, String group) { - if (metadata.size() > 0 || generateInstanceNames) { - for (String id : transform(iterable, new Function() { - - @Override - public String apply(RunningInstance arg0) { - return arg0.getId(); - } - - })) - aclient.getTagServices() - .createTagsInRegion(region, ImmutableSet.of(id), metadataForId(id, group, metadata)); - } - return iterable; - } - - private Map metadataForId(String id, String group, Map metadata) { - return generateInstanceNames && !metadata.containsKey("Name") ? ImmutableMap. builder().putAll( - metadata).put("Name", id.replaceAll(".*-", group + "-")).build() : metadata; - } - + private Float getSpotPriceOrNull(TemplateOptions options) { return options instanceof AWSEC2TemplateOptions ? AWSEC2TemplateOptions.class.cast(options).getSpotPrice() : null; } diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/config/AWSEC2RestClientModule.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/config/AWSEC2RestClientModule.java index feb36d0bc4..c4e35505e5 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/config/AWSEC2RestClientModule.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/config/AWSEC2RestClientModule.java @@ -25,6 +25,8 @@ import javax.inject.Singleton; import org.jclouds.aws.ec2.AWSEC2AsyncClient; import org.jclouds.aws.ec2.AWSEC2Client; import org.jclouds.aws.ec2.domain.AWSRunningInstance; +import org.jclouds.aws.ec2.domain.SpotInstanceRequest; +import org.jclouds.aws.ec2.functions.SpotInstanceRequestToAWSRunningInstance; import org.jclouds.aws.ec2.options.AWSRunInstancesOptions; import org.jclouds.aws.ec2.services.AWSAMIAsyncClient; import org.jclouds.aws.ec2.services.AWSAMIClient; @@ -40,12 +42,9 @@ import org.jclouds.aws.ec2.services.PlacementGroupAsyncClient; import org.jclouds.aws.ec2.services.PlacementGroupClient; import org.jclouds.aws.ec2.services.SpotInstanceAsyncClient; import org.jclouds.aws.ec2.services.SpotInstanceClient; -import org.jclouds.aws.ec2.services.TagAsyncClient; -import org.jclouds.aws.ec2.services.TagClient; import org.jclouds.ec2.EC2AsyncClient; import org.jclouds.ec2.EC2Client; import org.jclouds.ec2.config.EC2RestClientModule; -import org.jclouds.ec2.domain.RunningInstance; import org.jclouds.ec2.features.TagApi; import org.jclouds.ec2.features.TagAsyncApi; import org.jclouds.ec2.features.WindowsApi; @@ -67,9 +66,11 @@ import org.jclouds.ec2.services.WindowsAsyncClient; import org.jclouds.ec2.services.WindowsClient; import org.jclouds.rest.ConfiguresRestClient; +import com.google.common.base.Function; import com.google.common.collect.ImmutableMap; import com.google.common.reflect.TypeToken; import com.google.inject.Provides; +import com.google.inject.TypeLiteral; /** * Configures the EC2 connection. @@ -91,7 +92,6 @@ public class AWSEC2RestClientModule extends EC2RestClientModule>() { + }).to(SpotInstanceRequestToAWSRunningInstance.class); super.configure(); } } 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 06605b208f..ad50dbe94e 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 @@ -32,7 +32,6 @@ import org.jclouds.ec2.domain.RunningInstance; import org.jclouds.javax.annotation.Nullable; import com.google.common.base.Objects.ToStringHelper; -import com.google.common.base.Strings; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Maps; @@ -46,11 +45,17 @@ import com.google.common.collect.Sets; * @author Adrian Cole */ public class AWSRunningInstance extends RunningInstance { + public static Builder builder() { return new Builder(); } - public static class Builder extends org.jclouds.ec2.domain.RunningInstance.Builder { + @Override + public Builder toBuilder() { + return new Builder().fromRunningInstance(this); + } + + public static class Builder extends org.jclouds.ec2.domain.RunningInstance.Builder { private MonitoringState monitoringState; private String placementGroup; private Set productCodes = Sets.newLinkedHashSet(); @@ -59,19 +64,7 @@ public class AWSRunningInstance extends RunningInstance { private String vpcId; private Hypervisor hypervisor; private Map securityGroupIdToNames = Maps.newLinkedHashMap(); - private Map tags = Maps.newLinkedHashMap(); - public Builder tags(Map tags) { - this.tags = ImmutableMap.copyOf(checkNotNull(tags, "tags")); - return this; - } - - public Builder tag(String key, String value) { - if (key != null) - this.tags.put(key, Strings.nullToEmpty(value)); - return this; - } - public Builder securityGroupIdToNames(Map securityGroupIdToNames) { this.securityGroupIdToNames = ImmutableMap.copyOf(checkNotNull(securityGroupIdToNames, "securityGroupIdToNames")); @@ -125,131 +118,6 @@ public class AWSRunningInstance extends RunningInstance { return this; } - @Override - public Builder amiLaunchIndex(String amiLaunchIndex) { - return Builder.class.cast(super.amiLaunchIndex(amiLaunchIndex)); - } - - @Override - public Builder availabilityZone(String availabilityZone) { - return Builder.class.cast(super.availabilityZone(availabilityZone)); - } - - @Override - public Builder devices(Map ebsBlockDevices) { - return Builder.class.cast(super.devices(ebsBlockDevices)); - } - - @Override - public Builder dnsName(String dnsName) { - return Builder.class.cast(super.dnsName(dnsName)); - } - - @Override - public Builder imageId(String imageId) { - return Builder.class.cast(super.imageId(imageId)); - } - - @Override - public Builder instanceId(String instanceId) { - return Builder.class.cast(super.instanceId(instanceId)); - } - - @Override - public Builder instanceState(InstanceState instanceState) { - return Builder.class.cast(super.instanceState(instanceState)); - } - - @Override - public Builder rawState(String rawState) { - return Builder.class.cast(super.rawState(rawState)); - } - - @Override - public Builder instanceType(String instanceType) { - return Builder.class.cast(super.instanceType(instanceType)); - } - - @Override - public Builder ipAddress(String ipAddress) { - return Builder.class.cast(super.ipAddress(ipAddress)); - } - - @Override - public Builder kernelId(String kernelId) { - return Builder.class.cast(super.kernelId(kernelId)); - } - - @Override - public Builder keyName(String keyName) { - return Builder.class.cast(super.keyName(keyName)); - } - - @Override - public Builder launchTime(Date launchTime) { - return Builder.class.cast(super.launchTime(launchTime)); - } - - @Override - public Builder platform(String platform) { - return Builder.class.cast(super.platform(platform)); - } - - @Override - public Builder privateDnsName(String privateDnsName) { - return Builder.class.cast(super.privateDnsName(privateDnsName)); - } - - @Override - public Builder privateIpAddress(String privateIpAddress) { - return Builder.class.cast(super.privateIpAddress(privateIpAddress)); - } - - @Override - public Builder ramdiskId(String ramdiskId) { - return Builder.class.cast(super.ramdiskId(ramdiskId)); - } - - @Override - public Builder reason(String reason) { - return Builder.class.cast(super.reason(reason)); - } - - @Override - public Builder region(String region) { - return Builder.class.cast(super.region(region)); - } - - @Override - public Builder rootDeviceName(String rootDeviceName) { - return Builder.class.cast(super.rootDeviceName(rootDeviceName)); - } - - @Override - public Builder rootDeviceType(RootDeviceType rootDeviceType) { - return Builder.class.cast(super.rootDeviceType(rootDeviceType)); - } - - @Override - public Builder virtualizationType(String virtualizationType) { - return Builder.class.cast(super.virtualizationType(virtualizationType)); - } - - @Override - public Builder device(String key, BlockDevice value) { - return Builder.class.cast(super.device(key, value)); - } - - @Override - public Builder groupName(String groupName) { - return Builder.class.cast(super.groupName(groupName)); - } - - @Override - public Builder groupNames(Iterable groupNames) { - return Builder.class.cast(super.groupNames(groupNames)); - } - @Override public AWSRunningInstance build() { return new AWSRunningInstance(region, securityGroupIdToNames, amiLaunchIndex, dnsName, imageId, instanceId, @@ -258,6 +126,24 @@ public class AWSRunningInstance extends RunningInstance { rootDeviceName, ebsBlockDevices, monitoringState, placementGroup, productCodes, subnetId, spotInstanceRequestId, vpcId, hypervisor, tags); } + + @Override + public Builder fromRunningInstance(RunningInstance in) { + super.fromRunningInstance(in); + if (in instanceof AWSRunningInstance) { + AWSRunningInstance awsIn = AWSRunningInstance.class.cast(in); + monitoringState(awsIn.monitoringState).placementGroup(awsIn.placementGroup) + .productCodes(awsIn.productCodes).subnetId(awsIn.subnetId) + .spotInstanceRequestId(awsIn.spotInstanceRequestId).vpcId(awsIn.vpcId).hypervisor(awsIn.hypervisor) + .securityGroupIdToNames(awsIn.securityGroupIdToNames); + } + return this; + } + + @Override + protected Builder self() { + return this; + } } @@ -273,7 +159,6 @@ public class AWSRunningInstance extends RunningInstance { private final String vpcId; private final Hypervisor hypervisor; private final Map securityGroupIdToNames; - private final Map tags; protected AWSRunningInstance(String region, Map securityGroupIdToNames, String amiLaunchIndex, String dnsName, String imageId, String instanceId, InstanceState instanceState, String rawState, @@ -286,7 +171,7 @@ public class AWSRunningInstance extends RunningInstance { super(region, securityGroupIdToNames.values(), amiLaunchIndex, dnsName, imageId, instanceId, instanceState, rawState, instanceType, ipAddress, kernelId, keyName, launchTime, availabilityZone, virtualizationType, platform, privateDnsName, privateIpAddress, ramdiskId, reason, rootDeviceType, rootDeviceName, - ebsBlockDevices); + ebsBlockDevices, tags); this.monitoringState = checkNotNull(monitoringState, "monitoringState"); this.placementGroup = placementGroup; this.productCodes = ImmutableSet.copyOf(checkNotNull(productCodes, "productCodes")); @@ -296,7 +181,6 @@ public class AWSRunningInstance extends RunningInstance { this.hypervisor = checkNotNull(hypervisor, "hypervisor"); this.securityGroupIdToNames = ImmutableMap. copyOf(checkNotNull(securityGroupIdToNames, "securityGroupIdToNames")); - this.tags = ImmutableMap. copyOf(checkNotNull(tags, "tags")); } public Map getSecurityGroupIdToNames() { @@ -356,18 +240,11 @@ public class AWSRunningInstance extends RunningInstance { return subnetId; } - /** - * tags that are present in the instance - */ - public Map getTags() { - return tags; - } - @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("tags", tags); + .add("hypervisor", hypervisor); } } diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/functions/SpotInstanceRequestToAWSRunningInstance.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/functions/SpotInstanceRequestToAWSRunningInstance.java index 854000b8d1..97be487c87 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/functions/SpotInstanceRequestToAWSRunningInstance.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/functions/SpotInstanceRequestToAWSRunningInstance.java @@ -18,8 +18,6 @@ */ package org.jclouds.aws.ec2.functions; -import javax.inject.Singleton; - import org.jclouds.aws.ec2.domain.AWSRunningInstance; import org.jclouds.aws.ec2.domain.LaunchSpecification; import org.jclouds.aws.ec2.domain.MonitoringState; @@ -32,7 +30,6 @@ import com.google.common.base.Function; /** * @author Adrian Cole */ -@Singleton public class SpotInstanceRequestToAWSRunningInstance implements Function { @Override diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/reference/AWSEC2Constants.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/reference/AWSEC2Constants.java index 2f8f781064..213bee3265 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/reference/AWSEC2Constants.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/reference/AWSEC2Constants.java @@ -37,10 +37,5 @@ public interface AWSEC2Constants extends EC2Constants { public static final String PROPERTY_EC2_CC_AMI_QUERY = "jclouds.ec2.cc-ami-query"; public static final String PROPERTY_EC2_CC_REGIONS = "jclouds.ec2.cc-regions"; public static final String PROPERTY_EC2_AMI_QUERY = "jclouds.ec2.ami-query"; - /** - * If this property is set to true(default), jclouds generate a name for each instance based on - * the group. ex. i-ef34ae2 becomes hadoop-ef34ae2. - */ - public static final String PROPERTY_EC2_GENERATE_INSTANCE_NAMES = "jclouds.ec2.generate-instance-names"; } diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/TagAsyncClient.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/TagAsyncClient.java deleted file mode 100644 index 1b52bec2f9..0000000000 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/TagAsyncClient.java +++ /dev/null @@ -1,97 +0,0 @@ -/** - * Licensed to jclouds, Inc. (jclouds) under one or more - * contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. jclouds licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.jclouds.aws.ec2.services; - -import static org.jclouds.aws.reference.FormParameters.ACTION; - -import java.util.Map; -import java.util.Set; - -import javax.inject.Named; -import javax.ws.rs.POST; -import javax.ws.rs.Path; - -import org.jclouds.Fallbacks.EmptySetOnNotFoundOr404; -import org.jclouds.Fallbacks.VoidOnNotFoundOr404; -import org.jclouds.aws.ec2.binders.BindTagFiltersToIndexedFormParams; -import org.jclouds.aws.ec2.binders.BindTagsToIndexedFormParams; -import org.jclouds.aws.ec2.domain.Tag; -import org.jclouds.aws.ec2.util.TagFilters.FilterName; -import org.jclouds.aws.ec2.xml.DescribeTagsResponseHandler; -import org.jclouds.aws.filters.FormSigner; -import org.jclouds.ec2.binders.BindResourceIdsToIndexedFormParams; -import org.jclouds.javax.annotation.Nullable; -import org.jclouds.location.functions.RegionToEndpointOrProviderIfNull; -import org.jclouds.rest.annotations.BinderParam; -import org.jclouds.rest.annotations.EndpointParam; -import org.jclouds.rest.annotations.Fallback; -import org.jclouds.rest.annotations.FormParams; -import org.jclouds.rest.annotations.RequestFilters; -import org.jclouds.rest.annotations.VirtualHost; -import org.jclouds.rest.annotations.XMLResponseParser; - -import com.google.common.util.concurrent.ListenableFuture; - -/** - * Provides access to EC2 Tags via their REST API. - * - *

Important

- * This will be removed in jclouds version 1.6 - * - * @author grkvlt@apache.org - */ -@Deprecated -@RequestFilters(FormSigner.class) -@VirtualHost -public interface TagAsyncClient { - /** - * @see TagClient#createTagsInRegion(String, Iterable, Map) - */ - @Named("ec2:CreateTags") - @POST - @Path("/") - @FormParams(keys = ACTION, values = "CreateTags") - ListenableFuture createTagsInRegion(@EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region, - @BinderParam(BindResourceIdsToIndexedFormParams.class) Iterable resourceIds, - @BinderParam(BindTagsToIndexedFormParams.class) Map tags); - - /** - * @see TagClient#deleteTagsInRegion(String, Iterable, Map) - */ - @Named("ec2:DeleteTags") - @POST - @Path("/") - @FormParams(keys = ACTION, values = "DeleteTags") - @Fallback(VoidOnNotFoundOr404.class) - ListenableFuture deleteTagsInRegion(@EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region, - @BinderParam(BindResourceIdsToIndexedFormParams.class) Iterable resourceIds, - @BinderParam(BindTagsToIndexedFormParams.class) Map tags); - - /** - * @see TagClient#describeTagsInRegion(String, Map) - */ - @Named("ec2:DescribeTags") - @POST - @Path("/") - @FormParams(keys = ACTION, values = "DescribeTags") - @XMLResponseParser(DescribeTagsResponseHandler.class) - @Fallback(EmptySetOnNotFoundOr404.class) - ListenableFuture> describeTagsInRegion(@EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region, - @BinderParam(BindTagFiltersToIndexedFormParams.class) Map> filters); -} diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/TagClient.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/TagClient.java deleted file mode 100644 index 83dc99047c..0000000000 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/TagClient.java +++ /dev/null @@ -1,86 +0,0 @@ -/** - * Licensed to jclouds, Inc. (jclouds) under one or more - * contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. jclouds licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.jclouds.aws.ec2.services; - -import java.util.Map; -import java.util.Set; -import org.jclouds.aws.ec2.domain.Tag; -import org.jclouds.aws.ec2.util.TagFilters.FilterName; -import org.jclouds.javax.annotation.Nullable; - -/** - * - * Provides Tag services for EC2. For more information, refer to the Amazon EC2 - * Developer Guide. - * - *

Important

- * This will be removed in jclouds version 1.6 - * - * @author grkvlt@apache.org - * @see TagApi - */ -@Deprecated -public interface TagClient { - /** - * Creates tags. - * - * @param region - * Region to create the tag in. - * @param resourceIds - * IDs of the resources to tag. - * @param tags - * The tags to create. - * @see #describeTagsInRegion(String, Map) - * @see #deleteTagsInRegion(String, Iterable, Map) - * - * @see
- */ - void createTagsInRegion(@Nullable String region, Iterable resourceIds, Map tags); - - /** - * Deletes tags. - * - * @param region - * Region to delete the tags from - * @param resourceIds - * IDs of the tagged resources. - * @param tags - * The tags to delete. - * - * @see #describeTagsInRegion(String, Map) - * @see #createTagsInRegion(String, Iterable, Map) - * - * @see - */ - void deleteTagsInRegion(@Nullable String region, Iterable resourceIds, Map tags); - - /** - * Returns filtered information about tags. - * - * @param region - * The bundleTask ID is tied to the Region. - * @param filters - * A collection of filters to apply before selecting the tags. - * - * @see #deleteTagsInRegion(String, Iterable, Map) - * @see #createTagsInRegion(String, Iterable, Map) - * @see - */ - Set describeTagsInRegion(@Nullable String region, Map> filters); -} diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/xml/AWSDescribeInstancesResponseHandler.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/xml/AWSDescribeInstancesResponseHandler.java index 86365a9678..873955e435 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/xml/AWSDescribeInstancesResponseHandler.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/xml/AWSDescribeInstancesResponseHandler.java @@ -18,24 +18,23 @@ */ package org.jclouds.aws.ec2.xml; -import static org.jclouds.util.SaxUtils.currentOrNull; import static org.jclouds.util.SaxUtils.equalsOrSuffix; import java.util.Set; import javax.inject.Inject; -import org.jclouds.aws.ec2.domain.AWSRunningInstance; import org.jclouds.date.DateCodecFactory; import org.jclouds.ec2.domain.Reservation; import org.jclouds.ec2.domain.RunningInstance; +import org.jclouds.ec2.xml.TagSetHandler; import org.jclouds.location.Region; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import com.google.common.base.Supplier; -import com.google.common.collect.Sets; -import com.google.inject.Provider; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableSet.Builder; /** * Parses the following XML document: @@ -47,42 +46,51 @@ import com.google.inject.Provider; */ public class AWSDescribeInstancesResponseHandler extends BaseAWSReservationHandler>> { - private Set> reservations = Sets.newLinkedHashSet(); + private final TagSetHandler tagSetHandler; + private Builder> reservations = ImmutableSet.>builder(); private boolean inTagSet; - private String key; - private String value; @Inject AWSDescribeInstancesResponseHandler(DateCodecFactory dateCodecFactory, @Region Supplier defaultRegion, - Provider builderProvider, TagSetHandler tagSetHandler) { - super(dateCodecFactory, defaultRegion, builderProvider); + TagSetHandler tagSetHandler) { + super(dateCodecFactory, defaultRegion); + this.tagSetHandler = tagSetHandler; } @Override - public void startElement(String uri, String localName, String qName, Attributes attrs) throws SAXException { - super.startElement(uri, localName, qName, attrs); + public void startElement(String uri, String name, String qName, Attributes attrs) throws SAXException { + super.startElement(uri, name, qName, attrs); if (equalsOrSuffix(qName, "tagSet")) { inTagSet = true; } + if (inTagSet) { + tagSetHandler.startElement(uri, name, qName, attrs); + } + } + + @Override + public void characters(char ch[], int start, int length) { + if (inTagSet) { + tagSetHandler.characters(ch, start, length); + } else { + super.characters(ch, start, length); + } } @Override public void endElement(String uri, String name, String qName) { if (equalsOrSuffix(qName, "tagSet")) { inTagSet = false; + builder.tags(tagSetHandler.getResult()); } else if (inTagSet) { - if (equalsOrSuffix(qName, "key")) { - key = currentOrNull(currentText); - } else if (equalsOrSuffix(qName, "value")) { - value = currentOrNull(currentText); - } + tagSetHandler.endElement(uri, name, qName); } super.endElement(uri, name, qName); } @Override public Set> getResult() { - return reservations; + return reservations.build(); } protected boolean endOfReservationItem() { @@ -93,10 +101,6 @@ public class AWSDescribeInstancesResponseHandler extends protected void inItem() { if (endOfReservationItem()) { reservations.add(super.newReservation()); - } else if (inTagSet) { - builder.tag(key, value); - key = null; - value = null; } else { super.inItem(); } diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/xml/AWSRunInstancesResponseHandler.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/xml/AWSRunInstancesResponseHandler.java index 7a1d807b0f..aa7db10da9 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/xml/AWSRunInstancesResponseHandler.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/xml/AWSRunInstancesResponseHandler.java @@ -20,14 +20,12 @@ package org.jclouds.aws.ec2.xml; import javax.inject.Inject; -import org.jclouds.aws.ec2.domain.AWSRunningInstance; import org.jclouds.date.DateCodecFactory; import org.jclouds.ec2.domain.Reservation; import org.jclouds.ec2.domain.RunningInstance; import org.jclouds.location.Region; import com.google.common.base.Supplier; -import com.google.inject.Provider; /** * Parses the following XML document: @@ -40,9 +38,8 @@ import com.google.inject.Provider; public class AWSRunInstancesResponseHandler extends BaseAWSReservationHandler> { @Inject - AWSRunInstancesResponseHandler(DateCodecFactory dateCodecFactory, @Region Supplier defaultRegion, - Provider builderProvider) { - super(dateCodecFactory, defaultRegion, builderProvider); + AWSRunInstancesResponseHandler(DateCodecFactory dateCodecFactory, @Region Supplier defaultRegion) { + super(dateCodecFactory, defaultRegion); } @Override 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 19385ebcfb..620c649038 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 @@ -49,7 +49,6 @@ import org.xml.sax.SAXException; import com.google.common.base.Supplier; import com.google.common.collect.Maps; import com.google.common.collect.Sets; -import com.google.inject.Provider; /** * @@ -62,21 +61,21 @@ public abstract class BaseAWSReservationHandler extends HandlerForGeneratedRe protected final DateCodec dateCodec; protected final Supplier defaultRegion; - protected final Provider builderProvider; @Inject - public BaseAWSReservationHandler(DateCodecFactory dateCodecFactory, @Region Supplier defaultRegion, - Provider builderProvider) { + public BaseAWSReservationHandler(DateCodecFactory dateCodecFactory, @Region Supplier defaultRegion) { this.dateCodec = dateCodecFactory.iso8601(); this.defaultRegion = defaultRegion; - this.builderProvider = builderProvider; - this.builder = builderProvider.get(); + } + + protected AWSRunningInstance.Builder builder = newBuilder(); + + protected AWSRunningInstance.Builder newBuilder() { + return AWSRunningInstance.builder(); } protected StringBuilder currentText = new StringBuilder(); - - protected AWSRunningInstance.Builder builder; - + protected int itemDepth; boolean inInstancesSet; // attachments @@ -215,7 +214,7 @@ public abstract class BaseAWSReservationHandler extends HandlerForGeneratedRe if (endOfInstanceItem()) { refineBuilderBeforeAddingInstance(); instances.add(builder.build()); - builder = builderProvider.get(); + builder = newBuilder(); } } diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/xml/DescribeTagsResponseHandler.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/xml/DescribeTagsResponseHandler.java deleted file mode 100644 index daa08d1c18..0000000000 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/xml/DescribeTagsResponseHandler.java +++ /dev/null @@ -1,73 +0,0 @@ -/** - * Licensed to jclouds, Inc. (jclouds) under one or more - * contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. jclouds licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.jclouds.aws.ec2.xml; - -import java.util.Set; - -import javax.inject.Inject; - -import org.jclouds.aws.ec2.domain.Tag; -import org.jclouds.http.HttpRequest; -import org.jclouds.http.functions.ParseSax; -import org.jclouds.http.functions.ParseSax.HandlerWithResult; -import org.xml.sax.Attributes; -import org.xml.sax.SAXException; - -import com.google.common.collect.Sets; - -/** - * @author grkvlt@apache.org - */ -public class DescribeTagsResponseHandler extends ParseSax.HandlerWithResult> { - private Set tags = Sets.newLinkedHashSet(); - private final TagHandler tagHandler; - - @Inject - public DescribeTagsResponseHandler(TagHandler tagHandler) { - this.tagHandler = tagHandler; - } - - public Set getResult() { - return tags; - } - - @Override - public HandlerWithResult> setContext(HttpRequest request) { - tagHandler.setContext(request); - return super.setContext(request); - } - - @Override - public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { - if (!qName.equals("item")) - tagHandler.startElement(uri, localName, qName, attributes); - } - - @Override - public void endElement(String uri, String localName, String qName) throws SAXException { - if (qName.equals("item")) { - tags.add(tagHandler.getResult()); - } - tagHandler.endElement(uri, localName, qName); - } - - public void characters(char ch[], int start, int length) { - tagHandler.characters(ch, start, length); - } -} diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/xml/SpotInstanceHandler.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/xml/SpotInstanceHandler.java index a1c41f5802..4f608c525a 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/xml/SpotInstanceHandler.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/xml/SpotInstanceHandler.java @@ -28,6 +28,7 @@ import org.jclouds.aws.ec2.domain.SpotInstanceRequest.Builder; import org.jclouds.aws.util.AWSUtils; import org.jclouds.date.DateCodec; import org.jclouds.date.DateCodecFactory; +import org.jclouds.ec2.xml.TagSetHandler; import org.jclouds.http.functions.ParseSax; import org.jclouds.location.Region; import org.xml.sax.Attributes; @@ -92,7 +93,7 @@ public class SpotInstanceHandler extends ParseSax.HandlerForGeneratedRequestWith inTagSet = false; builder.tags(tagSetHandler.getResult()); } else if (inTagSet) { - tagSetHandler.endElement(uri, name, qName); + tagSetHandler.endElement(uri, name, qName); } if (qName.equals("launchSpecification")) { @@ -143,9 +144,9 @@ public class SpotInstanceHandler extends ParseSax.HandlerForGeneratedRequestWith @Override public void characters(char ch[], int start, int length) { if (inLaunchSpecification) { - launchSpecificationHandler.characters(ch, start, length); + launchSpecificationHandler.characters(ch, start, length); } else if (inTagSet) { - tagSetHandler.characters(ch, start, length); + tagSetHandler.characters(ch, start, length); } else { currentText.append(ch, start, length); } diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/xml/TagHandler.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/xml/TagHandler.java deleted file mode 100644 index a87f17efcd..0000000000 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/xml/TagHandler.java +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Licensed to jclouds, Inc. (jclouds) under one or more - * contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. jclouds licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.jclouds.aws.ec2.xml; - -import org.jclouds.aws.ec2.domain.Tag; -import org.jclouds.aws.ec2.util.TagFilters.ResourceType; -import org.jclouds.http.functions.ParseSax; -import org.xml.sax.SAXException; - -/** - * @author grkvlt@apache.org - */ -public class TagHandler extends ParseSax.HandlerForGeneratedRequestWithResult { - private StringBuilder currentText = new StringBuilder(); - - private String resourceId; - private ResourceType resourceType; - private String key; - private String value; - - public Tag getResult() { - Tag returnVal = new Tag(resourceId, resourceType, key, value); - return returnVal; - } - - public void endElement(String uri, String name, String qName) throws SAXException { - if (qName.equals("resourceId")) { - this.resourceId = currentText.toString().trim(); - } else if (qName.equals("resourceType")) { - resourceType = ResourceType.fromValue(currentText.toString().trim()); - } else if (qName.equals("key")) { - key = currentText.toString().trim(); - } else if (qName.equals("value")) { - value = currentText.toString().trim(); - } - currentText = new StringBuilder(); - } - - public void characters(char ch[], int start, int length) { - currentText.append(ch, start, length); - } -} diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/xml/TagSetHandler.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/xml/TagSetHandler.java deleted file mode 100644 index af611683b3..0000000000 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/xml/TagSetHandler.java +++ /dev/null @@ -1,85 +0,0 @@ -/** - * Licensed to jclouds, Inc. (jclouds) under one or more - * contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. jclouds licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.jclouds.aws.ec2.xml; - -import static org.jclouds.util.SaxUtils.currentOrNull; -import static org.jclouds.util.SaxUtils.equalsOrSuffix; - -import java.util.Map; - -import org.jclouds.http.functions.ParseSax; -import org.xml.sax.Attributes; -import org.xml.sax.SAXException; - -import com.google.common.base.Strings; -import com.google.common.collect.ImmutableMap; - -/** - * @author grkvlt@apache.org - */ -public class TagSetHandler extends ParseSax.HandlerForGeneratedRequestWithResult> { - private StringBuilder currentText = new StringBuilder(); - - private ImmutableMap.Builder result; - private boolean inItem = false; - private String key; - private String value; - - public TagSetHandler() { - super(); - } - - public Map getResult() { - return result.build(); - } - - @Override - public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { - if (equalsOrSuffix(qName, "tagSet")) { - result = ImmutableMap.builder(); - } else if (qName.equals("item")) { - inItem = true; - key = null; - value = null; - } - currentText = new StringBuilder(); - } - - @Override - public void endElement(String uri, String localName, String qName) throws SAXException { - if (qName.equals("item")) { - inItem = false; - if (key != null) { - result.put(key, Strings.nullToEmpty(value)); - } - } - if (inItem) { - if (qName.equals("key")) { - key = currentOrNull(currentText); - } else if (qName.equals("value")) { - value = currentOrNull(currentText); - } - } - } - - @Override - public void characters(char ch[], int start, int length) { - currentText.append(ch, start, length); - } -} diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/AWSEC2AsyncClientTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/AWSEC2AsyncClientTest.java index fc6e94fab4..8aefc51b18 100644 --- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/AWSEC2AsyncClientTest.java +++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/AWSEC2AsyncClientTest.java @@ -49,7 +49,6 @@ public class AWSEC2AsyncClientTest extends BaseAWSEC2AsyncClientTest userMetadata) { - assert node.getUserMetadata().equals(userMetadata) : String.format("node userMetadata did not match %s %s", - userMetadata, node); - } - @Override @Test public void testExtendedOptionsAndLogin() throws Exception { @@ -103,12 +96,14 @@ public class AWSEC2ComputeServiceLiveTest extends EC2ComputeServiceLiveTest { Date before = new Date(); ImmutableMap userMetadata = ImmutableMap. of("test", group); - ImmutableSet tags = ImmutableSet. of(group); + + ImmutableSet tags = ImmutableSet.of(group); // note that if you change the location, you must also specify image parameters Template template = client.templateBuilder().locationId(region).osFamily(AMZN_LINUX).os64Bit(true).build(); template.getOptions().tags(tags); template.getOptions().userMetadata(userMetadata); + template.getOptions().tags(tags); template.getOptions().as(AWSEC2TemplateOptions.class).enableMonitoring(); template.getOptions().as(AWSEC2TemplateOptions.class).spotPrice(0.3f); @@ -137,9 +132,6 @@ public class AWSEC2ComputeServiceLiveTest extends EC2ComputeServiceLiveTest { Set nodes = client.createNodesInGroup(group, 1, template); NodeMetadata first = getOnlyElement(nodes); - // Name metadata should turn into node.name - assertEquals(first.getName(), group); - checkUserMetadataContains(first, userMetadata); checkTagsInNodeEquals(first, tags); diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/IncidentalResourcesGetCleanedUpLiveTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/IncidentalResourcesGetCleanedUpLiveTest.java deleted file mode 100644 index 9504701275..0000000000 --- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/IncidentalResourcesGetCleanedUpLiveTest.java +++ /dev/null @@ -1,168 +0,0 @@ -/** - * Licensed to jclouds, Inc. (jclouds) under one or more - * contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. jclouds licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.jclouds.aws.ec2.compute; - -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotNull; - -import java.util.Set; - -import org.jclouds.aws.ec2.AWSEC2ApiMetadata; -import org.jclouds.aws.ec2.AWSEC2Client; -import org.jclouds.aws.ec2.domain.AWSRunningInstance; -import org.jclouds.aws.ec2.services.AWSSecurityGroupClient; -import org.jclouds.compute.domain.NodeMetadata; -import org.jclouds.compute.domain.Template; -import org.jclouds.compute.internal.BaseComputeServiceContextLiveTest; -import org.jclouds.compute.predicates.NodePredicates; -import org.jclouds.ec2.EC2Client; -import org.jclouds.ec2.compute.EC2ComputeServiceLiveTest; -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.testng.annotations.Test; - -import com.google.common.base.Function; -import com.google.common.base.Stopwatch; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Iterables; - -/** - * - * Disabled, as it doesn't pass - * - * @author Aled Sage - */ -@Test(enabled = false, groups = "live", singleThreaded = true, testName = "IncidentalResourcesGetCleanedUpLiveTest") -public class IncidentalResourcesGetCleanedUpLiveTest extends BaseComputeServiceContextLiveTest { - - public IncidentalResourcesGetCleanedUpLiveTest() { - provider = "aws-ec2"; - } - - @Test(enabled = false) - public void testIncidentalResourcesGetCleanedUpOnlyOnLastInstanceDestroyNode() throws Exception { - Function destroyer = new Function() { - @Override - public Void apply(String instanceId) { - view.getComputeService().destroyNode(instanceId); - return null; - } - }; - runIncidentalResourcesGetCleanedUpOnlyOnLastInstanceDestroy(destroyer); - } - - @Test(enabled = false) - public void testIncidentalResourcesGetCleanedUpOnlyOnLastInstanceDestroyNodesMatching() throws Exception { - Function destroyer = new Function() { - @Override - public Void apply(String instanceId) { - view.getComputeService().destroyNodesMatching(NodePredicates.withIds(instanceId)); - return null; - } - }; - runIncidentalResourcesGetCleanedUpOnlyOnLastInstanceDestroy(destroyer); - } - - private void runIncidentalResourcesGetCleanedUpOnlyOnLastInstanceDestroy(Function destroyer) throws Exception { - AWSSecurityGroupClient securityGroupClient = AWSEC2Client.class.cast(view.unwrap(AWSEC2ApiMetadata.CONTEXT_TOKEN).getApi()) - .getSecurityGroupServices(); - - KeyPairClient keyPairClient = EC2Client.class.cast(view.unwrap(AWSEC2ApiMetadata.CONTEXT_TOKEN).getApi()) - .getKeyPairServices(); - - InstanceClient instanceClient = EC2Client.class.cast(view.unwrap(AWSEC2ApiMetadata.CONTEXT_TOKEN).getApi()) - .getInstanceServices(); - - String group = "aws-ec2-incidental"; - String region = null; - - try { - // Create two instances - // TODO set spotPrice(0.3f) ? - Template template = view.getComputeService().templateBuilder().build(); - Set nodes = view.getComputeService().createNodesInGroup(group, 2, template); - NodeMetadata first = Iterables.get(nodes, 0); - NodeMetadata second = Iterables.get(nodes, 1); - - String instanceId1 = Iterables.get(nodes, 0).getProviderId(); - String instanceId2 = Iterables.get(nodes, 1).getProviderId(); - - AWSRunningInstance instance1 = AWSRunningInstance.class.cast(EC2ComputeServiceLiveTest.getInstance(instanceClient, instanceId1)); - AWSRunningInstance instance2 = AWSRunningInstance.class.cast(EC2ComputeServiceLiveTest.getInstance(instanceClient, instanceId2)); - - // Assert the two instances are in the same groups - region = instance1.getRegion(); - String expectedSecurityGroupName = "jclouds#" + group; - - assertEquals(instance1.getRegion(), region); - assertNotNull(instance1.getKeyName()); - assertEquals(instance1.getRegion(), instance2.getRegion(), "Nodes are not in the same region"); - assertEquals(instance1.getKeyName(), instance2.getKeyName(), "Nodes do not have same key-pair name"); - assertEquals(instance1.getGroupNames(), instance2.getGroupNames(), "Nodes are not in the same group"); - assertEquals(instance1.getGroupNames(), ImmutableSet.of(expectedSecurityGroupName), "Nodes are not in the expected security group"); - - // Assert a single key-pair and security group has been created - String expectedKeyPairName = instance1.getKeyName(); - Set securityGroups = securityGroupClient.describeSecurityGroupsInRegion(region, expectedSecurityGroupName); - Set keyPairs = keyPairClient.describeKeyPairsInRegion(region, expectedKeyPairName); - assertEquals(securityGroups.size(), 1); - assertEquals(Iterables.get(securityGroups, 0).getName(), expectedSecurityGroupName); - assertEquals(keyPairs.size(), 1); - assertEquals(Iterables.get(keyPairs, 0).getKeyName(), expectedKeyPairName); - - // Destroy the first node; the key-pair and security-group should still remain - destroyer.apply(first.getId()); - - Set securityGroupsAfterDestroyFirst = securityGroupClient.describeSecurityGroupsInRegion(region, expectedSecurityGroupName); - Set keyPairsAfterDestroyFirst = keyPairClient.describeKeyPairsInRegion(region, expectedKeyPairName); - assertEquals(securityGroupsAfterDestroyFirst, securityGroups); - assertEquals(keyPairsAfterDestroyFirst, keyPairs); - - // Destroy the second node; the key-pair and security-group should be automatically deleted - // It can take some time after destroyNode returns for the securityGroup and keyPair to be completely removed. - // Therefore try repeatedly. - destroyer.apply(second.getId()); - - final int TIMEOUT_MS = 30*1000; - boolean firstAttempt = true; - boolean done; - Set securityGroupsAfterDestroyAll; - Set keyPairsAfterDestroyAll; - Stopwatch stopwatch = new Stopwatch(); - stopwatch.start(); - do { - if (!firstAttempt) Thread.sleep(1000); - firstAttempt = false; - securityGroupsAfterDestroyAll = securityGroupClient.describeSecurityGroupsInRegion(region, expectedSecurityGroupName); - keyPairsAfterDestroyAll = keyPairClient.describeKeyPairsInRegion(region, expectedKeyPairName); - done = securityGroupsAfterDestroyAll.isEmpty() && keyPairsAfterDestroyAll.isEmpty(); - } while (!done && stopwatch.elapsedMillis() < TIMEOUT_MS); - - assertEquals(securityGroupsAfterDestroyAll, ImmutableSet.of()); - assertEquals(keyPairsAfterDestroyAll, ImmutableSet.of()); - - } finally { - view.getComputeService().destroyNodesMatching(NodePredicates.inGroup(group)); - - if (region != null) EC2ComputeServiceLiveTest.cleanupExtendedStuffInRegion(region, securityGroupClient, keyPairClient, group); - } - } -} diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/functions/PresentSpotRequestsAndInstancesTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/functions/PresentSpotRequestsAndInstancesTest.java new file mode 100644 index 0000000000..f954d93e00 --- /dev/null +++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/functions/PresentSpotRequestsAndInstancesTest.java @@ -0,0 +1,101 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.jclouds.aws.ec2.compute.functions; + +import static org.easymock.EasyMock.createMock; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.replay; +import static org.easymock.EasyMock.verify; +import static org.testng.Assert.assertEquals; + +import java.util.Set; + +import org.jclouds.aws.ec2.AWSEC2Client; +import org.jclouds.aws.ec2.domain.AWSRunningInstance; +import org.jclouds.aws.ec2.domain.SpotInstanceRequest; +import org.jclouds.aws.ec2.services.AWSInstanceClient; +import org.jclouds.aws.ec2.services.SpotInstanceClient; +import org.jclouds.ec2.compute.domain.RegionAndName; +import org.jclouds.ec2.domain.Reservation; +import org.testng.annotations.Test; + +import com.google.common.base.Function; +import com.google.common.base.Functions; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; + +/** + * @author Adrian Cole + */ +@Test(groups = "unit") +public class PresentSpotRequestsAndInstancesTest { + AWSRunningInstance instance1 = createMock(AWSRunningInstance.class); + AWSRunningInstance instance2 = createMock(AWSRunningInstance.class); + + @SuppressWarnings("unchecked") + @Test + public void testWhenInstancesPresentSingleCall() { + + AWSEC2Client client = createMock(AWSEC2Client.class); + AWSInstanceClient instanceClient = createMock(AWSInstanceClient.class); + Function converter = createMock(Function.class); + + expect(client.getInstanceServices()).andReturn(instanceClient); + + // avoid imatcher fail. if you change this, be sure to check multiple jres + expect(instanceClient.describeInstancesInRegion("us-east-1", "i-aaaa", "i-bbbb")).andReturn( + Set.class.cast(ImmutableSet.of(Reservation. builder().region("us-east-1") + .instances(ImmutableSet.of(instance1, instance2)).build()))); + + replay(client, instanceClient, converter); + + PresentSpotRequestsAndInstances fn = new PresentSpotRequestsAndInstances(client, converter); + + assertEquals(fn.apply(ImmutableSet.of(new RegionAndName("us-east-1", "i-aaaa"), new RegionAndName("us-east-1", + "i-bbbb"))), ImmutableSet.of(instance1, instance2)); + + verify(client, instanceClient, converter); + } + + SpotInstanceRequest spot1 = createMock(SpotInstanceRequest.class); + SpotInstanceRequest spot2 = createMock(SpotInstanceRequest.class); + + @Test + public void testWhenSpotsPresentSingleCall() { + + Function converter = Functions.forMap(ImmutableMap.of(spot1, instance1, + spot2, instance2)); + + AWSEC2Client client = createMock(AWSEC2Client.class); + SpotInstanceClient spotClient = createMock(SpotInstanceClient.class); + + expect(client.getSpotInstanceServices()).andReturn(spotClient); + expect(spotClient.describeSpotInstanceRequestsInRegion("us-east-1", "sir-aaaa", "sir-bbbb")).andReturn( + ImmutableSet.of(spot1, spot2)); + + replay(client, spotClient); + + PresentSpotRequestsAndInstances fn = new PresentSpotRequestsAndInstances(client, converter); + + assertEquals(fn.apply(ImmutableSet.of(new RegionAndName("us-east-1", "sir-aaaa"), new RegionAndName("us-east-1", + "sir-bbbb"))), ImmutableSet.of(instance1, instance2)); + + verify(client, spotClient); + } +} diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/TagAsyncClientTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/TagAsyncClientTest.java deleted file mode 100644 index f0d70a788e..0000000000 --- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/TagAsyncClientTest.java +++ /dev/null @@ -1,108 +0,0 @@ -/** - * Licensed to jclouds, Inc. (jclouds) under one or more - * contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. jclouds licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.jclouds.aws.ec2.services; - -import java.io.IOException; -import java.lang.reflect.Method; -import java.util.Map; - -import org.jclouds.Fallbacks.EmptySetOnNotFoundOr404; -import org.jclouds.Fallbacks.VoidOnNotFoundOr404; -import org.jclouds.aws.ec2.util.TagFilters; -import org.jclouds.aws.ec2.xml.DescribeTagsResponseHandler; -import org.jclouds.http.HttpRequest; -import org.jclouds.http.functions.ParseSax; -import org.jclouds.http.functions.ReleasePayloadAndReturn; -import org.testng.annotations.Test; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; - -/** - * Tests behavior of {@code TagsAsyncClient} - * - * @author grkvlt@apache.org - */ -// NOTE:without testName, this will not call @Before* and fail w/NPE during surefire -@Test(groups = "unit", testName = "TagsAsyncClientTest") -public class TagAsyncClientTest extends BaseAWSEC2AsyncClientTest { - public void testDeleteTags() throws SecurityException, NoSuchMethodException, IOException { - Method method = TagAsyncClient.class.getMethod("deleteTagsInRegion", String.class, Iterable.class, Map.class); - HttpRequest request = processor.createRequest(method, null, ImmutableList.builder().add("xxx").build(), ImmutableMap.builder().put("yyy", "zzz").build()); - - assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1"); - assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n"); - assertPayloadEquals(request, "Action=DeleteTags&Tag.1.Key=yyy&Tag.1.Value=zzz&ResourceId.1=xxx", - "application/x-www-form-urlencoded", false); - - assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class); - assertSaxResponseParserClassEquals(method, null); - assertFallbackClassEquals(method, VoidOnNotFoundOr404.class); - - checkFilters(request); - } - - public void testCreateTags() throws SecurityException, NoSuchMethodException, IOException { - Method method = TagAsyncClient.class.getMethod("createTagsInRegion", String.class, Iterable.class, Map.class); - HttpRequest request = processor.createRequest(method, null, ImmutableList.builder().add("xxx").build(), ImmutableMap.builder().put("yyy", "zzz").build()); - - assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1"); - assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n"); - assertPayloadEquals(request, "Action=CreateTags&Tag.1.Key=yyy&Tag.1.Value=zzz&ResourceId.1=xxx", - "application/x-www-form-urlencoded", false); - - assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class); - assertSaxResponseParserClassEquals(method, null); - assertFallbackClassEquals(method, null); - - checkFilters(request); - } - - public void testDescribeTags() throws SecurityException, NoSuchMethodException, IOException { - Method method = TagAsyncClient.class.getMethod("describeTagsInRegion", String.class, Map.class); - HttpRequest request = processor.createRequest(method, null, TagFilters.filters().build()); - - assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1"); - assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n"); - assertPayloadEquals(request, "Action=DescribeTags", - "application/x-www-form-urlencoded", false); - - assertResponseParserClassEquals(method, request, ParseSax.class); - assertSaxResponseParserClassEquals(method, DescribeTagsResponseHandler.class); - assertFallbackClassEquals(method, EmptySetOnNotFoundOr404.class); - - checkFilters(request); - } - - public void testDescribeTagsArgs() throws SecurityException, NoSuchMethodException, IOException { - Method method = TagAsyncClient.class.getMethod("describeTagsInRegion", String.class, Map.class); - HttpRequest request = processor.createRequest(method, null, TagFilters.filters().key("one").key("two").build()); - - assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1"); - assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n"); - assertPayloadEquals(request, "Action=DescribeTags&Filter.1.Name=key&Filter.1.Value.1=one&Filter.1.Value.2=two", - "application/x-www-form-urlencoded", false); - - assertResponseParserClassEquals(method, request, ParseSax.class); - assertSaxResponseParserClassEquals(method, DescribeTagsResponseHandler.class); - assertFallbackClassEquals(method, EmptySetOnNotFoundOr404.class); - - checkFilters(request); - } -} diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/TagClientLiveTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/TagClientLiveTest.java deleted file mode 100644 index f6d3466159..0000000000 --- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/TagClientLiveTest.java +++ /dev/null @@ -1,118 +0,0 @@ -/** - * Licensed to jclouds, Inc. (jclouds) under one or more - * contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. jclouds licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.jclouds.aws.ec2.services; - -import static com.google.common.collect.Iterables.getOnlyElement; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotNull; - -import java.util.Set; - -import org.jclouds.aws.ec2.AWSEC2ApiMetadata; -import org.jclouds.aws.ec2.domain.AWSRunningInstance; -import org.jclouds.aws.ec2.domain.Tag; -import org.jclouds.aws.ec2.util.TagFilters; -import org.jclouds.aws.ec2.util.TagFilters.ResourceType; -import org.jclouds.compute.internal.BaseComputeServiceContextLiveTest; -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Iterables; - -/** - * Tests live behavior of {@code TagClient} - * - * @author grkvlt@apache.org - */ -@Test(groups = "live", singleThreaded = true) -public class TagClientLiveTest extends BaseComputeServiceContextLiveTest { - public TagClientLiveTest() { - provider = "aws-ec2"; - } - - private TagClient client; - protected String testGroup; - - @Override - @BeforeClass(groups = { "integration", "live" }) - public void setupContext() { - super.setupContext(); - client = view.unwrap(AWSEC2ApiMetadata.CONTEXT_TOKEN).getApi().getTagServices(); - - try { - testGroup = view.unwrap(AWSEC2ApiMetadata.CONTEXT_TOKEN).getApi().getSecurityGroupServices().createSecurityGroupInRegionAndReturnId(null, - "test-group", "test-group"); - } catch (IllegalStateException e) { - // already exists - testGroup = Iterables.get( - view.unwrap(AWSEC2ApiMetadata.CONTEXT_TOKEN).getApi().getSecurityGroupServices().describeSecurityGroupsInRegion(null, "test-group"), 0) - .getId(); - } - } - - @Override - @AfterClass(groups = { "integration", "live" }) - protected void tearDownContext() { - view.unwrap(AWSEC2ApiMetadata.CONTEXT_TOKEN).getApi().getSecurityGroupServices().deleteSecurityGroupInRegionById(null, testGroup); - super.tearDownContext(); - } - - public static final String PREFIX = System.getProperty("user.name") + "-ec2"; - - @Test - void test() { - cleanupTag(testGroup, "test-key"); - cleanupTag(testGroup, "empty-key"); - try { - client.createTagsInRegion(null, ImmutableList.builder().add(testGroup).build(), ImmutableMap.builder().put("test-key", "test-value").build()); - checkTag(testGroup, ResourceType.SECURITY_GROUP, "test-key", "test-value"); - client.createTagsInRegion(null, ImmutableList.builder().add(testGroup).build(), ImmutableMap.builder().put("empty-key", "").build()); - checkTag(testGroup, ResourceType.SECURITY_GROUP, "empty-key", ""); - } finally { - cleanupTag(testGroup, "test-key"); - cleanupTag(testGroup, "empty-key"); - } - } - - protected void cleanupTag(String resourceId, String key) { - try { - client.deleteTagsInRegion(null, ImmutableList.builder().add(resourceId).build(), ImmutableMap.builder().put(key, null).build()); - } catch (Exception e) { - // Ignore - } - } - - protected void checkTag(String resourceId, ResourceType resourceType, String key, String value) { - Set results = client.describeTagsInRegion(null, TagFilters.filters().resourceId(resourceId).resourceType(resourceType).keyValuePair(key, value).build()); - assertNotNull(results); - assertEquals(results.size(), 1); - Tag tag = Iterables.getOnlyElement(results); - assertEquals(tag.getResourceId(), resourceId); - assertEquals(tag.getResourceType(), resourceType); - assertEquals(tag.getKey(), key); - assertEquals(tag.getValue(), value); - } - - protected AWSRunningInstance getInstance(AWSInstanceClient instanceClient, String id) { - return getOnlyElement(getOnlyElement(instanceClient.describeInstancesInRegion(null, id))); - } -}