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 9a83767055..431aab0663 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 @@ -19,9 +19,14 @@ package org.jclouds.ec2.compute; +import static com.google.common.collect.Iterables.concat; +import static com.google.common.collect.Iterables.filter; +import static com.google.common.collect.Iterables.transform; +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.ExecutorService; import javax.inject.Inject; @@ -40,13 +45,13 @@ import org.jclouds.compute.domain.TemplateBuilder; import org.jclouds.compute.internal.BaseComputeService; import org.jclouds.compute.options.TemplateOptions; import org.jclouds.compute.reference.ComputeServiceConstants.Timeouts; +import org.jclouds.compute.strategy.CreateNodesInGroupThenAddToSet; import org.jclouds.compute.strategy.DestroyNodeStrategy; import org.jclouds.compute.strategy.GetNodeMetadataStrategy; import org.jclouds.compute.strategy.InitializeRunScriptOnNodeOrPlaceInBadMap; import org.jclouds.compute.strategy.ListNodesStrategy; import org.jclouds.compute.strategy.RebootNodeStrategy; import org.jclouds.compute.strategy.ResumeNodeStrategy; -import org.jclouds.compute.strategy.CreateNodesInGroupThenAddToSet; import org.jclouds.compute.strategy.SuspendNodeStrategy; import org.jclouds.domain.Credentials; import org.jclouds.domain.Location; @@ -55,12 +60,15 @@ import org.jclouds.ec2.compute.domain.RegionAndName; import org.jclouds.ec2.compute.domain.RegionNameAndIngressRules; import org.jclouds.ec2.compute.options.EC2TemplateOptions; import org.jclouds.ec2.domain.KeyPair; -import org.jclouds.util.Preconditions2; +import org.jclouds.ec2.domain.RunningInstance; import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Function; import com.google.common.base.Predicate; import com.google.common.base.Supplier; -import com.google.common.collect.Maps; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableMap.Builder; +import com.google.common.collect.ImmutableSet; /** * @author Adrian Cole @@ -73,31 +81,30 @@ public class EC2ComputeService extends BaseComputeService { @Inject protected EC2ComputeService(ComputeServiceContext context, Map credentialStore, - @Memoized Supplier> images, @Memoized Supplier> sizes, - @Memoized Supplier> locations, ListNodesStrategy listNodesStrategy, - GetNodeMetadataStrategy getNodeMetadataStrategy, CreateNodesInGroupThenAddToSet runNodesAndAddToSetStrategy, - RebootNodeStrategy rebootNodeStrategy, DestroyNodeStrategy destroyNodeStrategy, - ResumeNodeStrategy startNodeStrategy, SuspendNodeStrategy stopNodeStrategy, - Provider templateBuilderProvider, Provider templateOptionsProvider, - @Named("NODE_RUNNING") Predicate nodeRunning, - @Named("NODE_TERMINATED") Predicate nodeTerminated, - @Named("NODE_SUSPENDED") Predicate nodeSuspended, - InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory, Timeouts timeouts, - @Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor, EC2Client ec2Client, - Map credentialsMap, @Named("SECURITY") Map securityGroupMap) { + @Memoized Supplier> images, @Memoized Supplier> sizes, + @Memoized Supplier> locations, ListNodesStrategy listNodesStrategy, + GetNodeMetadataStrategy getNodeMetadataStrategy, CreateNodesInGroupThenAddToSet runNodesAndAddToSetStrategy, + RebootNodeStrategy rebootNodeStrategy, DestroyNodeStrategy destroyNodeStrategy, + ResumeNodeStrategy startNodeStrategy, SuspendNodeStrategy stopNodeStrategy, + Provider templateBuilderProvider, Provider templateOptionsProvider, + @Named("NODE_RUNNING") Predicate nodeRunning, + @Named("NODE_TERMINATED") Predicate nodeTerminated, + @Named("NODE_SUSPENDED") Predicate nodeSuspended, + InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory, Timeouts timeouts, + @Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor, EC2Client ec2Client, + Map credentialsMap, @Named("SECURITY") Map securityGroupMap) { super(context, credentialStore, images, sizes, locations, listNodesStrategy, getNodeMetadataStrategy, - runNodesAndAddToSetStrategy, rebootNodeStrategy, destroyNodeStrategy, startNodeStrategy, - stopNodeStrategy, templateBuilderProvider, templateOptionsProvider, nodeRunning, nodeTerminated, - nodeSuspended, initScriptRunnerFactory, timeouts, executor); + runNodesAndAddToSetStrategy, rebootNodeStrategy, destroyNodeStrategy, startNodeStrategy, stopNodeStrategy, + templateBuilderProvider, templateOptionsProvider, nodeRunning, nodeTerminated, nodeSuspended, + initScriptRunnerFactory, timeouts, executor); this.ec2Client = ec2Client; this.credentialsMap = credentialsMap; this.securityGroupMap = securityGroupMap; - } @VisibleForTesting void deleteSecurityGroup(String region, String group) { - Preconditions2.checkNotEmpty(group, "group"); + checkNotEmpty(group, "group"); String groupName = String.format("jclouds#%s#%s", group, region); if (ec2Client.getSecurityGroupServices().describeSecurityGroupsInRegion(region, groupName).size() > 0) { logger.debug(">> deleting securityGroup(%s)", groupName); @@ -115,16 +122,54 @@ public class EC2ComputeService extends BaseComputeService { @VisibleForTesting void deleteKeyPair(String region, String group) { for (KeyPair keyPair : ec2Client.getKeyPairServices().describeKeyPairsInRegion(region)) { - if (keyPair.getKeyName().matches(String.format("jclouds#%s#%s#%s", group, region, "[0-9a-f]+"))) { - logger.debug(">> deleting keyPair(%s)", keyPair.getKeyName()); - ec2Client.getKeyPairServices().deleteKeyPairInRegion(region, keyPair.getKeyName()); - // TODO: test this clear happens - credentialsMap.remove(new RegionAndName(region, keyPair.getKeyName())); - logger.debug("<< deleted keyPair(%s)", keyPair.getKeyName()); + if ( + // when the keypair is unique per group + keyPair.getKeyName().equals("jclouds#" + group) + || keyPair.getKeyName().matches(String.format("jclouds#%s#%s", group, "[0-9a-f]+")) + // old keypair pattern too verbose as it has an unnecessary region qualifier + || keyPair.getKeyName().matches(String.format("jclouds#%s#%s#%s", group, region, "[0-9a-f]+"))) { + Set instancesUsingKeyPair = extractIdsFromInstances(filter(concat(ec2Client.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()); + // TODO: test this clear happens + credentialsMap.remove(new RegionAndName(region, keyPair.getKeyName())); + logger.debug("<< deleted keyPair(%s)", keyPair.getKeyName()); + } } } } + protected ImmutableSet extractIdsFromInstances(Iterable deadOnes) { + return ImmutableSet.copyOf(transform(deadOnes, new Function() { + + @Override + public String apply(RunningInstance input) { + return input.getId(); + } + + })); + } + + protected Predicate usingKeyPairAndNotDead(final KeyPair keyPair) { + return new Predicate() { + + @Override + public boolean apply(RunningInstance input) { + switch (input.getInstanceState()) { + case TERMINATED: + case SHUTTING_DOWN: + return false; + } + return keyPair.getKeyName().equals(input.getKeyName()); + } + + }; + } + /** * like {@link BaseComputeService#destroyNodesMatching} except that this will clean implicit * keypairs and security groups. @@ -132,20 +177,20 @@ public class EC2ComputeService extends BaseComputeService { @Override public Set destroyNodesMatching(Predicate filter) { Set deadOnes = super.destroyNodesMatching(filter); - Map regionTags = Maps.newHashMap(); + Builder regionGroups = ImmutableMap. builder(); for (NodeMetadata nodeMetadata : deadOnes) { if (nodeMetadata.getGroup() != null) - regionTags.put(AWSUtils.parseHandle(nodeMetadata.getId())[0], nodeMetadata.getGroup()); + regionGroups.put(AWSUtils.parseHandle(nodeMetadata.getId())[0], nodeMetadata.getGroup()); } - for (Entry regionTag : regionTags.entrySet()) { - cleanUpIncidentalResources(regionTag); + for (Entry regionGroup : regionGroups.build().entrySet()) { + cleanUpIncidentalResources(regionGroup); } return deadOnes; } - protected void cleanUpIncidentalResources(Entry regionTag) { - deleteKeyPair(regionTag.getKey(), regionTag.getValue()); - deleteSecurityGroup(regionTag.getKey(), regionTag.getValue()); + protected void cleanUpIncidentalResources(Entry regionGroup) { + deleteKeyPair(regionGroup.getKey(), regionGroup.getValue()); + deleteSecurityGroup(regionGroup.getKey(), regionGroup.getValue()); } /** diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceDependenciesModule.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceDependenciesModule.java index 6d17a4dddc..afa6a5653f 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceDependenciesModule.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceDependenciesModule.java @@ -43,6 +43,7 @@ import org.jclouds.ec2.EC2AsyncClient; import org.jclouds.ec2.EC2Client; import org.jclouds.ec2.compute.EC2ComputeService; import org.jclouds.ec2.compute.domain.RegionAndName; +import org.jclouds.ec2.compute.domain.RegionNameAndIngressRules; import org.jclouds.ec2.compute.functions.CreateSecurityGroupIfNeeded; import org.jclouds.ec2.compute.functions.CreateUniqueKeyPair; import org.jclouds.ec2.compute.functions.CredentialsForInstance; @@ -76,11 +77,11 @@ import com.google.inject.TypeLiteral; public class EC2ComputeServiceDependenciesModule extends AbstractModule { public static final Map instanceToNodeState = ImmutableMap - . builder().put(InstanceState.PENDING, NodeState.PENDING).put( - InstanceState.RUNNING, NodeState.RUNNING).put(InstanceState.SHUTTING_DOWN, NodeState.PENDING).put( - InstanceState.TERMINATED, NodeState.TERMINATED).put(InstanceState.STOPPING, NodeState.PENDING) - .put(InstanceState.STOPPED, NodeState.SUSPENDED).put(InstanceState.UNRECOGNIZED, NodeState.UNRECOGNIZED) - .build(); + . builder().put(InstanceState.PENDING, NodeState.PENDING) + .put(InstanceState.RUNNING, NodeState.RUNNING).put(InstanceState.SHUTTING_DOWN, NodeState.PENDING) + .put(InstanceState.TERMINATED, NodeState.TERMINATED).put(InstanceState.STOPPING, NodeState.PENDING) + .put(InstanceState.STOPPED, NodeState.SUSPENDED).put(InstanceState.UNRECOGNIZED, NodeState.UNRECOGNIZED) + .build(); @Singleton @Provides @@ -104,6 +105,12 @@ public class EC2ComputeServiceDependenciesModule extends AbstractModule { }).to(RunningInstanceToNodeMetadata.class); bind(new TypeLiteral>() { }).to(CredentialsForInstance.class); + bind(new TypeLiteral>() { + }).to(CreateSecurityGroupIfNeeded.class); + bind(new TypeLiteral>() { + }).to(CreateUniqueKeyPair.class); + bind(new TypeLiteral>() { + }).to(RegionAndIdToImage.class); bind(new TypeLiteral() { }).to(new TypeLiteral>() { }).in(Scopes.SINGLETON); @@ -128,7 +135,7 @@ public class EC2ComputeServiceDependenciesModule extends AbstractModule { @Provides @Singleton - protected final Map credentialsMap(CreateUniqueKeyPair in) { + protected final Map credentialsMap(Function in) { // doesn't seem to clear when someone issues remove(key) // return new MapMaker().makeComputingMap(in); return newLinkedHashMap(); @@ -137,13 +144,12 @@ public class EC2ComputeServiceDependenciesModule extends AbstractModule { @Provides @Singleton @Named("SECURITY") - protected final Map securityGroupMap(CreateSecurityGroupIfNeeded in) { + protected final Map securityGroupMap(Function in) { // doesn't seem to clear when someone issues remove(key) // return new MapMaker().makeComputingMap(in); return newLinkedHashMap(); } - @Provides @Singleton @Named(PROPERTY_EC2_AMI_OWNERS) @@ -153,10 +159,9 @@ public class EC2ComputeServiceDependenciesModule extends AbstractModule { return toArray(Splitter.on(',').split(amiOwners), String.class); } - @Provides @Singleton - protected Map provideImageMap(RegionAndIdToImage regionAndIdToImage) { + protected Map provideImageMap(Function regionAndIdToImage) { return new MapMaker().makeComputingMap(regionAndIdToImage); } 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 05497bfdab..7d463a1f1e 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 @@ -73,7 +73,7 @@ public class RegionAndName { @Override public String toString() { - return "RegionTag [region=" + region + ", name=" + name + "]"; + return "[region=" + region + ", name=" + name + "]"; } } diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/CreateUniqueKeyPair.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/CreateUniqueKeyPair.java index 7c3b0ad11d..b7fa7a7b0d 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/CreateUniqueKeyPair.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/CreateUniqueKeyPair.java @@ -26,10 +26,10 @@ import javax.inject.Inject; import javax.inject.Named; import javax.inject.Singleton; +import org.jclouds.compute.reference.ComputeServiceConstants; import org.jclouds.ec2.EC2Client; import org.jclouds.ec2.compute.domain.RegionAndName; import org.jclouds.ec2.domain.KeyPair; -import org.jclouds.compute.reference.ComputeServiceConstants; import org.jclouds.logging.Logger; import com.google.common.annotations.VisibleForTesting; @@ -46,7 +46,7 @@ public class CreateUniqueKeyPair implements Function { @Named(ComputeServiceConstants.COMPUTE_LOGGER) protected Logger logger = Logger.NULL; protected final EC2Client ec2Client; - protected Supplier randomSuffix; + protected final Supplier randomSuffix; @Inject public CreateUniqueKeyPair(EC2Client ec2Client, Supplier randomSuffix) { @@ -60,14 +60,14 @@ public class CreateUniqueKeyPair implements Function { } @VisibleForTesting - KeyPair createNewKeyPairInRegion(String region, String tag) { + KeyPair createNewKeyPairInRegion(String region, String group) { checkNotNull(region, "region"); - checkNotNull(tag, "tag"); - logger.debug(">> creating keyPair region(%s) tag(%s)", region, tag); + checkNotNull(group, "group"); + logger.debug(">> creating keyPair region(%s) group(%s)", region, group); KeyPair keyPair = null; while (keyPair == null) { try { - keyPair = ec2Client.getKeyPairServices().createKeyPairInRegion(region, getNextName(region, tag)); + keyPair = ec2Client.getKeyPairServices().createKeyPairInRegion(region, getNextName(region, group)); logger.debug("<< created keyPair(%s)", keyPair.getKeyName()); } catch (IllegalStateException e) { @@ -76,7 +76,7 @@ public class CreateUniqueKeyPair implements Function { return keyPair; } - private String getNextName(String region, String tag) { - return String.format("jclouds#%s#%s#%s", tag, region, randomSuffix.get()); + private String getNextName(String region, String group) { + return String.format("jclouds#%s#%s#%s", group, region, randomSuffix.get()); } } diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/EC2ImageParser.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/EC2ImageParser.java index 793283405c..9c7506b343 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/EC2ImageParser.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/EC2ImageParser.java @@ -82,7 +82,6 @@ public class EC2ImageParser implements Function securityGroupMap; @VisibleForTesting - public final CreateUniqueKeyPair createUniqueKeyPair; + public final Function createUniqueKeyPair; @VisibleForTesting - public final CreateSecurityGroupIfNeeded createSecurityGroupIfNeeded; - private final javax.inject.Provider optionsProvider; + public final Function createSecurityGroupIfNeeded; + protected final Provider optionsProvider; @Inject public CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions(Map credentialsMap, - @Named("SECURITY") Map securityGroupMap, CreateUniqueKeyPair createUniqueKeyPair, - CreateSecurityGroupIfNeeded createSecurityGroupIfNeeded, - javax.inject.Provider optionsProvider) { + @Named("SECURITY") Map securityGroupMap, Function createUniqueKeyPair, + Function createSecurityGroupIfNeeded, + Provider optionsProvider) { this.credentialsMap = credentialsMap; this.securityGroupMap = securityGroupMap; this.createUniqueKeyPair = createUniqueKeyPair; @@ -72,13 +73,13 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions { this.optionsProvider = optionsProvider; } - public RunInstancesOptions execute(String region, String tag, Template template) { + public RunInstancesOptions execute(String region, String group, Template template) { RunInstancesOptions instanceOptions = getOptionsProvider().get().asType(template.getHardware().getId()); - String keyPairName = createNewKeyPairUnlessUserSpecifiedOtherwise(region, tag, template.getOptions()); + String keyPairName = createNewKeyPairUnlessUserSpecifiedOtherwise(region, group, template.getOptions()); - addSecurityGroups(region, tag, template, instanceOptions); + addSecurityGroups(region, group, template, instanceOptions); if (template.getOptions() instanceof EC2TemplateOptions) { if (keyPairName != null) @@ -90,72 +91,83 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions { instanceOptions.withUserData(userData); Set blockDeviceMappings = EC2TemplateOptions.class.cast(template.getOptions()) - .getBlockDeviceMappings(); + .getBlockDeviceMappings(); if (blockDeviceMappings.size() > 0) { checkState("ebs".equals(template.getImage().getUserMetadata().get("rootDeviceType")), - "BlockDeviceMapping only available on ebs boot"); + "BlockDeviceMapping only available on ebs boot"); instanceOptions.withBlockDeviceMappings(blockDeviceMappings); } } return instanceOptions; } - protected void addSecurityGroups(String region, String tag, Template template, RunInstancesOptions instanceOptions) { - Set groups = getSecurityGroupsForTagAndOptions(region, tag, template.getOptions()); + protected void addSecurityGroups(String region, String group, Template template, RunInstancesOptions instanceOptions) { + Set groups = getSecurityGroupsForTagAndOptions(region, group, template.getOptions()); instanceOptions.withSecurityGroups(groups); } @VisibleForTesting - public String createNewKeyPairUnlessUserSpecifiedOtherwise(String region, String tag, TemplateOptions options) { + public String createNewKeyPairUnlessUserSpecifiedOtherwise(String region, String group, TemplateOptions options) { String keyPairName = null; boolean shouldAutomaticallyCreateKeyPair = true; if (options instanceof EC2TemplateOptions) { keyPairName = EC2TemplateOptions.class.cast(options).getKeyPair(); if (keyPairName == null) shouldAutomaticallyCreateKeyPair = EC2TemplateOptions.class.cast(options) - .shouldAutomaticallyCreateKeyPair(); + .shouldAutomaticallyCreateKeyPair(); } if (keyPairName == null && shouldAutomaticallyCreateKeyPair) { - RegionAndName regionAndName = new RegionAndName(region, tag); - KeyPair keyPair = createUniqueKeyPair.apply(regionAndName); - // get or create incidental resources - // TODO race condition. we were using MapMaker, but it doesn't seem to - // refresh properly - // when - // another thread - // deletes a key - credentialsMap.put(new RegionAndName(region, keyPair.getKeyName()), keyPair); - keyPairName = keyPair.getKeyName(); + keyPairName = createOrImportKeyPair(region, group, options); } return keyPairName; } - @VisibleForTesting - public Set getSecurityGroupsForTagAndOptions(String region, @Nullable String tag, TemplateOptions options) { - Set groups = Sets.newLinkedHashSet(); + // base EC2 driver currently does not support key import + protected String createOrImportKeyPair(String region, String group, TemplateOptions options) { + return createUniqueKeyPairAndPutIntoMap(region, group); + } - if (tag != null) { - String markerGroup = String.format("jclouds#%s#%s", tag, region); + protected String createUniqueKeyPairAndPutIntoMap(String region, String group) { + String keyPairName; + RegionAndName regionAndName = new RegionAndName(region, group); + KeyPair keyPair = createUniqueKeyPair.apply(regionAndName); + keyPairName = keyPair.getKeyName(); + // get or create incidental resources + // TODO race condition. we were using MapMaker, but it doesn't seem to + // refresh properly + // when + // another thread + // deletes a key + credentialsMap.put(new RegionAndName(regionAndName.getRegion(), keyPairName), keyPair); + return keyPairName; + } + + @VisibleForTesting + public Set getSecurityGroupsForTagAndOptions(String region, @Nullable String group, TemplateOptions options) { + Builder groups = ImmutableSet. builder(); + + if (group != null) { + String markerGroup = String.format("jclouds#%s#%s", group, region); groups.add(markerGroup); RegionNameAndIngressRules regionNameAndIngessRulesForMarkerGroup; if (options instanceof EC2TemplateOptions && EC2TemplateOptions.class.cast(options).getGroupIds().size() > 0) { regionNameAndIngessRulesForMarkerGroup = new RegionNameAndIngressRules(region, markerGroup, new int[] {}, - false); + false); groups.addAll(EC2TemplateOptions.class.cast(options).getGroupIds()); } else { - regionNameAndIngessRulesForMarkerGroup = new RegionNameAndIngressRules(region, markerGroup, options - .getInboundPorts(), true); + regionNameAndIngessRulesForMarkerGroup = new RegionNameAndIngressRules(region, markerGroup, + options.getInboundPorts(), true); } if (!securityGroupMap.containsKey(regionNameAndIngessRulesForMarkerGroup)) { - securityGroupMap.put(regionNameAndIngessRulesForMarkerGroup, createSecurityGroupIfNeeded - .apply(regionNameAndIngessRulesForMarkerGroup)); + securityGroupMap.put(regionNameAndIngessRulesForMarkerGroup, + createSecurityGroupIfNeeded.apply(regionNameAndIngessRulesForMarkerGroup)); } } - return groups; + return groups.build(); } // allows us to mock this method 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 f81f840693..c4eaaddf2d 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 @@ -31,12 +31,14 @@ import java.util.concurrent.Future; 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.reference.ComputeServiceConstants; import org.jclouds.compute.strategy.CreateNodesInGroupThenAddToSet; import org.jclouds.compute.util.ComputeUtils; @@ -77,16 +79,19 @@ public class EC2CreateNodesInGroupThenAddToSet implements CreateNodesInGroupThen final Predicate instancePresent; final Function instanceToCredentials; final Map credentialStore; + final Provider templateBuilderProvider; @Inject EC2CreateNodesInGroupThenAddToSet( - EC2Client client, - CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions createKeyPairAndSecurityGroupsAsNeededAndReturncustomize, - @Named("PRESENT") Predicate instancePresent, - Function runningInstanceToNodeMetadata, - Function instanceToCredentials, Map credentialStore, - ComputeUtils utils) { + EC2Client client, + Provider templateBuilderProvider, + CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions createKeyPairAndSecurityGroupsAsNeededAndReturncustomize, + @Named("PRESENT") Predicate instancePresent, + Function runningInstanceToNodeMetadata, + Function instanceToCredentials, Map credentialStore, + ComputeUtils utils) { this.client = client; + this.templateBuilderProvider = templateBuilderProvider; this.instancePresent = instancePresent; this.createKeyPairAndSecurityGroupsAsNeededAndReturncustomize = createKeyPairAndSecurityGroupsAsNeededAndReturncustomize; this.runningInstanceToNodeMetadata = runningInstanceToNodeMetadata; @@ -97,10 +102,11 @@ public class EC2CreateNodesInGroupThenAddToSet implements CreateNodesInGroupThen @Override public Map> execute(String group, int count, Template template, Set goodNodes, - Map badNodes, Multimap customizationResponses) { - - Iterable started = createKeyPairAndSecurityGroupsAsNeededThenRunInstances(group, count, - template); + Map badNodes, Multimap customizationResponses) { + // ensure we don't mutate the input template + template = templateBuilderProvider.get().fromTemplate(template).build(); + Iterable started = createKeyPairAndSecurityGroupsAsNeededThenRunInstances(group, + count, template); Iterable ids = transform(started, instanceToId); String idsString = Joiner.on(',').join(ids); @@ -111,8 +117,8 @@ public class EC2CreateNodesInGroupThenAddToSet implements CreateNodesInGroupThen populateCredentials(started); } - return utils.customizeNodesAndAddToGoodMapOrPutExceptionIntoBadMap(template.getOptions(), transform(started, - runningInstanceToNodeMetadata), goodNodes, badNodes, customizationResponses); + return utils.customizeNodesAndAddToGoodMapOrPutExceptionIntoBadMap(template.getOptions(), + transform(started, runningInstanceToNodeMetadata), goodNodes, badNodes, customizationResponses); } protected void populateCredentials(Iterable started) { @@ -131,12 +137,12 @@ public class EC2CreateNodesInGroupThenAddToSet implements CreateNodesInGroupThen // TODO write test for this @VisibleForTesting Iterable createKeyPairAndSecurityGroupsAsNeededThenRunInstances(String group, int count, - Template template) { + Template template) { String region = AWSUtils.getRegionFromLocationOrNull(template.getLocation()); String zone = getZoneFromLocationOrNull(template.getLocation()); RunInstancesOptions instanceOptions = createKeyPairAndSecurityGroupsAsNeededAndReturncustomize.execute(region, - group, template); + group, template); int countStarted = 0; int tries = 0; @@ -145,10 +151,12 @@ public class EC2CreateNodesInGroupThenAddToSet implements CreateNodesInGroupThen 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 = Iterables.concat( + started, + client.getInstanceServices().runInstancesInRegion(region, zone, template.getImage().getProviderId(), 1, + count - countStarted, instanceOptions)); countStarted = Iterables.size(started); if (countStarted < count) diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/domain/KeyPair.java b/apis/ec2/src/main/java/org/jclouds/ec2/domain/KeyPair.java index 7da0ee3cdf..fe7269eb21 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/domain/KeyPair.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/domain/KeyPair.java @@ -31,6 +31,52 @@ import javax.annotation.Nullable; * @author Adrian Cole */ public class KeyPair implements Comparable { + @Override + public String toString() { + return "[region=" + region + ", keyName=" + keyName + ", keyFingerprint=" + keyFingerprint + ", keyMaterial?=" + + (keyMaterial != null) + "]"; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private String region; + private String keyName; + private String keyFingerprint; + private String keyMaterial; + + public Builder region(String region) { + this.region = region; + return this; + } + + public Builder keyName(String keyName) { + this.keyName = keyName; + return this; + } + + public Builder keyFingerprint(String keyFingerprint) { + this.keyFingerprint = keyFingerprint; + return this; + } + + public Builder keyMaterial(String keyMaterial) { + this.keyMaterial = keyMaterial; + return this; + } + + public KeyPair build() { + return new KeyPair(region, keyName, keyFingerprint, keyMaterial); + } + + public static Builder fromKeyPair(KeyPair in) { + return new Builder().region(in.getRegion()).keyName(in.getKeyName()).keyFingerprint(in.getKeyFingerprint()) + .keyMaterial(in.getKeyMaterial()); + } + } + private final String region; private final String keyName; private final String keyFingerprint; @@ -122,4 +168,7 @@ public class KeyPair implements Comparable { return true; } + public Builder toBuilder() { + return Builder.fromKeyPair(this); + } } 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 1c7f95a671..10edeb8853 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 @@ -19,7 +19,6 @@ package org.jclouds.ec2.compute; -import static org.jclouds.compute.util.ComputeServiceUtils.getCores; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertTrue; @@ -28,7 +27,6 @@ import java.util.Set; import org.jclouds.compute.BaseComputeServiceLiveTest; import org.jclouds.compute.domain.NodeMetadata; -import org.jclouds.compute.domain.OsFamily; import org.jclouds.compute.domain.Template; import org.jclouds.compute.options.TemplateOptions; import org.jclouds.compute.predicates.NodePredicates; @@ -83,19 +81,6 @@ public class EC2ComputeServiceLiveTest extends BaseComputeServiceLiveTest { assertEquals(byId.getImage(), defaultTemplate.getImage()); } - @Test(enabled = true, dependsOnMethods = "testImagesResolveCorrectly") - public void testDefaultTemplateBuilder() { - assertDefaultWorks(); - } - - protected void assertDefaultWorks() { - Template defaultTemplate = client.templateBuilder().build(); - assertEquals(defaultTemplate.getImage().getOperatingSystem().is64Bit(), true); - assertEquals(defaultTemplate.getImage().getOperatingSystem().getVersion(), "2010.11.1-beta"); - assertEquals(defaultTemplate.getImage().getOperatingSystem().getFamily(), OsFamily.AMZN_LINUX); - assertEquals(getCores(defaultTemplate.getHardware()), 1.0d); - } - @Test(enabled = true, dependsOnMethods = "testCompareSizes") public void testExtendedOptionsAndLogin() throws Exception { SecurityGroupClient securityGroupClient = EC2Client.class.cast(context.getProviderSpecificContext().getApi()) diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/compute/strategy/CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/compute/strategy/CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest.java index 5b20782e9a..cf767c0279 100644 --- a/apis/ec2/src/test/java/org/jclouds/ec2/compute/strategy/CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest.java +++ b/apis/ec2/src/test/java/org/jclouds/ec2/compute/strategy/CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest.java @@ -47,6 +47,7 @@ import org.jclouds.ec2.options.RunInstancesOptions; import org.jclouds.encryption.internal.Base64; import org.testng.annotations.Test; +import com.google.common.base.Function; import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.ImmutableSet; @@ -426,10 +427,10 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest { Map credentialsMap = createMock(Map.class); Map securityGroupMap = createMock(Map.class); CreateUniqueKeyPair createUniqueKeyPair = createMock(CreateUniqueKeyPair.class); - CreateSecurityGroupIfNeeded createSecurityGroupIfNeeded = createMock(CreateSecurityGroupIfNeeded.class); + Function createSecurityGroupIfNeeded = createMock(CreateSecurityGroupIfNeeded.class); return new CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions(credentialsMap, securityGroupMap, - createUniqueKeyPair, createSecurityGroupIfNeeded, OPTIONS_PROVIDER); + createUniqueKeyPair, createSecurityGroupIfNeeded, OPTIONS_PROVIDER); } private void replayStrategy(CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions strategy) { diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/compute/strategy/EC2RunNodesAndAddToSetStrategyTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/compute/strategy/EC2RunNodesAndAddToSetStrategyTest.java index 568a2b4978..e79ff2b0c7 100644 --- a/apis/ec2/src/test/java/org/jclouds/ec2/compute/strategy/EC2RunNodesAndAddToSetStrategyTest.java +++ b/apis/ec2/src/test/java/org/jclouds/ec2/compute/strategy/EC2RunNodesAndAddToSetStrategyTest.java @@ -35,6 +35,7 @@ import org.jclouds.compute.domain.Hardware; import org.jclouds.compute.domain.Image; import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.compute.domain.Template; +import org.jclouds.compute.domain.TemplateBuilder; import org.jclouds.compute.util.ComputeUtils; import org.jclouds.domain.Credentials; import org.jclouds.domain.Location; @@ -54,6 +55,7 @@ import com.google.common.base.Predicate; 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 @@ -97,25 +99,29 @@ public class EC2RunNodesAndAddToSetStrategyTest { String imageId = "ami1"; String instanceCreatedId = "instance1"; // setup mocks - EC2CreateNodesInGroupThenAddToSet strategy = setupStrategy(); + TemplateBuilder templateBuilder = createMock(TemplateBuilder.class); + EC2CreateNodesInGroupThenAddToSet strategy = setupStrategy(templateBuilder); InputParams input = new InputParams(location); InstanceClient instanceClient = createMock(InstanceClient.class); RunInstancesOptions ec2Options = createMock(RunInstancesOptions.class); RunningInstance instance = createMock(RunningInstance.class); - Reservation reservation = new Reservation(region, ImmutableSet - . of(), ImmutableSet. of(instance), "ownerId", "requesterId", "reservationId"); + Reservation reservation = new Reservation(region, + ImmutableSet. of(), ImmutableSet. of(instance), "ownerId", "requesterId", + "reservationId"); NodeMetadata nodeMetadata = createMock(NodeMetadata.class); // setup expectations + expect(templateBuilder.fromTemplate(input.template)).andReturn(templateBuilder); + expect(templateBuilder.build()).andReturn(input.template); expect(strategy.client.getInstanceServices()).andReturn(instanceClient).atLeastOnce(); expect( - strategy.createKeyPairAndSecurityGroupsAsNeededAndReturncustomize.execute(region, input.tag, - input.template)).andReturn(ec2Options); + strategy.createKeyPairAndSecurityGroupsAsNeededAndReturncustomize + .execute(region, input.tag, input.template)).andReturn(ec2Options); expect(input.template.getLocation()).andReturn(input.location).atLeastOnce(); expect(input.template.getImage()).andReturn(input.image).atLeastOnce(); expect(input.image.getProviderId()).andReturn(imageId).atLeastOnce(); expect(instanceClient.runInstancesInRegion(region, zone, imageId, 1, input.count, ec2Options)).andReturn( - (Reservation) reservation); + (Reservation) reservation); expect(instance.getId()).andReturn(instanceCreatedId).atLeastOnce(); // simulate a lazy credentials fetch Credentials creds = new Credentials("foo", "bar"); @@ -128,11 +134,12 @@ public class EC2RunNodesAndAddToSetStrategyTest { expect(strategy.runningInstanceToNodeMetadata.apply(instance)).andReturn(nodeMetadata); expect( - strategy.utils.customizeNodesAndAddToGoodMapOrPutExceptionIntoBadMap(eq(input.options), - containsNodeMetadata(nodeMetadata), eq(input.nodes), eq(input.badNodes), - eq(input.customization))).andReturn(null); + strategy.utils.customizeNodesAndAddToGoodMapOrPutExceptionIntoBadMap(eq(input.options), + containsNodeMetadata(nodeMetadata), eq(input.nodes), eq(input.badNodes), eq(input.customization))) + .andReturn(null); // replay mocks + replay(templateBuilder); replay(instanceClient); replay(ec2Options); replay(instance); @@ -144,6 +151,7 @@ public class EC2RunNodesAndAddToSetStrategyTest { strategy.execute(input.tag, input.count, input.template, input.nodes, input.badNodes, input.customization); // verify mocks + verify(templateBuilder); verify(instanceClient); verify(ec2Options); verify(instance); @@ -152,12 +160,12 @@ public class EC2RunNodesAndAddToSetStrategyTest { verifyStrategy(strategy); } - private static final Location REGION_AP_SOUTHEAST_1 = new LocationBuilder().scope(LocationScope.REGION).id( - "ap-southeast-1").description("ap-southeast-1").parent( - new LocationBuilder().scope(LocationScope.PROVIDER).id("aws-ec2").description("aws-ec2").build()).build(); - private static final Location ZONE_AP_SOUTHEAST_1A = new LocationBuilder().scope(LocationScope.ZONE).id( - "ap-southeast-1a").description("ap-southeast-1a").parent( - REGION_AP_SOUTHEAST_1).build(); + private static final Location REGION_AP_SOUTHEAST_1 = new LocationBuilder().scope(LocationScope.REGION) + .id("ap-southeast-1").description("ap-southeast-1") + .parent(new LocationBuilder().scope(LocationScope.PROVIDER).id("aws-ec2").description("aws-ec2").build()) + .build(); + private static final Location ZONE_AP_SOUTHEAST_1A = new LocationBuilder().scope(LocationScope.ZONE) + .id("ap-southeast-1a").description("ap-southeast-1a").parent(REGION_AP_SOUTHEAST_1).build(); // ///////////////////////////////////////////////////////////////////// @SuppressWarnings("unchecked") @@ -209,7 +217,7 @@ public class EC2RunNodesAndAddToSetStrategyTest { } @SuppressWarnings("unchecked") - private EC2CreateNodesInGroupThenAddToSet setupStrategy() { + private EC2CreateNodesInGroupThenAddToSet setupStrategy(TemplateBuilder template) { EC2Client client = createMock(EC2Client.class); CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions createKeyPairAndSecurityGroupsAsNeededAndReturncustomize = createMock(CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.class); Predicate instanceStateRunning = createMock(Predicate.class); @@ -217,8 +225,9 @@ public class EC2RunNodesAndAddToSetStrategyTest { Function instanceToCredentials = createMock(Function.class); Map credentialStore = createMock(Map.class); ComputeUtils utils = createMock(ComputeUtils.class); - return new EC2CreateNodesInGroupThenAddToSet(client, createKeyPairAndSecurityGroupsAsNeededAndReturncustomize, - instanceStateRunning, runningInstanceToNodeMetadata, instanceToCredentials, credentialStore, utils); + return new EC2CreateNodesInGroupThenAddToSet(client, Providers. of(template), + createKeyPairAndSecurityGroupsAsNeededAndReturncustomize, instanceStateRunning, + runningInstanceToNodeMetadata, instanceToCredentials, credentialStore, utils); } private void replayStrategy(EC2CreateNodesInGroupThenAddToSet strategy) { diff --git a/apis/eucalyptus/src/test/java/org/jclouds/eucalyptus/compute/EucalyptusComputeServiceLiveTest.java b/apis/eucalyptus/src/test/java/org/jclouds/eucalyptus/compute/EucalyptusComputeServiceLiveTest.java index 1cc6166b2c..1c6e7254e0 100644 --- a/apis/eucalyptus/src/test/java/org/jclouds/eucalyptus/compute/EucalyptusComputeServiceLiveTest.java +++ b/apis/eucalyptus/src/test/java/org/jclouds/eucalyptus/compute/EucalyptusComputeServiceLiveTest.java @@ -19,11 +19,6 @@ package org.jclouds.eucalyptus.compute; -import static org.jclouds.compute.util.ComputeServiceUtils.getCores; -import static org.testng.Assert.assertEquals; - -import org.jclouds.compute.domain.OsFamily; -import org.jclouds.compute.domain.Template; import org.jclouds.ec2.compute.EC2ComputeServiceLiveTest; import org.jclouds.http.HttpResponseException; import org.testng.annotations.Test; @@ -41,14 +36,6 @@ public class EucalyptusComputeServiceLiveTest extends EC2ComputeServiceLiveTest group = "eu"; } - @Override - protected void assertDefaultWorks() { - Template defaultTemplate = client.templateBuilder().build(); - assertEquals(defaultTemplate.getImage().getOperatingSystem().is64Bit(), true); - assertEquals(defaultTemplate.getImage().getOperatingSystem().getFamily(), OsFamily.CENTOS); - assertEquals(getCores(defaultTemplate.getHardware()), 1.0d); - } - @Override @Test(enabled = true, dependsOnMethods = "testReboot") public void testSuspendResume() throws Exception { diff --git a/common/trmk/src/main/java/org/jclouds/vcloud/terremark/compute/domain/KeyPairCredentials.java b/common/trmk/src/main/java/org/jclouds/vcloud/terremark/compute/domain/KeyPairCredentials.java index 3fa6bfe126..6f0176d91f 100644 --- a/common/trmk/src/main/java/org/jclouds/vcloud/terremark/compute/domain/KeyPairCredentials.java +++ b/common/trmk/src/main/java/org/jclouds/vcloud/terremark/compute/domain/KeyPairCredentials.java @@ -26,6 +26,26 @@ import org.jclouds.vcloud.terremark.domain.KeyPair; */ public class KeyPairCredentials extends Credentials { + public static class Builder extends Credentials.Builder { + private String identity; + private KeyPair keyPair; + + public Builder identity(String identity) { + this.identity = identity; + return this; + } + + public Builder keyPair(KeyPair keyPair) { + this.keyPair = keyPair; + return this; + } + + @SuppressWarnings("unchecked") + public T build() { + return (T) new KeyPairCredentials(identity, keyPair); + } + } + public KeyPair getKeyPair() { return keyPair; } @@ -37,4 +57,8 @@ public class KeyPairCredentials extends Credentials { this.keyPair = keyPair; } + public Builder toBuilder() { + return new Builder().identity(identity).keyPair(keyPair); + } + } \ No newline at end of file diff --git a/compute/src/main/java/org/jclouds/compute/internal/BaseComputeService.java b/compute/src/main/java/org/jclouds/compute/internal/BaseComputeService.java index 33be65a04c..b32fe44d32 100755 --- a/compute/src/main/java/org/jclouds/compute/internal/BaseComputeService.java +++ b/compute/src/main/java/org/jclouds/compute/internal/BaseComputeService.java @@ -70,16 +70,17 @@ import org.jclouds.compute.options.RunScriptOptions; import org.jclouds.compute.options.TemplateOptions; import org.jclouds.compute.reference.ComputeServiceConstants; import org.jclouds.compute.reference.ComputeServiceConstants.Timeouts; +import org.jclouds.compute.strategy.CreateNodesInGroupThenAddToSet; import org.jclouds.compute.strategy.DestroyNodeStrategy; import org.jclouds.compute.strategy.GetNodeMetadataStrategy; import org.jclouds.compute.strategy.InitializeRunScriptOnNodeOrPlaceInBadMap; import org.jclouds.compute.strategy.ListNodesStrategy; import org.jclouds.compute.strategy.RebootNodeStrategy; import org.jclouds.compute.strategy.ResumeNodeStrategy; -import org.jclouds.compute.strategy.CreateNodesInGroupThenAddToSet; import org.jclouds.compute.strategy.RunScriptOnNodeAndAddToGoodMapOrPutExceptionIntoBadMap; import org.jclouds.compute.strategy.SuspendNodeStrategy; import org.jclouds.domain.Credentials; +import org.jclouds.domain.Credentials.Builder; import org.jclouds.domain.Location; import org.jclouds.io.Payload; import org.jclouds.logging.Logger; @@ -132,18 +133,17 @@ public class BaseComputeService implements ComputeService { @Inject protected BaseComputeService(ComputeServiceContext context, Map credentialStore, - @Memoized Supplier> images, - @Memoized Supplier> hardwareProfiles, - @Memoized Supplier> locations, ListNodesStrategy listNodesStrategy, - GetNodeMetadataStrategy getNodeMetadataStrategy, CreateNodesInGroupThenAddToSet runNodesAndAddToSetStrategy, - RebootNodeStrategy rebootNodeStrategy, DestroyNodeStrategy destroyNodeStrategy, - ResumeNodeStrategy resumeNodeStrategy, SuspendNodeStrategy suspendNodeStrategy, - Provider templateBuilderProvider, Provider templateOptionsProvider, - @Named("NODE_RUNNING") Predicate nodeRunning, - @Named("NODE_TERMINATED") Predicate nodeTerminated, - @Named("NODE_SUSPENDED") Predicate nodeSuspended, - InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory, Timeouts timeouts, - @Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor) { + @Memoized Supplier> images, @Memoized Supplier> hardwareProfiles, + @Memoized Supplier> locations, ListNodesStrategy listNodesStrategy, + GetNodeMetadataStrategy getNodeMetadataStrategy, CreateNodesInGroupThenAddToSet runNodesAndAddToSetStrategy, + RebootNodeStrategy rebootNodeStrategy, DestroyNodeStrategy destroyNodeStrategy, + ResumeNodeStrategy resumeNodeStrategy, SuspendNodeStrategy suspendNodeStrategy, + Provider templateBuilderProvider, Provider templateOptionsProvider, + @Named("NODE_RUNNING") Predicate nodeRunning, + @Named("NODE_TERMINATED") Predicate nodeTerminated, + @Named("NODE_SUSPENDED") Predicate nodeSuspended, + InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory, Timeouts timeouts, + @Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor) { this.context = checkNotNull(context, "context"); this.credentialStore = checkNotNull(credentialStore, "credentialStore"); this.images = checkNotNull(images, "images"); @@ -179,7 +179,7 @@ public class BaseComputeService implements ComputeService { */ @Override public Set runNodesWithTag(String group, int count, Template template) - throws RunNodesException { + throws RunNodesException { return createNodesInGroup(group, count, template); } @@ -188,7 +188,7 @@ public class BaseComputeService implements ComputeService { */ @Override public Set runNodesWithTag(String group, int count, TemplateOptions templateOptions) - throws RunNodesException { + throws RunNodesException { return createNodesInGroup(group, count, templateBuilder().any().options(templateOptions).build()); } @@ -202,20 +202,20 @@ public class BaseComputeService implements ComputeService { @Override public Set createNodesInGroup(String group, int count, Template template) - throws RunNodesException { + throws RunNodesException { checkNotNull(group, "group cannot be null"); checkNotNull(template.getLocation(), "location"); logger.debug(">> running %d node%s group(%s) location(%s) image(%s) hardwareProfile(%s) options(%s)", count, - count > 1 ? "s" : "", group, template.getLocation().getId(), template.getImage().getId(), template - .getHardware().getId(), template.getOptions()); + count > 1 ? "s" : "", group, template.getLocation().getId(), template.getImage().getId(), template + .getHardware().getId(), template.getOptions()); Set goodNodes = newLinkedHashSet(); Map badNodes = newLinkedHashMap(); Multimap customizationResponses = LinkedHashMultimap.create(); Map> responses = runNodesAndAddToSetStrategy.execute(group, count, template, goodNodes, badNodes, - customizationResponses); + customizationResponses); Map executionExceptions = awaitCompletion(responses, executor, null, logger, "runNodesWithTag(" - + group + ")"); + + group + ")"); for (NodeMetadata node : concat(goodNodes, badNodes.keySet())) if (node.getCredentials() != null) credentialStore.put("node#" + node.getId(), node.getCredentials()); @@ -227,7 +227,7 @@ public class BaseComputeService implements ComputeService { @Override public Set createNodesInGroup(String group, int count, TemplateOptions templateOptions) - throws RunNodesException { + throws RunNodesException { return createNodesInGroup(group, count, templateBuilder().any().options(templateOptions).build()); } @@ -274,23 +274,23 @@ public class BaseComputeService implements ComputeService { public Set destroyNodesMatching(Predicate filter) { logger.debug(">> destroying nodes matching(%s)", filter); Set set = newLinkedHashSet(transformParallel(nodesMatchingFilterAndNotTerminated(filter), - new Function>() { + new Function>() { - // TODO make an async interface instead of re-wrapping - @Override - public Future apply(final NodeMetadata from) { - return executor.submit(new Callable() { + // TODO make an async interface instead of re-wrapping + @Override + public Future apply(final NodeMetadata from) { + return executor.submit(new Callable() { - @Override - public NodeMetadata call() throws Exception { - destroyNode(from.getId()); - return from; - } + @Override + public NodeMetadata call() throws Exception { + destroyNode(from.getId()); + return from; + } - }); - } + }); + } - }, executor, null, logger, "destroyNodesMatching(" + filter + ")")); + }, executor, null, logger, "destroyNodesMatching(" + filter + ")")); logger.debug("<< destroyed(%d)", set.size()); return set; } @@ -304,7 +304,7 @@ public class BaseComputeService implements ComputeService { * if none found */ Iterable nodesMatchingFilterAndNotTerminatedExceptionIfNotFound( - Predicate filter) { + Predicate filter) { Iterable nodes = nodesMatchingFilterAndNotTerminated(filter); if (Iterables.size(nodes) == 0) throw new NoSuchElementException("no nodes matched filter: " + filter); @@ -394,15 +394,15 @@ public class BaseComputeService implements ComputeService { public void rebootNodesMatching(Predicate filter) { logger.debug(">> rebooting nodes matching(%s)", filter); transformParallel(nodesMatchingFilterAndNotTerminatedExceptionIfNotFound(filter), - new Function>() { - // TODO use native async - @Override - public Future apply(NodeMetadata from) { - rebootNode(from.getId()); - return immediateFuture(null); - } + new Function>() { + // TODO use native async + @Override + public Future apply(NodeMetadata from) { + rebootNode(from.getId()); + return immediateFuture(null); + } - }, executor, null, logger, "rebootNodesMatching(" + filter + ")"); + }, executor, null, logger, "rebootNodesMatching(" + filter + ")"); logger.debug("<< rebooted"); } @@ -425,15 +425,15 @@ public class BaseComputeService implements ComputeService { public void resumeNodesMatching(Predicate filter) { logger.debug(">> resuming nodes matching(%s)", filter); transformParallel(nodesMatchingFilterAndNotTerminatedExceptionIfNotFound(filter), - new Function>() { - // TODO use native async - @Override - public Future apply(NodeMetadata from) { - resumeNode(from.getId()); - return immediateFuture(null); - } + new Function>() { + // TODO use native async + @Override + public Future apply(NodeMetadata from) { + resumeNode(from.getId()); + return immediateFuture(null); + } - }, executor, null, logger, "resumeNodesMatching(" + filter + ")"); + }, executor, null, logger, "resumeNodesMatching(" + filter + ")"); logger.debug("<< resumed"); } @@ -456,15 +456,15 @@ public class BaseComputeService implements ComputeService { public void suspendNodesMatching(Predicate filter) { logger.debug(">> suspending nodes matching(%s)", filter); transformParallel(nodesMatchingFilterAndNotTerminatedExceptionIfNotFound(filter), - new Function>() { - // TODO use native async - @Override - public Future apply(NodeMetadata from) { - suspendNode(from.getId()); - return immediateFuture(null); - } + new Function>() { + // TODO use native async + @Override + public Future apply(NodeMetadata from) { + suspendNode(from.getId()); + return immediateFuture(null); + } - }, executor, null, logger, "suspendNodesMatching(" + filter + ")"); + }, executor, null, logger, "suspendNodesMatching(" + filter + ")"); logger.debug("<< suspended"); } @@ -473,7 +473,7 @@ public class BaseComputeService implements ComputeService { */ @Override public Map runScriptOnNodesMatching(Predicate filter, Payload runScript) - throws RunScriptOnNodesException { + throws RunScriptOnNodesException { return runScriptOnNodesMatching(filter, runScript, RunScriptOptions.NONE); } @@ -482,10 +482,10 @@ public class BaseComputeService implements ComputeService { */ @Override public Map runScriptOnNodesMatching(Predicate filter, Payload runScript, - RunScriptOptions options) throws RunScriptOnNodesException { + RunScriptOptions options) throws RunScriptOnNodesException { try { - return runScriptOnNodesMatching(filter, Statements.exec(Strings2.toStringAndClose(checkNotNull(runScript, - "runScript").getInput())), options); + return runScriptOnNodesMatching(filter, + Statements.exec(Strings2.toStringAndClose(checkNotNull(runScript, "runScript").getInput())), options); } catch (IOException e) { Throwables.propagate(e); return null; @@ -497,7 +497,7 @@ public class BaseComputeService implements ComputeService { */ @Override public Map runScriptOnNodesMatching(Predicate filter, String runScript) - throws RunScriptOnNodesException { + throws RunScriptOnNodesException { return runScriptOnNodesMatching(filter, Statements.exec(checkNotNull(runScript, "runScript"))); } @@ -506,15 +506,15 @@ public class BaseComputeService implements ComputeService { */ @Override public Map runScriptOnNodesMatching(Predicate filter, Statement runScript) - throws RunScriptOnNodesException { + throws RunScriptOnNodesException { return runScriptOnNodesMatching(filter, runScript, RunScriptOptions.NONE); } @Override public Map runScriptOnNodesMatching(Predicate filter, - String runScript, RunScriptOptions options) throws RunScriptOnNodesException { + String runScript, RunScriptOptions options) throws RunScriptOnNodesException { return runScriptOnNodesMatching(filter, Statements.exec(checkNotNull(runScript, "runScript")), - RunScriptOptions.NONE); + RunScriptOptions.NONE); } /** @@ -522,7 +522,7 @@ public class BaseComputeService implements ComputeService { */ @Override public Map runScriptOnNodesMatching(Predicate filter, Statement runScript, - RunScriptOptions options) throws RunScriptOnNodesException { + RunScriptOptions options) throws RunScriptOnNodesException { checkNotNull(filter, "filter"); checkNotNull(runScript, "runScript"); @@ -534,11 +534,11 @@ public class BaseComputeService implements ComputeService { Map exceptions = ImmutableMap. of(); Iterable scriptRunners = transformNodesIntoInitializedScriptRunners( - nodesMatchingFilterAndNotTerminatedExceptionIfNotFound(filter), runScript, options, badNodes); + nodesMatchingFilterAndNotTerminatedExceptionIfNotFound(filter), runScript, options, badNodes); if (Iterables.size(scriptRunners) > 0) { for (RunScriptOnNode runner : scriptRunners) { responses.put(runner.getNode(), executor.submit(new RunScriptOnNodeAndAddToGoodMapOrPutExceptionIntoBadMap( - runner, goodNodes, badNodes))); + runner, goodNodes, badNodes))); } exceptions = awaitCompletion(responses, executor, null, logger, "runScriptOnNodesMatching(" + filter + ")"); } @@ -550,10 +550,11 @@ public class BaseComputeService implements ComputeService { } private Iterable transformNodesIntoInitializedScriptRunners( - Iterable nodes, Statement script, RunScriptOptions options, - Map badNodes) { - return filter(transformParallel(nodes, new TransformNodesIntoInitializedScriptRunners(script, options, badNodes), - executor, null, logger, "initialize script runners"), notNull()); + Iterable nodes, Statement script, RunScriptOptions options, + Map badNodes) { + return filter( + transformParallel(nodes, new TransformNodesIntoInitializedScriptRunners(script, options, badNodes), + executor, null, logger, "initialize script runners"), notNull()); } private Set detailsOnAllNodes() { @@ -566,13 +567,13 @@ public class BaseComputeService implements ComputeService { } private final class TransformNodesIntoInitializedScriptRunners implements - Function> { + Function> { private final Map badNodes; private final Statement script; private final RunScriptOptions options; private TransformNodesIntoInitializedScriptRunners(Statement script, RunScriptOptions options, - Map badNodes) { + Map badNodes) { this.badNodes = checkNotNull(badNodes, "badNodes"); this.script = checkNotNull(script, "script"); this.options = checkNotNull(options, "options"); @@ -581,8 +582,14 @@ public class BaseComputeService implements ComputeService { @Override public Future apply(NodeMetadata node) { checkNotNull(node, "node"); - if (options.getOverrideCredentials() != null) { - node = NodeMetadataBuilder.fromNodeMetadata(node).credentials(options.getOverrideCredentials()).build(); + if (options.getOverridingCredentials() != null) { + Builder builder = node.getCredentials() != null ? node.getCredentials().toBuilder() + : new Credentials.Builder(); + if (options.getOverridingCredentials().identity != null) + builder.identity(options.getOverridingCredentials().identity); + if (options.getOverridingCredentials().credential != null) + builder.credential(options.getOverridingCredentials().credential); + node = NodeMetadataBuilder.fromNodeMetadata(node).credentials(builder.build()).build(); } return executor.submit(initScriptRunnerFactory.create(node, script, options, badNodes)); } diff --git a/core/src/main/java/org/jclouds/domain/Credentials.java b/core/src/main/java/org/jclouds/domain/Credentials.java index 886928ada0..a77acf5b26 100644 --- a/core/src/main/java/org/jclouds/domain/Credentials.java +++ b/core/src/main/java/org/jclouds/domain/Credentials.java @@ -34,6 +34,26 @@ import com.google.common.collect.Lists; */ public class Credentials { + public static class Builder { + private String identity; + private String credential; + + public Builder identity(String identity) { + this.identity = identity; + return this; + } + + public Builder credential(String credential) { + this.credential = credential; + return this; + } + + @SuppressWarnings("unchecked") + public T build() { + return (T) new Credentials(identity, credential); + } + } + public final String identity; public final String credential; @@ -45,7 +65,7 @@ public class Credentials { public static Credentials parse(URI uri) { checkNotNull(uri, "uri"); List userInfo = Lists.newArrayList(Splitter.on(':').split( - checkNotNull(uri.getUserInfo(), "no userInfo in " + uri))); + checkNotNull(uri.getUserInfo(), "no userInfo in " + uri))); String identity = checkNotNull(userInfo.get(0), "no username in " + uri.getUserInfo()); if (Strings2.isUrlEncoded(identity)) { identity = Strings2.urlDecode(identity); @@ -57,6 +77,10 @@ public class Credentials { return new Credentials(identity, credential); } + public Builder toBuilder() { + return new Builder().identity(identity).credential(credential); + } + @Override public int hashCode() { final int prime = 31; 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 57247c8ad1..4dfa58be69 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 @@ -22,8 +22,8 @@ package org.jclouds.aws.ec2.compute; import static com.google.common.base.Preconditions.checkState; import java.util.Map; -import java.util.Set; import java.util.Map.Entry; +import java.util.Set; import java.util.concurrent.ExecutorService; import javax.inject.Inject; @@ -43,13 +43,13 @@ import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.compute.domain.TemplateBuilder; import org.jclouds.compute.options.TemplateOptions; import org.jclouds.compute.reference.ComputeServiceConstants.Timeouts; +import org.jclouds.compute.strategy.CreateNodesInGroupThenAddToSet; import org.jclouds.compute.strategy.DestroyNodeStrategy; import org.jclouds.compute.strategy.GetNodeMetadataStrategy; import org.jclouds.compute.strategy.InitializeRunScriptOnNodeOrPlaceInBadMap; import org.jclouds.compute.strategy.ListNodesStrategy; import org.jclouds.compute.strategy.RebootNodeStrategy; import org.jclouds.compute.strategy.ResumeNodeStrategy; -import org.jclouds.compute.strategy.CreateNodesInGroupThenAddToSet; import org.jclouds.compute.strategy.SuspendNodeStrategy; import org.jclouds.domain.Credentials; import org.jclouds.domain.Location; @@ -75,44 +75,47 @@ public class AWSEC2ComputeService extends EC2ComputeService { @Inject protected AWSEC2ComputeService(ComputeServiceContext context, Map credentialStore, - @Memoized Supplier> images, @Memoized Supplier> sizes, - @Memoized Supplier> locations, ListNodesStrategy listNodesStrategy, - GetNodeMetadataStrategy getNodeMetadataStrategy, CreateNodesInGroupThenAddToSet runNodesAndAddToSetStrategy, - RebootNodeStrategy rebootNodeStrategy, DestroyNodeStrategy destroyNodeStrategy, - ResumeNodeStrategy startNodeStrategy, SuspendNodeStrategy stopNodeStrategy, - Provider templateBuilderProvider, Provider templateOptionsProvider, - @Named("NODE_RUNNING") Predicate nodeRunning, - @Named("NODE_TERMINATED") Predicate nodeTerminated, - @Named("NODE_SUSPENDED") Predicate nodeSuspended, - InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory, Timeouts timeouts, - @Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor, AWSEC2Client ec2Client, - Map credentialsMap, @Named("SECURITY") Map securityGroupMap, - @Named("PLACEMENT") Map placementGroupMap, - @Named("DELETED") Predicate placementGroupDeleted) { + @Memoized Supplier> images, @Memoized Supplier> sizes, + @Memoized Supplier> locations, ListNodesStrategy listNodesStrategy, + GetNodeMetadataStrategy getNodeMetadataStrategy, CreateNodesInGroupThenAddToSet runNodesAndAddToSetStrategy, + RebootNodeStrategy rebootNodeStrategy, DestroyNodeStrategy destroyNodeStrategy, + ResumeNodeStrategy startNodeStrategy, SuspendNodeStrategy stopNodeStrategy, + Provider templateBuilderProvider, Provider templateOptionsProvider, + @Named("NODE_RUNNING") Predicate nodeRunning, + @Named("NODE_TERMINATED") Predicate nodeTerminated, + @Named("NODE_SUSPENDED") Predicate nodeSuspended, + InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory, Timeouts timeouts, + @Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor, AWSEC2Client ec2Client, + Map credentialsMap, @Named("SECURITY") Map securityGroupMap, + @Named("PLACEMENT") Map placementGroupMap, + @Named("DELETED") Predicate placementGroupDeleted) { super(context, credentialStore, images, sizes, locations, listNodesStrategy, getNodeMetadataStrategy, - runNodesAndAddToSetStrategy, rebootNodeStrategy, destroyNodeStrategy, startNodeStrategy, - stopNodeStrategy, templateBuilderProvider, templateOptionsProvider, nodeRunning, nodeTerminated, - nodeSuspended, initScriptRunnerFactory, timeouts, executor, ec2Client, credentialsMap, securityGroupMap); + runNodesAndAddToSetStrategy, rebootNodeStrategy, destroyNodeStrategy, startNodeStrategy, stopNodeStrategy, + templateBuilderProvider, templateOptionsProvider, nodeRunning, nodeTerminated, nodeSuspended, + initScriptRunnerFactory, timeouts, executor, ec2Client, credentialsMap, securityGroupMap); this.ec2Client = ec2Client; this.placementGroupMap = placementGroupMap; this.placementGroupDeleted = placementGroupDeleted; } @VisibleForTesting - void deletePlacementGroup(String region, String tag) { - Preconditions2.checkNotEmpty(tag, "tag"); - String group = String.format("jclouds#%s#%s", tag, region); + void deletePlacementGroup(String region, String group) { + Preconditions2.checkNotEmpty(group, "group"); + // placementGroupName must be unique within an account per + // 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, group).size() > 0) { - logger.debug(">> deleting placementGroup(%s)", group); + if (ec2Client.getPlacementGroupServices().describePlacementGroupsInRegion(region, placementGroup).size() > 0) { + logger.debug(">> deleting placementGroup(%s)", placementGroup); try { - ec2Client.getPlacementGroupServices().deletePlacementGroupInRegion(region, group); - checkState(placementGroupDeleted.apply(new PlacementGroup(region, group, "cluster", State.PENDING)), - String.format("placementGroup region(%s) name(%s) failed to delete", region, group)); - placementGroupMap.remove(new RegionAndName(region, group)); - logger.debug("<< deleted placementGroup(%s)", group); + ec2Client.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)); + placementGroupMap.remove(new RegionAndName(region, placementGroup)); + logger.debug("<< deleted placementGroup(%s)", placementGroup); } catch (IllegalStateException e) { - logger.debug("<< inUse placementGroup(%s)", group); + logger.debug("<< inUse placementGroup(%s)", placementGroup); } } } catch (UnsupportedOperationException e) { @@ -120,6 +123,7 @@ public class AWSEC2ComputeService extends EC2ComputeService { } } + @Override protected void cleanUpIncidentalResources(Entry regionTag) { super.cleanUpIncidentalResources(regionTag); deletePlacementGroup(regionTag.getKey(), regionTag.getValue()); diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceDependenciesModule.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceDependenciesModule.java index cfdfd72bc2..fca529f502 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceDependenciesModule.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceDependenciesModule.java @@ -34,10 +34,13 @@ import org.jclouds.aws.ec2.AWSEC2Client; import org.jclouds.aws.ec2.compute.AWSEC2ComputeService; import org.jclouds.aws.ec2.compute.AWSEC2TemplateOptions; import org.jclouds.aws.ec2.domain.PlacementGroup; +import org.jclouds.aws.ec2.domain.RegionNameAndPublicKeyMaterial; +import org.jclouds.aws.ec2.functions.ImportOrReturnExistingKeypair; import org.jclouds.aws.ec2.predicates.PlacementGroupAvailable; import org.jclouds.aws.ec2.predicates.PlacementGroupDeleted; import org.jclouds.compute.ComputeService; import org.jclouds.compute.ComputeServiceContext; +import org.jclouds.compute.domain.Image; import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.compute.domain.TemplateBuilder; import org.jclouds.compute.internal.ComputeServiceContextImpl; @@ -45,10 +48,14 @@ import org.jclouds.compute.options.TemplateOptions; import org.jclouds.domain.Credentials; import org.jclouds.ec2.compute.config.EC2ComputeServiceDependenciesModule; import org.jclouds.ec2.compute.domain.RegionAndName; +import org.jclouds.ec2.compute.domain.RegionNameAndIngressRules; import org.jclouds.ec2.compute.functions.CreateSecurityGroupIfNeeded; +import org.jclouds.ec2.compute.functions.CreateUniqueKeyPair; import org.jclouds.ec2.compute.functions.CredentialsForInstance; +import org.jclouds.ec2.compute.functions.RegionAndIdToImage; import org.jclouds.ec2.compute.functions.RunningInstanceToNodeMetadata; import org.jclouds.ec2.compute.internal.EC2TemplateBuilderImpl; +import org.jclouds.ec2.domain.KeyPair; import org.jclouds.ec2.domain.RunningInstance; import org.jclouds.predicates.RetryablePredicate; import org.jclouds.rest.RestContext; @@ -75,6 +82,14 @@ public class AWSEC2ComputeServiceDependenciesModule extends EC2ComputeServiceDep }).to(RunningInstanceToNodeMetadata.class); bind(new TypeLiteral>() { }).to(CredentialsForInstance.class); + bind(new TypeLiteral>() { + }).to(CreateSecurityGroupIfNeeded.class); + bind(new TypeLiteral>() { + }).to(CreateUniqueKeyPair.class); + bind(new TypeLiteral>() { + }).to(ImportOrReturnExistingKeypair.class); + bind(new TypeLiteral>() { + }).to(RegionAndIdToImage.class); bind(new TypeLiteral() { }).to(new TypeLiteral>() { }).in(Scopes.SINGLETON); diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/strategy/AWSEC2ReviseParsedImage.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/strategy/AWSEC2ReviseParsedImage.java index 83a894594b..5a5107c5dd 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/strategy/AWSEC2ReviseParsedImage.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/strategy/AWSEC2ReviseParsedImage.java @@ -100,7 +100,7 @@ public class AWSEC2ReviseParsedImage implements ReviseParsedImage { } catch (IllegalArgumentException e) { logger.debug("<< didn't match os(%s)", from.getImageLocation()); } catch (NoSuchElementException e) { - logger.debug("<< didn't match at all(%s)", from.getImageLocation()); + logger.trace("<< didn't match at all(%s)", from.getImageLocation()); } } diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/strategy/CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/strategy/CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions.java index 35913beb32..f7aa572d05 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/strategy/CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/strategy/CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions.java @@ -19,26 +19,35 @@ package org.jclouds.aws.ec2.compute.strategy; +import static com.google.common.base.Predicates.and; +import static com.google.common.base.Predicates.or; + import java.util.Map; +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.compute.AWSEC2TemplateOptions; +import org.jclouds.aws.ec2.domain.RegionNameAndPublicKeyMaterial; import org.jclouds.aws.ec2.functions.CreatePlacementGroupIfNeeded; import org.jclouds.aws.ec2.options.AWSRunInstancesOptions; import org.jclouds.compute.domain.Template; import org.jclouds.compute.options.TemplateOptions; +import org.jclouds.compute.reference.ComputeServiceConstants; import org.jclouds.ec2.compute.domain.RegionAndName; -import org.jclouds.ec2.compute.functions.CreateSecurityGroupIfNeeded; -import org.jclouds.ec2.compute.functions.CreateUniqueKeyPair; +import org.jclouds.ec2.compute.domain.RegionNameAndIngressRules; import org.jclouds.ec2.compute.options.EC2TemplateOptions; import org.jclouds.ec2.compute.strategy.CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions; import org.jclouds.ec2.domain.KeyPair; import org.jclouds.ec2.options.RunInstancesOptions; +import org.jclouds.logging.Logger; import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Function; +import com.google.common.base.Predicate; /** * @@ -46,31 +55,37 @@ import com.google.common.annotations.VisibleForTesting; */ @Singleton public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions extends - CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions { - + CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions { + @Resource + @Named(ComputeServiceConstants.COMPUTE_LOGGER) + protected Logger logger = Logger.NULL; @VisibleForTesting final Map placementGroupMap; @VisibleForTesting final CreatePlacementGroupIfNeeded createPlacementGroupIfNeeded; + @VisibleForTesting + final Function importExistingKeyPair; @Inject public CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions( - Map credentialsMap, @Named("SECURITY") Map securityGroupMap, - @Named("PLACEMENT") Map placementGroupMap, CreateUniqueKeyPair createUniqueKeyPair, - CreateSecurityGroupIfNeeded createSecurityGroupIfNeeded, - javax.inject.Provider optionsProvider, - CreatePlacementGroupIfNeeded createPlacementGroupIfNeeded) { + Map credentialsMap, @Named("SECURITY") Map securityGroupMap, + @Named("PLACEMENT") Map placementGroupMap, + Function createUniqueKeyPair, + Function createSecurityGroupIfNeeded, + Provider optionsProvider, CreatePlacementGroupIfNeeded createPlacementGroupIfNeeded, + Function importExistingKeyPair) { super(credentialsMap, securityGroupMap, createUniqueKeyPair, createSecurityGroupIfNeeded, optionsProvider); this.placementGroupMap = placementGroupMap; this.createPlacementGroupIfNeeded = createPlacementGroupIfNeeded; + this.importExistingKeyPair = importExistingKeyPair; } - public AWSRunInstancesOptions execute(String region, String tag, Template template) { - AWSRunInstancesOptions instanceOptions = AWSRunInstancesOptions.class.cast(super.execute(region, tag, template)); + public AWSRunInstancesOptions execute(String region, String group, Template template) { + AWSRunInstancesOptions instanceOptions = AWSRunInstancesOptions.class + .cast(super.execute(region, group, template)); String placementGroupName = template.getHardware().getId().startsWith("cc") ? createNewPlacementGroupUnlessUserSpecifiedOtherwise( - region, tag, template.getOptions()) - : null; + region, group, template.getOptions()) : null; if (placementGroupName != null) instanceOptions.inPlacementGroup(placementGroupName); @@ -82,17 +97,19 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions } @VisibleForTesting - String createNewPlacementGroupUnlessUserSpecifiedOtherwise(String region, String tag, TemplateOptions options) { + String createNewPlacementGroupUnlessUserSpecifiedOtherwise(String region, String group, TemplateOptions options) { String placementGroupName = null; boolean shouldAutomaticallyCreatePlacementGroup = true; if (options instanceof EC2TemplateOptions) { placementGroupName = AWSEC2TemplateOptions.class.cast(options).getPlacementGroup(); if (placementGroupName == null) shouldAutomaticallyCreatePlacementGroup = AWSEC2TemplateOptions.class.cast(options) - .shouldAutomaticallyCreatePlacementGroup(); + .shouldAutomaticallyCreatePlacementGroup(); } if (placementGroupName == null && shouldAutomaticallyCreatePlacementGroup) { - placementGroupName = String.format("jclouds#%s#%s", tag, region); + // placementGroupName must be unique within an account per + // http://docs.amazonwebservices.com/AWSEC2/latest/UserGuide/index.html?using_cluster_computing.html + placementGroupName = String.format("jclouds#%s#%s", group, region); RegionAndName regionAndName = new RegionAndName(region, placementGroupName); if (!placementGroupMap.containsKey(regionAndName)) { placementGroupMap.put(regionAndName, createPlacementGroupIfNeeded.apply(regionAndName)); @@ -102,12 +119,60 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions } @Override - protected void addSecurityGroups(String region, String tag, Template template, RunInstancesOptions instanceOptions) { + protected String createOrImportKeyPair(String region, String group, TemplateOptions options) { + RegionAndName key = new RegionAndName(region, "jclouds#" + group); + KeyPair pair = credentialsMap.get(key); + if (pair != null) + return pair.getKeyName(); + if (and(hasPublicKeyMaterial, or(doesntNeedSshAfterImportingPublicKey, hasLoginCredential)).apply(options)) { + pair = importExistingKeyPair.apply(new RegionNameAndPublicKeyMaterial(region, group, options.getPublicKey())); + options.dontAuthorizePublicKey(); + if (hasLoginCredential.apply(options)) + pair = pair.toBuilder().keyMaterial(options.getOverridingCredentials().credential).build(); + credentialsMap.put(key, pair); + } else { + if (hasPublicKeyMaterial.apply(options)) { + logger.warn("to avoid creating extra keys in aws-ec2, use templateOption overrideLoginCredentialWith(id_rsa)"); + } + return createUniqueKeyPairAndPutIntoMap(region, group); + } + return pair.getKeyName(); + } + + public static final Predicate hasPublicKeyMaterial = new Predicate() { + + @Override + public boolean apply(TemplateOptions options) { + return options.getPublicKey() != null; + } + + }; + + public static final Predicate doesntNeedSshAfterImportingPublicKey = new Predicate() { + + @Override + public boolean apply(TemplateOptions options) { + return (options.getRunScript() == null && options.getPrivateKey() == null); + } + + }; + + public static final Predicate hasLoginCredential = new Predicate() { + + @Override + public boolean apply(TemplateOptions options) { + return options.getOverridingCredentials() != null && options.getOverridingCredentials().credential != null; + } + + }; + + @Override + protected void addSecurityGroups(String region, String group, Template template, RunInstancesOptions instanceOptions) { String subnetId = AWSEC2TemplateOptions.class.cast(template.getOptions()).getSubnetId(); if (subnetId != null) { AWSRunInstancesOptions.class.cast(instanceOptions).withSubnetId(subnetId); } else { - super.addSecurityGroups(region, tag, template, instanceOptions); + super.addSecurityGroups(region, group, template, instanceOptions); } } } \ No newline at end of file diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/domain/RegionNameAndPublicKeyMaterial.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/domain/RegionNameAndPublicKeyMaterial.java new file mode 100644 index 0000000000..915bf9dd82 --- /dev/null +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/domain/RegionNameAndPublicKeyMaterial.java @@ -0,0 +1,43 @@ +/** + * + * Copyright (C) 2010 Cloud Conscious, LLC. + * + * ==================================================================== + * Licensed 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.domain; + +import org.jclouds.ec2.compute.domain.RegionAndName; + +/** + * + * @author Adrian Cole + */ +public class RegionNameAndPublicKeyMaterial extends RegionAndName { + private final String publicKeyMaterial; + + public RegionNameAndPublicKeyMaterial(String region, String tag, String publicKeyMaterial) { + super(region, tag); + this.publicKeyMaterial = publicKeyMaterial; + } + + // intentionally not overriding equals or hash-code so that we can search only by region/tag in a + // map + + public String getPublicKeyMaterial() { + return publicKeyMaterial; + } + +} diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/functions/ImportOrReturnExistingKeypair.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/functions/ImportOrReturnExistingKeypair.java new file mode 100644 index 0000000000..b59fabd75c --- /dev/null +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/functions/ImportOrReturnExistingKeypair.java @@ -0,0 +1,83 @@ +/** + * + * Copyright (C) 2010 Cloud Conscious, LLC. + * + * ==================================================================== + * Licensed 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.functions; + +import static com.google.common.base.Preconditions.checkNotNull; + +import javax.annotation.Resource; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.jclouds.aws.ec2.AWSEC2Client; +import org.jclouds.aws.ec2.domain.RegionNameAndPublicKeyMaterial; +import org.jclouds.compute.reference.ComputeServiceConstants; +import org.jclouds.ec2.domain.KeyPair; +import org.jclouds.logging.Logger; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Function; +import com.google.common.collect.Iterables; + +/** + * + * @author Adrian Cole + */ +@Singleton +public class ImportOrReturnExistingKeypair implements Function { + @Resource + @Named(ComputeServiceConstants.COMPUTE_LOGGER) + protected Logger logger = Logger.NULL; + protected final AWSEC2Client ec2Client; + + @Inject + public ImportOrReturnExistingKeypair(AWSEC2Client ec2Client) { + this.ec2Client = ec2Client; + } + + @Override + public KeyPair apply(RegionNameAndPublicKeyMaterial from) { + return importOrReturnExistingKeypair(from.getRegion(), from.getName(), from.getPublicKeyMaterial()); + } + + @VisibleForTesting + KeyPair importOrReturnExistingKeypair(String region, String group, String publicKeyMaterial) { + checkNotNull(region, "region"); + checkNotNull(group, "group"); + checkNotNull(publicKeyMaterial, "publicKeyMaterial"); + logger.debug(">> importing keyPair region(%s) group(%s)", region, group); + KeyPair keyPair = null; + // loop for eventual consistency or race condition. + // as this command is idempotent, it should be ok + while (keyPair == null) + try { + keyPair = ec2Client.getKeyPairServices().importKeyPairInRegion(region, "jclouds#" + group, + publicKeyMaterial); + logger.debug("<< imported keyPair(%s)", keyPair.getKeyName()); + } catch (IllegalStateException e) { + keyPair = Iterables.getFirst( + ec2Client.getKeyPairServices().describeKeyPairsInRegion(region, "jclouds#" + group), null); + if (keyPair != null) + logger.debug("<< retrieved existing keyPair(%s)", keyPair.getKeyName()); + } + return keyPair; + } + +} diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/AWSEC2ComputeServiceLiveTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/AWSEC2ComputeServiceLiveTest.java index 937339785f..b02b8e7dea 100644 --- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/AWSEC2ComputeServiceLiveTest.java +++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/AWSEC2ComputeServiceLiveTest.java @@ -59,13 +59,13 @@ public class AWSEC2ComputeServiceLiveTest extends EC2ComputeServiceLiveTest { @Test(enabled = true, dependsOnMethods = "testCompareSizes") public void testExtendedOptionsAndLogin() throws Exception { SecurityGroupClient securityGroupClient = EC2Client.class.cast(context.getProviderSpecificContext().getApi()) - .getSecurityGroupServices(); + .getSecurityGroupServices(); KeyPairClient keyPairClient = EC2Client.class.cast(context.getProviderSpecificContext().getApi()) - .getKeyPairServices(); + .getKeyPairServices(); InstanceClient instanceClient = EC2Client.class.cast(context.getProviderSpecificContext().getApi()) - .getInstanceServices(); + .getInstanceServices(); String group = this.group + "o"; @@ -117,17 +117,17 @@ public class AWSEC2ComputeServiceLiveTest extends EC2ComputeServiceLiveTest { // } // make sure we made our dummy group and also let in the user's group - assertEquals(Sets.newTreeSet(instance.getGroupIds()), ImmutableSortedSet. of("jclouds#" + group + "#" - + instance.getRegion(), group)); + assertEquals(Sets.newTreeSet(instance.getGroupIds()), + ImmutableSortedSet. of("jclouds#" + group + "#" + instance.getRegion(), group)); // make sure our dummy group has no rules SecurityGroup secgroup = Iterables.getOnlyElement(securityGroupClient.describeSecurityGroupsInRegion(null, - "jclouds#" +group + "#" + instance.getRegion())); + "jclouds#" + group + "#" + instance.getRegion())); assert secgroup.getIpPermissions().size() == 0 : secgroup; // try to run a script with the original keyPair - runScriptWithCreds(group, first.getOperatingSystem(), new Credentials(first.getCredentials().identity, result - .getKeyMaterial())); + runScriptWithCreds(group, first.getOperatingSystem(), + new Credentials(first.getCredentials().identity, result.getKeyMaterial())); } finally { client.destroyNodesMatching(NodePredicates.inGroup(group)); @@ -138,21 +138,17 @@ public class AWSEC2ComputeServiceLiveTest extends EC2ComputeServiceLiveTest { } cleanupExtendedStuff(securityGroupClient, keyPairClient, group); } - + } + @Test(enabled = true, dependsOnMethods = "testCompareSizes") - public void testExtendedOptionsWithSubnetId() throws Exception { + public void testSubnetId() throws Exception { String subnetId = System.getProperty("test.subnetId"); if (subnetId == null) { // Skip test and return return; } - SecurityGroupClient securityGroupClient = EC2Client.class.cast(context.getProviderSpecificContext().getApi()) - .getSecurityGroupServices(); - - KeyPairClient keyPairClient = EC2Client.class.cast(context.getProviderSpecificContext().getApi()) - .getKeyPairServices(); InstanceClient instanceClient = EC2Client.class.cast(context.getProviderSpecificContext().getApi()) .getInstanceServices(); @@ -161,20 +157,11 @@ public class AWSEC2ComputeServiceLiveTest extends EC2ComputeServiceLiveTest { TemplateOptions options = client.templateOptions(); - // options.as(AWSEC2TemplateOptions.class).securityGroups(group); - options.as(AWSEC2TemplateOptions.class).keyPair(group); options.as(AWSEC2TemplateOptions.class).subnetId(subnetId); String startedId = null; String nodeId = null; try { - cleanupExtendedStuff(securityGroupClient, keyPairClient, group); - - // create the security group - // securityGroupClient.createSecurityGroupInRegion(null, group, group); - - // create a keypair to pass in as well - keyPairClient.createKeyPairInRegion(null, group); Set nodes = client.createNodesInGroup(group, 1, options); @@ -192,11 +179,6 @@ public class AWSEC2ComputeServiceLiveTest extends EC2ComputeServiceLiveTest { } finally { if (nodeId != null) client.destroyNode(nodeId); - if (startedId != null) { - // ensure we didn't delete these resources! - assertEquals(keyPairClient.describeKeyPairsInRegion(null, group).size(), 1); - } - cleanupExtendedStuff(securityGroupClient, keyPairClient, group); } } diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/strategy/CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/strategy/CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsTest.java index 244f481b2a..13dfc6e750 100644 --- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/strategy/CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsTest.java +++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/strategy/CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsTest.java @@ -34,17 +34,17 @@ import javax.inject.Provider; import org.jclouds.aws.domain.Region; import org.jclouds.aws.ec2.compute.AWSEC2TemplateOptions; import org.jclouds.aws.ec2.domain.PlacementGroup; +import org.jclouds.aws.ec2.domain.RegionNameAndPublicKeyMaterial; import org.jclouds.aws.ec2.functions.CreatePlacementGroupIfNeeded; import org.jclouds.aws.ec2.options.AWSRunInstancesOptions; import org.jclouds.compute.domain.Hardware; import org.jclouds.compute.domain.Template; import org.jclouds.compute.options.TemplateOptions; +import org.jclouds.domain.Credentials; import org.jclouds.ec2.compute.EC2TemplateBuilderTest; import org.jclouds.ec2.compute.domain.EC2HardwareBuilder; import org.jclouds.ec2.compute.domain.RegionAndName; import org.jclouds.ec2.compute.domain.RegionNameAndIngressRules; -import org.jclouds.ec2.compute.functions.CreateSecurityGroupIfNeeded; -import org.jclouds.ec2.compute.functions.CreateUniqueKeyPair; import org.jclouds.ec2.compute.strategy.CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions; import org.jclouds.ec2.domain.BlockDeviceMapping; import org.jclouds.ec2.domain.KeyPair; @@ -52,6 +52,7 @@ import org.jclouds.ec2.options.RunInstancesOptions; import org.jclouds.encryption.internal.Base64; import org.testng.annotations.Test; +import com.google.common.base.Function; import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.ImmutableSet; @@ -72,7 +73,7 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT public void testExecuteWithDefaultOptionsEC2() throws SecurityException, NoSuchMethodException { // setup constants String region = Region.AP_SOUTHEAST_1; - String tag = "tag"; + String group = "group"; Hardware size = EC2HardwareBuilder.m1_small().build(); String systemGeneratedKeyPairName = "systemGeneratedKeyPair"; String generatedGroup = "group"; @@ -80,18 +81,18 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT // create mocks CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions strategy = createMock( - CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions.class, new Method[] { - CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.class - .getDeclaredMethod("getOptionsProvider"), - CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.class.getDeclaredMethod( - "createNewKeyPairUnlessUserSpecifiedOtherwise", String.class, String.class, - TemplateOptions.class), - CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions.class.getDeclaredMethod( - "createNewPlacementGroupUnlessUserSpecifiedOtherwise", String.class, String.class, - TemplateOptions.class), - CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.class - .getDeclaredMethod("getSecurityGroupsForTagAndOptions", String.class, String.class, - TemplateOptions.class) }); + CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions.class, + new Method[] { + CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.class + .getDeclaredMethod("getOptionsProvider"), + CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.class.getDeclaredMethod( + "createNewKeyPairUnlessUserSpecifiedOtherwise", String.class, String.class, + TemplateOptions.class), + CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions.class.getDeclaredMethod( + "createNewPlacementGroupUnlessUserSpecifiedOtherwise", String.class, String.class, + TemplateOptions.class), + CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.class.getDeclaredMethod( + "getSecurityGroupsForTagAndOptions", String.class, String.class, TemplateOptions.class) }); AWSEC2TemplateOptions options = createMock(AWSEC2TemplateOptions.class); Template template = createMock(Template.class); @@ -101,9 +102,9 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT expect(template.getHardware()).andReturn(size).atLeastOnce(); expect(template.getOptions()).andReturn(options).atLeastOnce(); expect(options.getBlockDeviceMappings()).andReturn(ImmutableSet. of()).atLeastOnce(); - expect(strategy.createNewKeyPairUnlessUserSpecifiedOtherwise(region, tag, options)).andReturn( - systemGeneratedKeyPairName); - expect(strategy.getSecurityGroupsForTagAndOptions(region, tag, options)).andReturn(generatedGroups); + expect(strategy.createNewKeyPairUnlessUserSpecifiedOtherwise(region, group, options)).andReturn( + systemGeneratedKeyPairName); + expect(strategy.getSecurityGroupsForTagAndOptions(region, group, options)).andReturn(generatedGroups); expect(options.getSubnetId()).andReturn(null); expect(options.getUserData()).andReturn(null); expect(options.isMonitoringEnabled()).andReturn(false); @@ -114,11 +115,12 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT replay(strategy); // run - RunInstancesOptions customize = strategy.execute(region, tag, template); + RunInstancesOptions customize = strategy.execute(region, group, template); assertEquals(customize.buildQueryParameters(), ImmutableMultimap. of()); - assertEquals(customize.buildFormParameters().entries(), ImmutableMultimap. of("InstanceType", - size.getProviderId(), "SecurityGroup.1", generatedGroup, "KeyName", systemGeneratedKeyPairName) - .entries()); + assertEquals( + customize.buildFormParameters().entries(), + ImmutableMultimap. of("InstanceType", size.getProviderId(), "SecurityGroup.1", + generatedGroup, "KeyName", systemGeneratedKeyPairName).entries()); assertEquals(customize.buildMatrixParameters(), ImmutableMultimap. of()); assertEquals(customize.buildRequestHeaders(), ImmutableMultimap. of()); assertEquals(customize.buildStringPayload(), null); @@ -132,7 +134,7 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT public void testExecuteForCCAutomatic() throws SecurityException, NoSuchMethodException { // setup constants String region = Region.US_EAST_1; - String tag = "tag"; + String group = "group"; Hardware size = EC2TemplateBuilderTest.CC1_4XLARGE; String systemGeneratedKeyPairName = "systemGeneratedKeyPair"; String generatedGroup = "group"; @@ -140,18 +142,18 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT // create mocks CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions strategy = createMock( - CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions.class, new Method[] { - CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.class - .getDeclaredMethod("getOptionsProvider"), - CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.class.getDeclaredMethod( - "createNewKeyPairUnlessUserSpecifiedOtherwise", String.class, String.class, - TemplateOptions.class), - CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions.class.getDeclaredMethod( - "createNewPlacementGroupUnlessUserSpecifiedOtherwise", String.class, String.class, - TemplateOptions.class), - CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.class - .getDeclaredMethod("getSecurityGroupsForTagAndOptions", String.class, String.class, - TemplateOptions.class) }); + CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions.class, + new Method[] { + CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.class + .getDeclaredMethod("getOptionsProvider"), + CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.class.getDeclaredMethod( + "createNewKeyPairUnlessUserSpecifiedOtherwise", String.class, String.class, + TemplateOptions.class), + CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions.class.getDeclaredMethod( + "createNewPlacementGroupUnlessUserSpecifiedOtherwise", String.class, String.class, + TemplateOptions.class), + CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.class.getDeclaredMethod( + "getSecurityGroupsForTagAndOptions", String.class, String.class, TemplateOptions.class) }); AWSEC2TemplateOptions options = createMock(AWSEC2TemplateOptions.class); Template template = createMock(Template.class); @@ -161,11 +163,11 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT expect(template.getHardware()).andReturn(size).atLeastOnce(); expect(template.getOptions()).andReturn(options).atLeastOnce(); expect(options.getBlockDeviceMappings()).andReturn(ImmutableSet. of()).atLeastOnce(); - expect(strategy.createNewKeyPairUnlessUserSpecifiedOtherwise(region, tag, options)).andReturn( - systemGeneratedKeyPairName); - expect(strategy.createNewPlacementGroupUnlessUserSpecifiedOtherwise(region, tag, options)).andReturn( - generatedGroup); - expect(strategy.getSecurityGroupsForTagAndOptions(region, tag, options)).andReturn(generatedGroups); + expect(strategy.createNewKeyPairUnlessUserSpecifiedOtherwise(region, group, options)).andReturn( + systemGeneratedKeyPairName); + expect(strategy.createNewPlacementGroupUnlessUserSpecifiedOtherwise(region, group, options)).andReturn( + generatedGroup); + expect(strategy.getSecurityGroupsForTagAndOptions(region, group, options)).andReturn(generatedGroups); expect(options.getSubnetId()).andReturn(null); expect(options.getUserData()).andReturn(null); expect(options.isMonitoringEnabled()).andReturn(false); @@ -176,11 +178,13 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT replay(strategy); // run - RunInstancesOptions customize = strategy.execute(region, tag, template); + RunInstancesOptions customize = strategy.execute(region, group, template); assertEquals(customize.buildQueryParameters(), ImmutableMultimap. of()); - assertEquals(customize.buildFormParameters().entries(), ImmutableMultimap. of("InstanceType", - size.getProviderId(), "SecurityGroup.1", generatedGroup, "KeyName", systemGeneratedKeyPairName, - "Placement.GroupName", generatedGroup).entries()); + assertEquals( + customize.buildFormParameters().entries(), + ImmutableMultimap. of("InstanceType", size.getProviderId(), "SecurityGroup.1", + generatedGroup, "KeyName", systemGeneratedKeyPairName, "Placement.GroupName", generatedGroup) + .entries()); assertEquals(customize.buildMatrixParameters(), ImmutableMultimap. of()); assertEquals(customize.buildRequestHeaders(), ImmutableMultimap. of()); assertEquals(customize.buildStringPayload(), null); @@ -194,7 +198,7 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT public void testExecuteForCCUserSpecified() throws SecurityException, NoSuchMethodException { // setup constants String region = Region.US_EAST_1; - String tag = "tag"; + String group = "group"; Hardware size = EC2TemplateBuilderTest.CC1_4XLARGE; String systemGeneratedKeyPairName = "systemGeneratedKeyPair"; String generatedGroup = "group"; @@ -202,18 +206,18 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT // create mocks CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions strategy = createMock( - CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions.class, new Method[] { - CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.class - .getDeclaredMethod("getOptionsProvider"), - CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.class.getDeclaredMethod( - "createNewKeyPairUnlessUserSpecifiedOtherwise", String.class, String.class, - TemplateOptions.class), - CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions.class.getDeclaredMethod( - "createNewPlacementGroupUnlessUserSpecifiedOtherwise", String.class, String.class, - TemplateOptions.class), - CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.class - .getDeclaredMethod("getSecurityGroupsForTagAndOptions", String.class, String.class, - TemplateOptions.class) }); + CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions.class, + new Method[] { + CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.class + .getDeclaredMethod("getOptionsProvider"), + CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.class.getDeclaredMethod( + "createNewKeyPairUnlessUserSpecifiedOtherwise", String.class, String.class, + TemplateOptions.class), + CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions.class.getDeclaredMethod( + "createNewPlacementGroupUnlessUserSpecifiedOtherwise", String.class, String.class, + TemplateOptions.class), + CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.class.getDeclaredMethod( + "getSecurityGroupsForTagAndOptions", String.class, String.class, TemplateOptions.class) }); AWSEC2TemplateOptions options = createMock(AWSEC2TemplateOptions.class); Template template = createMock(Template.class); @@ -223,11 +227,11 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT expect(template.getHardware()).andReturn(size).atLeastOnce(); expect(template.getOptions()).andReturn(options).atLeastOnce(); expect(options.getBlockDeviceMappings()).andReturn(ImmutableSet. of()).atLeastOnce(); - expect(strategy.createNewKeyPairUnlessUserSpecifiedOtherwise(region, tag, options)).andReturn( - systemGeneratedKeyPairName); - expect(strategy.createNewPlacementGroupUnlessUserSpecifiedOtherwise(region, tag, options)).andReturn( - generatedGroup); - expect(strategy.getSecurityGroupsForTagAndOptions(region, tag, options)).andReturn(generatedGroups); + expect(strategy.createNewKeyPairUnlessUserSpecifiedOtherwise(region, group, options)).andReturn( + systemGeneratedKeyPairName); + expect(strategy.createNewPlacementGroupUnlessUserSpecifiedOtherwise(region, group, options)).andReturn( + generatedGroup); + expect(strategy.getSecurityGroupsForTagAndOptions(region, group, options)).andReturn(generatedGroups); expect(options.getSubnetId()).andReturn(null); expect(options.getUserData()).andReturn(null); expect(options.isMonitoringEnabled()).andReturn(false); @@ -238,11 +242,13 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT replay(strategy); // run - RunInstancesOptions customize = strategy.execute(region, tag, template); + RunInstancesOptions customize = strategy.execute(region, group, template); assertEquals(customize.buildQueryParameters(), ImmutableMultimap. of()); - assertEquals(customize.buildFormParameters().entries(), ImmutableMultimap. of("InstanceType", - size.getProviderId(), "SecurityGroup.1", generatedGroup, "KeyName", systemGeneratedKeyPairName, - "Placement.GroupName", generatedGroup).entries()); + assertEquals( + customize.buildFormParameters().entries(), + ImmutableMultimap. of("InstanceType", size.getProviderId(), "SecurityGroup.1", + generatedGroup, "KeyName", systemGeneratedKeyPairName, "Placement.GroupName", generatedGroup) + .entries()); assertEquals(customize.buildMatrixParameters(), ImmutableMultimap. of()); assertEquals(customize.buildRequestHeaders(), ImmutableMultimap. of()); assertEquals(customize.buildStringPayload(), null); @@ -256,24 +262,24 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT public void testExecuteWithSubnet() throws SecurityException, NoSuchMethodException { // setup constants String region = Region.AP_SOUTHEAST_1; - String tag = "tag"; + String group = "group"; Hardware size = EC2HardwareBuilder.m1_small().build(); String systemGeneratedKeyPairName = "systemGeneratedKeyPair"; // create mocks CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions strategy = createMock( - CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions.class, new Method[] { - CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.class - .getDeclaredMethod("getOptionsProvider"), - CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.class.getDeclaredMethod( - "createNewKeyPairUnlessUserSpecifiedOtherwise", String.class, String.class, - TemplateOptions.class), - CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions.class.getDeclaredMethod( - "createNewPlacementGroupUnlessUserSpecifiedOtherwise", String.class, String.class, - TemplateOptions.class), - CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.class - .getDeclaredMethod("getSecurityGroupsForTagAndOptions", String.class, String.class, - TemplateOptions.class) }); + CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions.class, + new Method[] { + CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.class + .getDeclaredMethod("getOptionsProvider"), + CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.class.getDeclaredMethod( + "createNewKeyPairUnlessUserSpecifiedOtherwise", String.class, String.class, + TemplateOptions.class), + CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions.class.getDeclaredMethod( + "createNewPlacementGroupUnlessUserSpecifiedOtherwise", String.class, String.class, + TemplateOptions.class), + CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.class.getDeclaredMethod( + "getSecurityGroupsForTagAndOptions", String.class, String.class, TemplateOptions.class) }); AWSEC2TemplateOptions options = createMock(AWSEC2TemplateOptions.class); Template template = createMock(Template.class); @@ -283,8 +289,8 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT expect(template.getHardware()).andReturn(size).atLeastOnce(); expect(template.getOptions()).andReturn(options).atLeastOnce(); expect(options.getBlockDeviceMappings()).andReturn(ImmutableSet. of()).atLeastOnce(); - expect(strategy.createNewKeyPairUnlessUserSpecifiedOtherwise(region, tag, options)).andReturn( - systemGeneratedKeyPairName); + expect(strategy.createNewKeyPairUnlessUserSpecifiedOtherwise(region, group, options)).andReturn( + systemGeneratedKeyPairName); expect(options.getSubnetId()).andReturn("1"); expect(options.getUserData()).andReturn(null); expect(options.isMonitoringEnabled()).andReturn(false); @@ -295,10 +301,12 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT replay(strategy); // run - RunInstancesOptions customize = strategy.execute(region, tag, template); + RunInstancesOptions customize = strategy.execute(region, group, template); assertEquals(customize.buildQueryParameters(), ImmutableMultimap. of()); - assertEquals(customize.buildFormParameters().entries(), ImmutableMultimap. of("InstanceType", - size.getProviderId(), "SubnetId", "1", "KeyName", systemGeneratedKeyPairName).entries()); + assertEquals( + customize.buildFormParameters().entries(), + ImmutableMultimap. of("InstanceType", size.getProviderId(), "SubnetId", "1", "KeyName", + systemGeneratedKeyPairName).entries()); assertEquals(customize.buildMatrixParameters(), ImmutableMultimap. of()); assertEquals(customize.buildRequestHeaders(), ImmutableMultimap. of()); assertEquals(customize.buildStringPayload(), null); @@ -312,7 +320,7 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT public void testExecuteWithUserData() throws SecurityException, NoSuchMethodException { // setup constants String region = Region.AP_SOUTHEAST_1; - String tag = "tag"; + String group = "group"; Hardware size = EC2HardwareBuilder.m1_small().build(); String systemGeneratedKeyPairName = "systemGeneratedKeyPair"; String generatedGroup = "group"; @@ -320,18 +328,18 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT // create mocks CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions strategy = createMock( - CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions.class, new Method[] { - CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.class - .getDeclaredMethod("getOptionsProvider"), - CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.class.getDeclaredMethod( - "createNewKeyPairUnlessUserSpecifiedOtherwise", String.class, String.class, - TemplateOptions.class), - CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions.class.getDeclaredMethod( - "createNewPlacementGroupUnlessUserSpecifiedOtherwise", String.class, String.class, - TemplateOptions.class), - CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.class - .getDeclaredMethod("getSecurityGroupsForTagAndOptions", String.class, String.class, - TemplateOptions.class) }); + CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions.class, + new Method[] { + CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.class + .getDeclaredMethod("getOptionsProvider"), + CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.class.getDeclaredMethod( + "createNewKeyPairUnlessUserSpecifiedOtherwise", String.class, String.class, + TemplateOptions.class), + CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions.class.getDeclaredMethod( + "createNewPlacementGroupUnlessUserSpecifiedOtherwise", String.class, String.class, + TemplateOptions.class), + CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.class.getDeclaredMethod( + "getSecurityGroupsForTagAndOptions", String.class, String.class, TemplateOptions.class) }); AWSEC2TemplateOptions options = createMock(AWSEC2TemplateOptions.class); Template template = createMock(Template.class); @@ -341,9 +349,9 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT expect(template.getHardware()).andReturn(size).atLeastOnce(); expect(template.getOptions()).andReturn(options).atLeastOnce(); expect(options.getBlockDeviceMappings()).andReturn(ImmutableSet. of()).atLeastOnce(); - expect(strategy.createNewKeyPairUnlessUserSpecifiedOtherwise(region, tag, options)).andReturn( - systemGeneratedKeyPairName); - expect(strategy.getSecurityGroupsForTagAndOptions(region, tag, options)).andReturn(generatedGroups); + expect(strategy.createNewKeyPairUnlessUserSpecifiedOtherwise(region, group, options)).andReturn( + systemGeneratedKeyPairName); + expect(strategy.getSecurityGroupsForTagAndOptions(region, group, options)).andReturn(generatedGroups); expect(options.getSubnetId()).andReturn(null); expect(options.getUserData()).andReturn("hello".getBytes()); expect(options.isMonitoringEnabled()).andReturn(false); @@ -354,11 +362,12 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT replay(strategy); // run - RunInstancesOptions customize = strategy.execute(region, tag, template); + RunInstancesOptions customize = strategy.execute(region, group, template); assertEquals(customize.buildQueryParameters(), ImmutableMultimap. of()); - assertEquals(customize.buildFormParameters().entries(), ImmutableMultimap. of("InstanceType", - size.getProviderId(), "SecurityGroup.1", "group", "KeyName", systemGeneratedKeyPairName, "UserData", - Base64.encodeBytes("hello".getBytes())).entries()); + assertEquals( + customize.buildFormParameters().entries(), + ImmutableMultimap. of("InstanceType", size.getProviderId(), "SecurityGroup.1", "group", + "KeyName", systemGeneratedKeyPairName, "UserData", Base64.encodeBytes("hello".getBytes())).entries()); assertEquals(customize.buildMatrixParameters(), ImmutableMultimap. of()); assertEquals(customize.buildRequestHeaders(), ImmutableMultimap. of()); assertEquals(customize.buildStringPayload(), null); @@ -372,7 +381,7 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT public void testCreateNewKeyPairUnlessUserSpecifiedOtherwise_reusesKeyWhenToldTo() { // setup constants String region = Region.AP_SOUTHEAST_1; - String tag = "tag"; + String group = "group"; String userSuppliedKeyPair = "myKeyPair"; // create mocks @@ -389,7 +398,7 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT replayStrategy(strategy); // run - assertEquals(strategy.createNewKeyPairUnlessUserSpecifiedOtherwise(region, tag, options), userSuppliedKeyPair); + assertEquals(strategy.createNewKeyPairUnlessUserSpecifiedOtherwise(region, group, options), userSuppliedKeyPair); // verify mocks verify(options); @@ -397,10 +406,86 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT verifyStrategy(strategy); } - public void testCreateNewKeyPairUnlessUserSpecifiedOtherwise_createsNewKeyPairAndReturnsItsNameByDefault() { + public void testCreateNewKeyPairUnlessUserSpecifiedOtherwise_importsKeyPairAndUnsetsTemplateInstructionWhenPublicKeySuppliedAndAddsCredentialToMapWhenOverridingCredsAreSet() { // setup constants String region = Region.AP_SOUTHEAST_1; - String tag = "tag"; + String group = "group"; + String userSuppliedKeyPair = null; + boolean shouldAutomaticallyCreateKeyPair = true; + + // create mocks + CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions strategy = setupStrategy(); + AWSEC2TemplateOptions options = createMock(AWSEC2TemplateOptions.class); + KeyPair keyPair = new KeyPair(region, "jclouds#" + group, "fingerprint", null); + + // setup expectations + expect(options.getKeyPair()).andReturn(userSuppliedKeyPair); + expect(options.shouldAutomaticallyCreateKeyPair()).andReturn(shouldAutomaticallyCreateKeyPair); + expect(strategy.credentialsMap.get(new RegionAndName(region, "jclouds#" + group))).andReturn(null); + expect(options.getPublicKey()).andReturn("ssh-rsa").times(2); + expect(strategy.importExistingKeyPair.apply(new RegionNameAndPublicKeyMaterial(region, group, "ssh-rsa"))) + .andReturn(keyPair); + expect(options.dontAuthorizePublicKey()).andReturn(options); + expect(options.getOverridingCredentials()).andReturn(new Credentials("foo", "bar")).times(3); + expect(options.getRunScript()).andReturn(null); + expect(options.getPrivateKey()).andReturn(null); + expect( + strategy.credentialsMap.put(new RegionAndName(region, "jclouds#" + group), + keyPair.toBuilder().keyMaterial("bar").build())).andReturn(null); + + // replay mocks + replay(options); + replayStrategy(strategy); + + // run + assertEquals(strategy.createNewKeyPairUnlessUserSpecifiedOtherwise(region, group, options), "jclouds#" + group); + + // verify mocks + verify(options); + verifyStrategy(strategy); + } + + public void testCreateNewKeyPairUnlessUserSpecifiedOtherwise_importsKeyPairAndUnsetsTemplateInstructionWhenPublicKeySupplied() { + // setup constants + String region = Region.AP_SOUTHEAST_1; + String group = "group"; + String userSuppliedKeyPair = null; + boolean shouldAutomaticallyCreateKeyPair = true; + + // create mocks + CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions strategy = setupStrategy(); + AWSEC2TemplateOptions options = createMock(AWSEC2TemplateOptions.class); + KeyPair keyPair = new KeyPair(region, "jclouds#" + group, "fingerprint", null); + + // setup expectations + expect(options.getKeyPair()).andReturn(userSuppliedKeyPair); + expect(options.shouldAutomaticallyCreateKeyPair()).andReturn(shouldAutomaticallyCreateKeyPair); + expect(strategy.credentialsMap.get(new RegionAndName(region, "jclouds#" + group))).andReturn(null); + expect(options.getPublicKey()).andReturn("ssh-rsa").times(2); + expect(strategy.importExistingKeyPair.apply(new RegionNameAndPublicKeyMaterial(region, group, "ssh-rsa"))) + .andReturn(keyPair); + expect(options.dontAuthorizePublicKey()).andReturn(options); + expect(options.getOverridingCredentials()).andReturn(null); + expect(options.getRunScript()).andReturn(null); + expect(options.getPrivateKey()).andReturn(null); + expect(strategy.credentialsMap.put(new RegionAndName(region, "jclouds#" + group), keyPair)).andReturn(null); + + // replay mocks + replay(options); + replayStrategy(strategy); + + // run + assertEquals(strategy.createNewKeyPairUnlessUserSpecifiedOtherwise(region, group, options), "jclouds#" + group); + + // verify mocks + verify(options); + verifyStrategy(strategy); + } + + public void testCreateNewKeyPairUnlessUserSpecifiedOtherwise_createsNewKeyPairAndReturnsItsNameWhenNoPublicKeySupplied() { + // setup constants + String region = Region.AP_SOUTHEAST_1; + String group = "group"; String userSuppliedKeyPair = null; boolean shouldAutomaticallyCreateKeyPair = true; String systemGeneratedKeyPairName = "systemGeneratedKeyPair"; @@ -413,10 +498,12 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT // setup expectations expect(options.getKeyPair()).andReturn(userSuppliedKeyPair); expect(options.shouldAutomaticallyCreateKeyPair()).andReturn(shouldAutomaticallyCreateKeyPair); - expect(strategy.createUniqueKeyPair.apply(new RegionAndName(region, tag))).andReturn(keyPair); + expect(strategy.credentialsMap.get(new RegionAndName(region, "jclouds#" + group))).andReturn(null); + expect(options.getPublicKey()).andReturn(null).times(2); + expect(strategy.createUniqueKeyPair.apply(new RegionAndName(region, group))).andReturn(keyPair); expect(keyPair.getKeyName()).andReturn(systemGeneratedKeyPairName).atLeastOnce(); expect(strategy.credentialsMap.put(new RegionAndName(region, systemGeneratedKeyPairName), keyPair)).andReturn( - null); + null); // replay mocks replay(options); @@ -424,8 +511,41 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT replayStrategy(strategy); // run - assertEquals(strategy.createNewKeyPairUnlessUserSpecifiedOtherwise(region, tag, options), - systemGeneratedKeyPairName); + assertEquals(strategy.createNewKeyPairUnlessUserSpecifiedOtherwise(region, group, options), + systemGeneratedKeyPairName); + + // verify mocks + verify(options); + verify(keyPair); + verifyStrategy(strategy); + } + + public void testCreateNewKeyPairUnlessUserSpecifiedOtherwise_returnsExistingKeyIfAlreadyPresent() { + // setup constants + String region = Region.AP_SOUTHEAST_1; + String group = "group"; + String userSuppliedKeyPair = null; + boolean shouldAutomaticallyCreateKeyPair = true; + String systemGeneratedKeyPairName = "systemGeneratedKeyPair"; + + // create mocks + CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions strategy = setupStrategy(); + AWSEC2TemplateOptions options = createMock(AWSEC2TemplateOptions.class); + KeyPair keyPair = createMock(KeyPair.class); + + // setup expectations + expect(options.getKeyPair()).andReturn(userSuppliedKeyPair); + expect(options.shouldAutomaticallyCreateKeyPair()).andReturn(shouldAutomaticallyCreateKeyPair); + expect(strategy.credentialsMap.get(new RegionAndName(region, "jclouds#" + group))).andReturn(keyPair); + expect(keyPair.getKeyName()).andReturn(systemGeneratedKeyPairName).atLeastOnce(); + // replay mocks + replay(options); + replay(keyPair); + replayStrategy(strategy); + + // run + assertEquals(strategy.createNewKeyPairUnlessUserSpecifiedOtherwise(region, group, options), + systemGeneratedKeyPairName); // verify mocks verify(options); @@ -436,7 +556,7 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT public void testCreateNewKeyPairUnlessUserSpecifiedOtherwise_doesntCreateAKeyPairAndReturnsNullWhenToldNotTo() { // setup constants String region = Region.AP_SOUTHEAST_1; - String tag = "tag"; + String group = "group"; String userSuppliedKeyPair = null; boolean shouldAutomaticallyCreateKeyPair = false; // here's the important // part! @@ -456,7 +576,7 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT replayStrategy(strategy); // run - assertEquals(strategy.createNewKeyPairUnlessUserSpecifiedOtherwise(region, tag, options), null); + assertEquals(strategy.createNewKeyPairUnlessUserSpecifiedOtherwise(region, group, options), null); // verify mocks verify(options); @@ -467,8 +587,8 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT public void testGetSecurityGroupsForTagAndOptions_createsNewGroupByDefaultWhenNoPortsAreSpecifiedWhenDoesntExist() { // setup constants String region = Region.AP_SOUTHEAST_1; - String tag = "tag"; - String generatedMarkerGroup = "jclouds#tag#" + Region.AP_SOUTHEAST_1; + String group = "group"; + String generatedMarkerGroup = "jclouds#group#" + Region.AP_SOUTHEAST_1; Set groupIds = ImmutableSet. of(); int[] ports = new int[] {}; boolean shouldAuthorizeSelf = true; @@ -483,7 +603,7 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT expect(options.getGroupIds()).andReturn(groupIds).atLeastOnce(); expect(options.getInboundPorts()).andReturn(ports).atLeastOnce(); RegionNameAndIngressRules regionNameAndIngressRules = new RegionNameAndIngressRules(region, generatedMarkerGroup, - ports, shouldAuthorizeSelf); + ports, shouldAuthorizeSelf); expect(strategy.securityGroupMap.containsKey(regionNameAndIngressRules)).andReturn(groupExisted); expect(strategy.createSecurityGroupIfNeeded.apply(regionNameAndIngressRules)).andReturn(generatedMarkerGroup); expect(strategy.securityGroupMap.put(regionNameAndIngressRules, generatedMarkerGroup)).andReturn(null); @@ -493,7 +613,7 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT replayStrategy(strategy); // run - assertEquals(strategy.getSecurityGroupsForTagAndOptions(region, tag, options), returnVal); + assertEquals(strategy.getSecurityGroupsForTagAndOptions(region, group, options), returnVal); // verify mocks verify(options); @@ -503,8 +623,8 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT public void testGetSecurityGroupsForTagAndOptions_createsNewGroupByDefaultWhenPortsAreSpecifiedWhenDoesntExist() { // setup constants String region = Region.AP_SOUTHEAST_1; - String tag = "tag"; - String generatedMarkerGroup = "jclouds#tag#" + Region.AP_SOUTHEAST_1; + String group = "group"; + String generatedMarkerGroup = "jclouds#group#" + Region.AP_SOUTHEAST_1; Set groupIds = ImmutableSet. of(); int[] ports = new int[] { 22, 80 }; boolean shouldAuthorizeSelf = true; @@ -519,7 +639,7 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT expect(options.getGroupIds()).andReturn(groupIds).atLeastOnce(); expect(options.getInboundPorts()).andReturn(ports).atLeastOnce(); RegionNameAndIngressRules regionNameAndIngressRules = new RegionNameAndIngressRules(region, generatedMarkerGroup, - ports, shouldAuthorizeSelf); + ports, shouldAuthorizeSelf); expect(strategy.securityGroupMap.containsKey(regionNameAndIngressRules)).andReturn(groupExisted); expect(strategy.createSecurityGroupIfNeeded.apply(regionNameAndIngressRules)).andReturn(generatedMarkerGroup); expect(strategy.securityGroupMap.put(regionNameAndIngressRules, generatedMarkerGroup)).andReturn(null); @@ -529,7 +649,7 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT replayStrategy(strategy); // run - assertEquals(strategy.getSecurityGroupsForTagAndOptions(region, tag, options), returnVal); + assertEquals(strategy.getSecurityGroupsForTagAndOptions(region, group, options), returnVal); // verify mocks verify(options); @@ -539,8 +659,8 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT public void testGetSecurityGroupsForTagAndOptions_reusesGroupByDefaultWhenNoPortsAreSpecifiedWhenDoesExist() { // setup constants String region = Region.AP_SOUTHEAST_1; - String tag = "tag"; - String generatedMarkerGroup = "jclouds#tag#" + Region.AP_SOUTHEAST_1; + String group = "group"; + String generatedMarkerGroup = "jclouds#group#" + Region.AP_SOUTHEAST_1; Set groupIds = ImmutableSet. of(); int[] ports = new int[] {}; boolean shouldAuthorizeSelf = true; @@ -555,7 +675,7 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT expect(options.getGroupIds()).andReturn(groupIds).atLeastOnce(); expect(options.getInboundPorts()).andReturn(ports).atLeastOnce(); RegionNameAndIngressRules regionNameAndIngressRules = new RegionNameAndIngressRules(region, generatedMarkerGroup, - ports, shouldAuthorizeSelf); + ports, shouldAuthorizeSelf); expect(strategy.securityGroupMap.containsKey(regionNameAndIngressRules)).andReturn(groupExisted); // replay mocks @@ -563,7 +683,7 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT replayStrategy(strategy); // run - assertEquals(strategy.getSecurityGroupsForTagAndOptions(region, tag, options), returnVal); + assertEquals(strategy.getSecurityGroupsForTagAndOptions(region, group, options), returnVal); // verify mocks verify(options); @@ -573,8 +693,8 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT public void testGetSecurityGroupsForTagAndOptions_reusesGroupByDefaultWhenNoPortsAreSpecifiedWhenDoesExistAndAcceptsUserSuppliedGroups() { // setup constants String region = Region.AP_SOUTHEAST_1; - String tag = "tag"; - String generatedMarkerGroup = "jclouds#tag#" + Region.AP_SOUTHEAST_1; + String group = "group"; + String generatedMarkerGroup = "jclouds#group#" + Region.AP_SOUTHEAST_1; Set groupIds = ImmutableSet. of("group1", "group2"); int[] ports = new int[] {}; boolean shouldAuthorizeSelf = true; @@ -588,7 +708,7 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT // setup expectations expect(options.getGroupIds()).andReturn(groupIds).atLeastOnce(); RegionNameAndIngressRules regionNameAndIngressRules = new RegionNameAndIngressRules(region, generatedMarkerGroup, - ports, shouldAuthorizeSelf); // note + ports, shouldAuthorizeSelf); // note // this // works // since @@ -601,7 +721,7 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT replayStrategy(strategy); // run - assertEquals(strategy.getSecurityGroupsForTagAndOptions(region, tag, options), returnVal); + assertEquals(strategy.getSecurityGroupsForTagAndOptions(region, group, options), returnVal); // verify mocks verify(options); @@ -611,7 +731,7 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT public void testCreateNewPlacementGroupUnlessUserSpecifiedOtherwise_reusesKeyWhenToldTo() { // setup constants String region = Region.AP_SOUTHEAST_1; - String tag = "tag"; + String group = "group"; String userSuppliedPlacementGroup = "myPlacementGroup"; // create mocks @@ -628,8 +748,8 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT replayStrategy(strategy); // run - assertEquals(strategy.createNewPlacementGroupUnlessUserSpecifiedOtherwise(region, tag, options), - userSuppliedPlacementGroup); + assertEquals(strategy.createNewPlacementGroupUnlessUserSpecifiedOtherwise(region, group, options), + userSuppliedPlacementGroup); // verify mocks verify(options); @@ -640,10 +760,10 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT public void testCreateNewPlacementGroupUnlessUserSpecifiedOtherwise_createsNewPlacementGroupAndReturnsItsNameByDefault() { // setup constants String region = Region.AP_SOUTHEAST_1; - String tag = "tag"; + String group = "group"; String userSuppliedPlacementGroup = null; boolean shouldAutomaticallyCreatePlacementGroup = true; - String generatedMarkerGroup = "jclouds#tag#" + Region.AP_SOUTHEAST_1; + String generatedMarkerGroup = "jclouds#group#" + Region.AP_SOUTHEAST_1; // create mocks CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions strategy = setupStrategy(); @@ -654,17 +774,17 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT expect(options.shouldAutomaticallyCreatePlacementGroup()).andReturn(shouldAutomaticallyCreatePlacementGroup); expect(strategy.placementGroupMap.containsKey(new RegionAndName(region, generatedMarkerGroup))).andReturn(false); expect(strategy.createPlacementGroupIfNeeded.apply(new RegionAndName(region, generatedMarkerGroup))).andReturn( - generatedMarkerGroup); + generatedMarkerGroup); expect(strategy.placementGroupMap.put(new RegionAndName(region, generatedMarkerGroup), generatedMarkerGroup)) - .andReturn(null); + .andReturn(null); // replay mocks replay(options); replayStrategy(strategy); // run - assertEquals(strategy.createNewPlacementGroupUnlessUserSpecifiedOtherwise(region, tag, options), - generatedMarkerGroup); + assertEquals(strategy.createNewPlacementGroupUnlessUserSpecifiedOtherwise(region, group, options), + generatedMarkerGroup); // verify mocks verify(options); @@ -674,7 +794,7 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT public void testCreateNewPlacementGroupUnlessUserSpecifiedOtherwise_doesntCreateAPlacementGroupAndReturnsNullWhenToldNotTo() { // setup constants String region = Region.AP_SOUTHEAST_1; - String tag = "tag"; + String group = "group"; String userSuppliedPlacementGroup = null; boolean shouldAutomaticallyCreatePlacementGroup = false; // here's the important // part! @@ -694,7 +814,7 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT replayStrategy(strategy); // run - assertEquals(strategy.createNewPlacementGroupUnlessUserSpecifiedOtherwise(region, tag, options), null); + assertEquals(strategy.createNewPlacementGroupUnlessUserSpecifiedOtherwise(region, group, options), null); // verify mocks verify(options); @@ -707,6 +827,7 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT verify(strategy.securityGroupMap); verify(strategy.placementGroupMap); verify(strategy.createUniqueKeyPair); + verify(strategy.importExistingKeyPair); verify(strategy.createSecurityGroupIfNeeded); verify(strategy.createPlacementGroupIfNeeded); } @@ -716,13 +837,14 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT Map credentialsMap = createMock(Map.class); Map securityGroupMap = createMock(Map.class); Map placementGroupMap = createMock(Map.class); - CreateUniqueKeyPair createUniqueKeyPair = createMock(CreateUniqueKeyPair.class); - CreateSecurityGroupIfNeeded createSecurityGroupIfNeeded = createMock(CreateSecurityGroupIfNeeded.class); + Function createOrGetKeyPair = createMock(Function.class); + Function createSecurityGroupIfNeeded = createMock(Function.class); + Function importExistingKeyPair = createMock(Function.class); CreatePlacementGroupIfNeeded createPlacementGroupIfNeeded = createMock(CreatePlacementGroupIfNeeded.class); return new CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions(credentialsMap, securityGroupMap, - placementGroupMap, createUniqueKeyPair, createSecurityGroupIfNeeded, OPTIONS_PROVIDER, - createPlacementGroupIfNeeded); + placementGroupMap, createOrGetKeyPair, createSecurityGroupIfNeeded, OPTIONS_PROVIDER, + createPlacementGroupIfNeeded, importExistingKeyPair); } private void replayStrategy(CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions strategy) { @@ -730,6 +852,7 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT replay(strategy.securityGroupMap); replay(strategy.placementGroupMap); replay(strategy.createUniqueKeyPair); + replay(strategy.importExistingKeyPair); replay(strategy.createSecurityGroupIfNeeded); replay(strategy.createPlacementGroupIfNeeded); } diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/functions/ImportOrReturnExistingKeypairTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/functions/ImportOrReturnExistingKeypairTest.java new file mode 100644 index 0000000000..03918ee181 --- /dev/null +++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/functions/ImportOrReturnExistingKeypairTest.java @@ -0,0 +1,116 @@ +/** + * + * Copyright (C) 2010 Cloud Conscious, LLC. + * + * ==================================================================== + * Licensed 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.functions; + +import static org.easymock.EasyMock.expect; +import static org.easymock.classextension.EasyMock.createMock; +import static org.easymock.classextension.EasyMock.replay; +import static org.easymock.classextension.EasyMock.verify; +import static org.testng.Assert.assertEquals; + +import java.net.UnknownHostException; + +import org.jclouds.aws.ec2.AWSEC2Client; +import org.jclouds.aws.ec2.services.AWSKeyPairClient; +import org.jclouds.ec2.domain.KeyPair; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableSet; + +/** + * @author Adrian Cole + */ +@Test(groups = "unit") +public class ImportOrReturnExistingKeypairTest { + @Test + public void testApply() throws UnknownHostException { + AWSEC2Client client = createMock(AWSEC2Client.class); + AWSKeyPairClient keyClient = createMock(AWSKeyPairClient.class); + + KeyPair pair = createMock(KeyPair.class); + + expect(client.getKeyPairServices()).andReturn(keyClient).atLeastOnce(); + + expect(keyClient.importKeyPairInRegion("region", "jclouds#group", "ssh-rsa")).andReturn(pair); + + replay(client); + replay(keyClient); + + ImportOrReturnExistingKeypair parser = new ImportOrReturnExistingKeypair(client); + + assertEquals(parser.importOrReturnExistingKeypair("region", "group", "ssh-rsa"), pair); + + verify(client); + verify(keyClient); + } + + @Test + public void testApplyWithIllegalStateExceptionReturnsExistingKey() throws UnknownHostException { + AWSEC2Client client = createMock(AWSEC2Client.class); + AWSKeyPairClient keyClient = createMock(AWSKeyPairClient.class); + + KeyPair pair = createMock(KeyPair.class); + + expect(client.getKeyPairServices()).andReturn(keyClient).atLeastOnce(); + + expect(keyClient.importKeyPairInRegion("region", "jclouds#group", "ssh-rsa")).andThrow( + new IllegalStateException()); + expect(keyClient.describeKeyPairsInRegion("region", "jclouds#group")).andReturn(ImmutableSet.of(pair)); + + replay(client); + replay(keyClient); + + ImportOrReturnExistingKeypair parser = new ImportOrReturnExistingKeypair(client); + + assertEquals(parser.importOrReturnExistingKeypair("region", "group", "ssh-rsa"), pair); + + verify(client); + verify(keyClient); + + } + + @Test + public void testApplyWithIllegalStateExceptionRetriesWhenExistingKeyNotFound() throws UnknownHostException { + AWSEC2Client client = createMock(AWSEC2Client.class); + AWSKeyPairClient keyClient = createMock(AWSKeyPairClient.class); + + KeyPair pair = createMock(KeyPair.class); + + expect(client.getKeyPairServices()).andReturn(keyClient).atLeastOnce(); + + expect(keyClient.importKeyPairInRegion("region", "jclouds#group", "ssh-rsa")).andThrow( + new IllegalStateException()); + expect(keyClient.describeKeyPairsInRegion("region", "jclouds#group")).andReturn(ImmutableSet. of()); + expect(keyClient.importKeyPairInRegion("region", "jclouds#group", "ssh-rsa")).andThrow( + new IllegalStateException()); + expect(keyClient.describeKeyPairsInRegion("region", "jclouds#group")).andReturn(ImmutableSet. of(pair)); + + replay(client); + replay(keyClient); + + ImportOrReturnExistingKeypair parser = new ImportOrReturnExistingKeypair(client); + + assertEquals(parser.importOrReturnExistingKeypair("region", "group", "ssh-rsa"), pair); + + verify(client); + verify(keyClient); + + } +} diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSKeyPairClientLiveTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSKeyPairClientLiveTest.java index aabfe33cff..5e95f7c655 100644 --- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSKeyPairClientLiveTest.java +++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSKeyPairClientLiveTest.java @@ -20,6 +20,13 @@ package org.jclouds.aws.ec2.services; import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.Iterables.get; +import static com.google.common.collect.Iterables.getOnlyElement; +import static com.google.common.collect.Sets.newTreeSet; +import static org.jclouds.compute.options.TemplateOptions.Builder.overrideCredentialsWith; +import static org.jclouds.compute.predicates.NodePredicates.inGroup; +import static org.jclouds.compute.predicates.NodePredicates.runningInGroup; +import static org.jclouds.scriptbuilder.domain.Statements.exec; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNotNull; @@ -34,17 +41,26 @@ import org.jclouds.Constants; import org.jclouds.aws.domain.Region; import org.jclouds.aws.ec2.AWSEC2AsyncClient; import org.jclouds.aws.ec2.AWSEC2Client; +import org.jclouds.aws.ec2.domain.AWSRunningInstance; +import org.jclouds.compute.ComputeServiceContext; import org.jclouds.compute.ComputeServiceContextFactory; import org.jclouds.compute.ComputeTestUtils; +import org.jclouds.compute.domain.ExecResponse; +import org.jclouds.compute.domain.NodeMetadata; +import org.jclouds.compute.options.TemplateOptions; +import org.jclouds.domain.Credentials; +import org.jclouds.ec2.EC2Client; import org.jclouds.ec2.domain.KeyPair; +import org.jclouds.ec2.domain.RunningInstance; +import org.jclouds.ec2.services.InstanceClient; import org.jclouds.logging.log4j.config.Log4JLoggingModule; import org.jclouds.rest.RestContext; +import org.jclouds.ssh.jsch.config.JschSshClientModule; import org.testng.annotations.AfterTest; import org.testng.annotations.BeforeGroups; import org.testng.annotations.Test; import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Sets; import com.google.inject.Module; /** @@ -63,6 +79,7 @@ public class AWSKeyPairClientLiveTest { protected String credential; protected String endpoint; protected String apiversion; + private ComputeServiceContext computeContext; protected void setupCredentials() { identity = checkNotNull(System.getProperty("test." + provider + ".identity"), "test." + provider + ".identity"); @@ -89,20 +106,67 @@ public class AWSKeyPairClientLiveTest { public void setupClient() { setupCredentials(); Properties overrides = setupProperties(); - context = new ComputeServiceContextFactory().createContext(provider, - ImmutableSet. of(new Log4JLoggingModule()), overrides).getProviderSpecificContext(); + computeContext = new ComputeServiceContextFactory().createContext(provider, + ImmutableSet. of(new Log4JLoggingModule(), new JschSshClientModule()), overrides); + context = computeContext.getProviderSpecificContext(); client = context.getApi().getKeyPairServices(); } + public void testNoSsh() throws Exception { + + Map keyPair = ComputeTestUtils.setupKeyPair(); + + InstanceClient instanceClient = EC2Client.class.cast(context.getApi()).getInstanceServices(); + + String group = PREFIX + "unssh"; + computeContext.getComputeService().destroyNodesMatching(inGroup(group)); + + TemplateOptions options = computeContext.getComputeService().templateOptions(); + + options.authorizePublicKey(keyPair.get("public")); + + ComputeServiceContext noSshContext = null; + try { + noSshContext = new ComputeServiceContextFactory().createContext(provider, + ImmutableSet.of(new Log4JLoggingModule()), setupProperties()); + + Set nodes = noSshContext.getComputeService().createNodesInGroup(group, 1, options); + + NodeMetadata first = get(nodes, 0); + assert first.getCredentials() != null : first; + assert first.getCredentials().identity != null : first; + + AWSRunningInstance instance = AWSRunningInstance.class + .cast(getInstance(instanceClient, first.getProviderId())); + + assertEquals(instance.getKeyName(), "jclouds#" + group); + assertEquals(first.getCredentials().credential, null); + + Map responses = computeContext.getComputeService() + .runScriptOnNodesMatching( + runningInGroup(group), + exec("echo hello"), + overrideCredentialsWith(new Credentials(first.getCredentials().identity, keyPair.get("private"))) + .wrapInInitScript(false).runAsRoot(false)); + + ExecResponse hello = getOnlyElement(responses.values()); + assertEquals(hello.getOutput().trim(), "hello"); + + } finally { + noSshContext.close(); + computeContext.getComputeService().destroyNodesMatching(inGroup(group)); + } + } + @Test void testDescribeAWSKeyPairs() { for (String region : Region.DEFAULT_REGIONS) { - SortedSet allResults = Sets.newTreeSet(client.describeKeyPairsInRegion(region)); + SortedSet allResults = newTreeSet(client.describeKeyPairsInRegion(region)); assertNotNull(allResults); if (allResults.size() >= 1) { KeyPair pair = allResults.last(); - SortedSet result = Sets.newTreeSet(client.describeKeyPairsInRegion(region, pair.getKeyName())); + SortedSet result = newTreeSet(client.describeKeyPairsInRegion(region, pair.getKeyName())); assertNotNull(result); KeyPair compare = result.last(); assertEquals(compare, pair); @@ -163,7 +227,7 @@ public class AWSKeyPairClientLiveTest { assertNotNull(keyPair.getKeyFingerprint()); assertEquals(keyPair.getKeyName(), keyName); - Set twoResults = Sets.newLinkedHashSet(client.describeKeyPairsInRegion(null, keyName)); + Set twoResults = client.describeKeyPairsInRegion(null, keyName); assertNotNull(twoResults); assertEquals(twoResults.size(), 1); KeyPair listPair = twoResults.iterator().next(); @@ -171,6 +235,11 @@ public class AWSKeyPairClientLiveTest { assertEquals(listPair.getKeyFingerprint(), keyPair.getKeyFingerprint()); } + protected RunningInstance getInstance(InstanceClient instanceClient, String id) { + RunningInstance instance = getOnlyElement(getOnlyElement(instanceClient.describeInstancesInRegion(null, id))); + return instance; + } + @AfterTest public void shutdown() { context.close(); diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/PlacementGroupClientLiveTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/PlacementGroupClientLiveTest.java index 077e0e2fd6..078e3da3eb 100644 --- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/PlacementGroupClientLiveTest.java +++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/PlacementGroupClientLiveTest.java @@ -197,8 +197,8 @@ public class PlacementGroupClientLiveTest { assertEquals(template.getHardware().getProviderId(), InstanceType.CC1_4XLARGE); assertEquals(template.getImage().getId(), "us-east-1/ami-7ea24a17"); - template.getOptions().installPrivateKey(keyPair.get("private")).authorizePublicKey(keyPair.get("public")) - .runScript(buildScript(template.getImage().getOperatingSystem())); + template.getOptions().overrideLoginCredentialWith(keyPair.get("private")) + .authorizePublicKey(keyPair.get("public")).runScript(buildScript(template.getImage().getOperatingSystem())); String group = PREFIX + "cccluster"; context.getComputeService().destroyNodesMatching(NodePredicates.inGroup(group));