From b0c4b9be9ade39c40cc4d04c1f5a03b5be35c1aa Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Tue, 8 Mar 2011 13:48:04 -0800 Subject: [PATCH] Issue 308: added compute service hooks for spot instances --- .../EC2ComputeServiceDependenciesModule.java | 21 +--- .../RunningInstanceToNodeMetadata.java | 42 ++++--- .../predicates/InstancePresent.java | 24 ++-- .../EC2CreateNodesInGroupThenAddToSet.java | 81 +++++++------ .../strategy/EC2DestroyNodeStrategy.java | 18 +-- .../strategy/EC2GetNodeMetadataStrategy.java | 17 ++- .../strategy/EC2ListNodesStrategy.java | 57 +++++---- .../ec2/compute/util/EC2ComputeUtils.java | 10 -- .../jclouds/ec2/domain/RunningInstance.java | 7 +- ...C2CreateNodesInGroupThenAddToSetTest.java} | 13 ++- .../NodePresentAndInIntendedState.java | 2 + .../ec2/compute/AWSEC2TemplateOptions.java | 84 ++++++++++++-- .../AWSEC2ComputeServiceContextModule.java | 18 +++ .../AWSRunningInstanceToNodeMetadata.java | 69 +++++++++++ .../predicates/AWSEC2InstancePresent.java | 56 +++++++++ .../AWSEC2CreateNodesInGroupThenAddToSet.java | 109 ++++++++++++++++++ .../strategy/AWSEC2DestroyNodeStrategy.java | 35 ++++++ .../AWSEC2GetNodeMetadataStrategy.java | 68 +++++++++++ .../strategy/AWSEC2ListNodesStrategy.java | 85 ++++++++++++++ .../aws/ec2/domain/AWSRunningInstance.java | 3 + ...otInstanceRequestToAWSRunningInstance.java | 64 ++++++++++ .../ec2/options/AWSRunInstancesOptions.java | 34 ++++-- .../compute/AWSEC2ComputeServiceLiveTest.java | 21 ++-- ...stanceRequestToAWSRunningInstanceTest.java | 74 ++++++++++++ .../services/AWSKeyPairClientLiveTest.java | 38 +++--- 25 files changed, 859 insertions(+), 191 deletions(-) rename apis/ec2/src/main/java/org/jclouds/ec2/{ => compute}/predicates/InstancePresent.java (69%) rename apis/ec2/src/test/java/org/jclouds/ec2/compute/strategy/{EC2RunNodesAndAddToSetStrategyTest.java => EC2CreateNodesInGroupThenAddToSetTest.java} (95%) create mode 100644 providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/functions/AWSRunningInstanceToNodeMetadata.java create mode 100644 providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/predicates/AWSEC2InstancePresent.java create mode 100644 providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/strategy/AWSEC2CreateNodesInGroupThenAddToSet.java create mode 100644 providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/strategy/AWSEC2DestroyNodeStrategy.java create mode 100644 providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/strategy/AWSEC2GetNodeMetadataStrategy.java create mode 100644 providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/strategy/AWSEC2ListNodesStrategy.java create mode 100644 providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/functions/SpotInstanceRequestToAWSRunningInstance.java create mode 100644 providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/functions/SpotInstanceRequestToAWSRunningInstanceTest.java 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 afa6a5653f..118e5645c3 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 @@ -25,7 +25,6 @@ import static org.jclouds.ec2.reference.EC2Constants.PROPERTY_EC2_AMI_OWNERS; import java.security.SecureRandom; import java.util.Map; -import java.util.concurrent.TimeUnit; import javax.inject.Named; import javax.inject.Singleton; @@ -54,13 +53,10 @@ import org.jclouds.ec2.compute.options.EC2TemplateOptions; import org.jclouds.ec2.domain.InstanceState; import org.jclouds.ec2.domain.KeyPair; import org.jclouds.ec2.domain.RunningInstance; -import org.jclouds.ec2.predicates.InstancePresent; -import org.jclouds.predicates.RetryablePredicate; import org.jclouds.rest.RestContext; import org.jclouds.rest.internal.RestContextImpl; import com.google.common.base.Function; -import com.google.common.base.Predicate; import com.google.common.base.Splitter; import com.google.common.base.Supplier; import com.google.common.collect.ImmutableMap; @@ -77,11 +73,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 @@ -89,13 +85,6 @@ public class EC2ComputeServiceDependenciesModule extends AbstractModule { return instanceToNodeState; } - @Provides - @Singleton - @Named("PRESENT") - protected Predicate instancePresent(InstancePresent present) { - return new RetryablePredicate(present, 5000, 200, TimeUnit.MILLISECONDS); - } - @Override protected void configure() { bind(TemplateBuilder.class).to(EC2TemplateBuilderImpl.class); diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/RunningInstanceToNodeMetadata.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/RunningInstanceToNodeMetadata.java index 5944293b8f..b2d1200212 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/RunningInstanceToNodeMetadata.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/RunningInstanceToNodeMetadata.java @@ -73,7 +73,7 @@ public class RunningInstanceToNodeMetadata implements Function instanceToNodeState; @Inject - RunningInstanceToNodeMetadata(Map instanceToNodeState, + protected RunningInstanceToNodeMetadata(Map instanceToNodeState, Map credentialStore, Map instanceToImage, @Memoized Supplier> locations, @Memoized Supplier> hardware) { this.locations = checkNotNull(locations, "locations"); @@ -85,13 +85,14 @@ public class RunningInstanceToNodeMetadata implements Function() { diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/predicates/InstancePresent.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/predicates/InstancePresent.java similarity index 69% rename from apis/ec2/src/main/java/org/jclouds/ec2/predicates/InstancePresent.java rename to apis/ec2/src/main/java/org/jclouds/ec2/compute/predicates/InstancePresent.java index 03d1fc8cff..7ae0b0ed12 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/predicates/InstancePresent.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/predicates/InstancePresent.java @@ -17,7 +17,9 @@ * ==================================================================== */ -package org.jclouds.ec2.predicates; +package org.jclouds.ec2.compute.predicates; + +import static com.google.common.base.Preconditions.checkNotNull; import java.util.NoSuchElementException; @@ -25,7 +27,7 @@ import javax.annotation.Resource; import javax.inject.Singleton; import org.jclouds.ec2.EC2Client; -import org.jclouds.ec2.domain.RunningInstance; +import org.jclouds.ec2.compute.domain.RegionAndName; import org.jclouds.logging.Logger; import org.jclouds.rest.ResourceNotFoundException; @@ -34,13 +36,11 @@ import com.google.common.collect.Iterables; import com.google.inject.Inject; /** - * - * Tests to see if a task succeeds. * * @author Adrian Cole */ @Singleton -public class InstancePresent implements Predicate { +public class InstancePresent implements Predicate { private final EC2Client client; @@ -49,13 +49,13 @@ public class InstancePresent implements Predicate { @Inject public InstancePresent(EC2Client client) { - this.client = client; + this.client = checkNotNull(client, "client"); } - public boolean apply(RunningInstance instance) { - logger.trace("looking for instance %s", instance); + public boolean apply(RegionAndName instance) { + logger.trace("looking for instance %s/%s", instance.getRegion(), instance.getName()); try { - instance = refresh(instance); + refresh(instance); return true; } catch (ResourceNotFoundException e) { return false; @@ -64,8 +64,8 @@ public class InstancePresent implements Predicate { } } - private RunningInstance refresh(RunningInstance instance) { - return Iterables.getOnlyElement(Iterables.getOnlyElement(client.getInstanceServices().describeInstancesInRegion( - instance.getRegion(), instance.getId()))); + protected void refresh(RegionAndName instance) { + Iterables.getOnlyElement(Iterables.getOnlyElement(client.getInstanceServices().describeInstancesInRegion( + instance.getRegion(), instance.getName()))); } } diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/strategy/EC2CreateNodesInGroupThenAddToSet.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/strategy/EC2CreateNodesInGroupThenAddToSet.java index c4eaaddf2d..adf98e6b9b 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 @@ -19,10 +19,10 @@ package org.jclouds.ec2.compute.strategy; +import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.collect.Iterables.all; import static com.google.common.collect.Iterables.transform; import static org.jclouds.ec2.compute.util.EC2ComputeUtils.getZoneFromLocationOrNull; -import static org.jclouds.ec2.compute.util.EC2ComputeUtils.instanceToId; import java.util.Map; import java.util.Set; @@ -44,6 +44,8 @@ import org.jclouds.compute.strategy.CreateNodesInGroupThenAddToSet; import org.jclouds.compute.util.ComputeUtils; import org.jclouds.domain.Credentials; import org.jclouds.ec2.EC2Client; +import org.jclouds.ec2.compute.domain.RegionAndName; +import org.jclouds.ec2.compute.predicates.InstancePresent; import org.jclouds.ec2.domain.RunningInstance; import org.jclouds.ec2.options.RunInstancesOptions; import org.jclouds.logging.Logger; @@ -51,7 +53,6 @@ import org.jclouds.logging.Logger; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Function; import com.google.common.base.Joiner; -import com.google.common.base.Predicate; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Multimap; @@ -76,49 +77,58 @@ public class EC2CreateNodesInGroupThenAddToSet implements CreateNodesInGroupThen final Function runningInstanceToNodeMetadata; @VisibleForTesting final ComputeUtils utils; - final Predicate instancePresent; + final InstancePresent instancePresent; final Function instanceToCredentials; final Map credentialStore; final Provider templateBuilderProvider; @Inject - EC2CreateNodesInGroupThenAddToSet( - 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; - this.instanceToCredentials = instanceToCredentials; - this.credentialStore = credentialStore; - this.utils = utils; + protected EC2CreateNodesInGroupThenAddToSet( + EC2Client client, + Provider templateBuilderProvider, + CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions createKeyPairAndSecurityGroupsAsNeededAndReturncustomize, + InstancePresent instancePresent, Function runningInstanceToNodeMetadata, + Function instanceToCredentials, Map credentialStore, + ComputeUtils utils) { + this.client = checkNotNull(client, "client"); + this.templateBuilderProvider = checkNotNull(templateBuilderProvider, "templateBuilderProvider"); + this.instancePresent = checkNotNull(instancePresent, "instancePresent"); + this.createKeyPairAndSecurityGroupsAsNeededAndReturncustomize = checkNotNull( + createKeyPairAndSecurityGroupsAsNeededAndReturncustomize, + "createKeyPairAndSecurityGroupsAsNeededAndReturncustomize"); + this.runningInstanceToNodeMetadata = checkNotNull(runningInstanceToNodeMetadata, "runningInstanceToNodeMetadata"); + this.instanceToCredentials = checkNotNull(instanceToCredentials, "instanceToCredentials"); + this.credentialStore = checkNotNull(credentialStore, "credentialStore"); + this.utils = checkNotNull(utils, "utils"); } + public static Function instanceToRegionAndName = new Function() { + @Override + public RegionAndName apply(RunningInstance from) { + return new RegionAndName(from.getRegion(), from.getId()); + } + }; + @Override public Map> execute(String group, int count, Template template, Set goodNodes, - Map badNodes, Multimap customizationResponses) { + 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); + count, template); + + Iterable ids = transform(started, instanceToRegionAndName); String idsString = Joiner.on(',').join(ids); if (Iterables.size(ids) > 0) { logger.debug("<< started instances(%s)", idsString); - all(started, instancePresent); + all(ids, instancePresent); logger.debug("<< present instances(%s)", idsString); 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,19 +141,20 @@ public class EC2CreateNodesInGroupThenAddToSet implements CreateNodesInGroupThen if (credentials != null) for (RunningInstance instance : started) credentialStore.put("node#" + instance.getRegion() + "/" + instance.getId(), credentials); - } // TODO write test for this - @VisibleForTesting - Iterable createKeyPairAndSecurityGroupsAsNeededThenRunInstances(String group, int count, - Template template) { + protected Iterable createKeyPairAndSecurityGroupsAsNeededThenRunInstances(String group, + int count, Template template) { String region = AWSUtils.getRegionFromLocationOrNull(template.getLocation()); String zone = getZoneFromLocationOrNull(template.getLocation()); - RunInstancesOptions instanceOptions = createKeyPairAndSecurityGroupsAsNeededAndReturncustomize.execute(region, - group, template); + group, template); + return createNodesInRegionAndZone(region, zone, count, template, instanceOptions); + } + protected Iterable createNodesInRegionAndZone(String region, String zone, int count, + Template template, RunInstancesOptions instanceOptions) { int countStarted = 0; int tries = 0; Iterable started = ImmutableSet. of(); @@ -151,12 +162,10 @@ 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/compute/strategy/EC2DestroyNodeStrategy.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/strategy/EC2DestroyNodeStrategy.java index 59063c7127..c2e807c21f 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/strategy/EC2DestroyNodeStrategy.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/strategy/EC2DestroyNodeStrategy.java @@ -19,6 +19,8 @@ package org.jclouds.ec2.compute.strategy; +import static com.google.common.base.Preconditions.checkNotNull; + import javax.annotation.Resource; import javax.inject.Inject; import javax.inject.Named; @@ -41,14 +43,13 @@ public class EC2DestroyNodeStrategy implements DestroyNodeStrategy { @Resource @Named(ComputeServiceConstants.COMPUTE_LOGGER) protected Logger logger = Logger.NULL; - protected final EC2Client ec2Client; + protected final EC2Client client; protected final GetNodeMetadataStrategy getNode; @Inject - protected EC2DestroyNodeStrategy(EC2Client ec2Client, - GetNodeMetadataStrategy getNodeMetadataStrategy) { - this.ec2Client = ec2Client; - this.getNode = getNodeMetadataStrategy; + protected EC2DestroyNodeStrategy(EC2Client client, GetNodeMetadataStrategy getNode) { + this.client = checkNotNull(client, "client"); + this.getNode = checkNotNull(getNode, "getNode"); } @Override @@ -56,8 +57,11 @@ public class EC2DestroyNodeStrategy implements DestroyNodeStrategy { String[] parts = AWSUtils.parseHandle(id); String region = parts[0]; String instanceId = parts[1]; - ec2Client.getInstanceServices().terminateInstancesInRegion(region, - instanceId); + destroyInstanceInRegion(region, instanceId); return getNode.getNode(id); } + + protected void destroyInstanceInRegion(String region, String instanceId) { + client.getInstanceServices().terminateInstancesInRegion(region, instanceId); + } } diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/strategy/EC2GetNodeMetadataStrategy.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/strategy/EC2GetNodeMetadataStrategy.java index 53a5a75b03..2cb5daf264 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/strategy/EC2GetNodeMetadataStrategy.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/strategy/EC2GetNodeMetadataStrategy.java @@ -19,6 +19,7 @@ package org.jclouds.ec2.compute.strategy; +import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.collect.Iterables.getOnlyElement; import java.util.NoSuchElementException; @@ -31,7 +32,6 @@ import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.compute.strategy.GetNodeMetadataStrategy; import org.jclouds.ec2.EC2Client; import org.jclouds.ec2.domain.RunningInstance; -import org.jclouds.ec2.services.InstanceClient; import com.google.common.base.Function; import com.google.common.collect.Iterables; @@ -48,28 +48,27 @@ public class EC2GetNodeMetadataStrategy implements GetNodeMetadataStrategy { @Inject protected EC2GetNodeMetadataStrategy(EC2Client client, - Function runningInstanceToNodeMetadata) { - this.client = client; - this.runningInstanceToNodeMetadata = runningInstanceToNodeMetadata; + Function runningInstanceToNodeMetadata) { + this.client = checkNotNull(client, "client"); + this.runningInstanceToNodeMetadata = checkNotNull(runningInstanceToNodeMetadata, "runningInstanceToNodeMetadata"); } @Override public NodeMetadata getNode(String id) { + checkNotNull(id, "id"); String[] parts = AWSUtils.parseHandle(id); String region = parts[0]; String instanceId = parts[1]; try { - RunningInstance runningInstance = getOnlyElement(getAllRunningInstancesInRegion(client.getInstanceServices(), - region, instanceId)); + RunningInstance runningInstance = getRunningInstanceInRegion(region, instanceId); return runningInstanceToNodeMetadata.apply(runningInstance); } catch (NoSuchElementException e) { return null; } } - public static Iterable getAllRunningInstancesInRegion(InstanceClient client, String region, - String id) { - return Iterables.concat(client.describeInstancesInRegion(region, id)); + public RunningInstance getRunningInstanceInRegion(String region, String id) { + return getOnlyElement(Iterables.concat(client.getInstanceServices().describeInstancesInRegion(region, id))); } } diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/strategy/EC2ListNodesStrategy.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/strategy/EC2ListNodesStrategy.java index 73d6585cee..79402928e0 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/strategy/EC2ListNodesStrategy.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/strategy/EC2ListNodesStrategy.java @@ -19,10 +19,12 @@ package org.jclouds.ec2.compute.strategy; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Predicates.and; +import static com.google.common.base.Predicates.notNull; 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 com.google.common.collect.Sets.newLinkedHashSet; import static org.jclouds.concurrent.FutureIterables.transformParallel; import java.util.Set; @@ -35,19 +37,20 @@ import javax.inject.Named; import javax.inject.Singleton; import org.jclouds.Constants; -import org.jclouds.ec2.EC2AsyncClient; -import org.jclouds.ec2.domain.Reservation; -import org.jclouds.ec2.domain.RunningInstance; import org.jclouds.compute.domain.ComputeMetadata; import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.compute.predicates.NodePredicates; import org.jclouds.compute.reference.ComputeServiceConstants; import org.jclouds.compute.strategy.ListNodesStrategy; +import org.jclouds.ec2.EC2AsyncClient; +import org.jclouds.ec2.domain.Reservation; +import org.jclouds.ec2.domain.RunningInstance; import org.jclouds.location.Region; import org.jclouds.logging.Logger; import com.google.common.base.Function; import com.google.common.base.Predicate; +import com.google.common.collect.ImmutableSet; /** * @@ -59,19 +62,19 @@ public class EC2ListNodesStrategy implements ListNodesStrategy { @Named(ComputeServiceConstants.COMPUTE_LOGGER) protected Logger logger = Logger.NULL; - private final EC2AsyncClient client; - private final Set regions; - private final Function runningInstanceToNodeMetadata; - private final ExecutorService executor; + protected final EC2AsyncClient client; + protected final Set regions; + protected final Function runningInstanceToNodeMetadata; + protected final ExecutorService executor; @Inject protected EC2ListNodesStrategy(EC2AsyncClient client, @Region Set regions, - Function runningInstanceToNodeMetadata, - @Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor) { - this.client = client; - this.regions = regions; - this.runningInstanceToNodeMetadata = runningInstanceToNodeMetadata; - this.executor = executor; + Function runningInstanceToNodeMetadata, + @Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor) { + this.client = checkNotNull(client, "client"); + this.regions = checkNotNull(regions, "regions"); + this.runningInstanceToNodeMetadata = checkNotNull(runningInstanceToNodeMetadata, "runningInstanceToNodeMetadata"); + this.executor = checkNotNull(executor, "executor"); } @Override @@ -81,19 +84,25 @@ public class EC2ListNodesStrategy implements ListNodesStrategy { @Override public Set listDetailsOnNodesMatching(Predicate filter) { + Iterable instances = pollRunningInstances(); + Iterable nodes = filter(transform(filter(instances, notNull()), + runningInstanceToNodeMetadata), and(notNull(), filter)); + return ImmutableSet.copyOf(nodes); + } + + protected Iterable pollRunningInstances() { Iterable>> reservations = transformParallel( - regions, new Function>>>() { + regions, new Function>>>() { - @SuppressWarnings("unchecked") - @Override - public Future>> apply(String from) { - return (Future>>) client.getInstanceServices().describeInstancesInRegion(from); - } + @SuppressWarnings("unchecked") + @Override + public Future>> apply(String from) { + return (Future>>) client + .getInstanceServices().describeInstancesInRegion(from); + } - }, executor, null, logger, "reservations"); + }, executor, null, logger, "reservations"); - Iterable instances = concat(concat(reservations)); - Iterable nodes = filter(transform(instances, runningInstanceToNodeMetadata), filter); - return newLinkedHashSet(nodes); + return concat(concat(reservations)); } } diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/util/EC2ComputeUtils.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/util/EC2ComputeUtils.java index c3f0a8a049..8018d482ce 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/util/EC2ComputeUtils.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/util/EC2ComputeUtils.java @@ -21,12 +21,9 @@ package org.jclouds.ec2.compute.util; import javax.inject.Singleton; -import org.jclouds.ec2.domain.RunningInstance; import org.jclouds.domain.Location; import org.jclouds.domain.LocationScope; -import com.google.common.base.Function; - /** * * @author Adrian Cole @@ -34,13 +31,6 @@ import com.google.common.base.Function; @Singleton public class EC2ComputeUtils { - public static Function instanceToId = new Function() { - @Override - public String apply(RunningInstance from) { - return from.getId(); - } - }; - public static String getZoneFromLocationOrNull(Location location) { return location.getScope() == LocationScope.ZONE ? location.getId() : null; } diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/domain/RunningInstance.java b/apis/ec2/src/main/java/org/jclouds/ec2/domain/RunningInstance.java index 020c2e975f..97d2067919 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/domain/RunningInstance.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/domain/RunningInstance.java @@ -39,6 +39,9 @@ import com.google.common.collect.Sets; * @author Adrian Cole */ public class RunningInstance implements Comparable { + public static Builder builder() { + return new Builder(); + } public static class Builder { protected String region; @@ -265,8 +268,8 @@ public class RunningInstance implements Comparable { this.ipAddress = ipAddress; this.kernelId = kernelId; this.keyName = keyName; - this.launchTime = checkNotNull(launchTime, "launchTime"); - this.availabilityZone = checkNotNull(availabilityZone, "availabilityZone"); + this.launchTime = launchTime;// nullable on spot. + this.availabilityZone = availabilityZone;// nullable on spot. this.virtualizationType = virtualizationType; this.platform = platform; this.privateDnsName = privateDnsName;// nullable on runinstances. 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/EC2CreateNodesInGroupThenAddToSetTest.java similarity index 95% rename from apis/ec2/src/test/java/org/jclouds/ec2/compute/strategy/EC2RunNodesAndAddToSetStrategyTest.java rename to apis/ec2/src/test/java/org/jclouds/ec2/compute/strategy/EC2CreateNodesInGroupThenAddToSetTest.java index e79ff2b0c7..ec9b73ab95 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/EC2CreateNodesInGroupThenAddToSetTest.java @@ -42,8 +42,10 @@ import org.jclouds.domain.Location; import org.jclouds.domain.LocationBuilder; import org.jclouds.domain.LocationScope; import org.jclouds.ec2.EC2Client; +import org.jclouds.ec2.compute.domain.RegionAndName; import org.jclouds.ec2.compute.functions.RunningInstanceToNodeMetadata; import org.jclouds.ec2.compute.options.EC2TemplateOptions; +import org.jclouds.ec2.compute.predicates.InstancePresent; import org.jclouds.ec2.domain.Reservation; import org.jclouds.ec2.domain.RunningInstance; import org.jclouds.ec2.options.RunInstancesOptions; @@ -51,7 +53,6 @@ import org.jclouds.ec2.services.InstanceClient; import org.testng.annotations.Test; import com.google.common.base.Function; -import com.google.common.base.Predicate; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Multimap; @@ -61,7 +62,7 @@ import com.google.inject.util.Providers; * @author Adrian Cole */ @Test(groups = "unit") -public class EC2RunNodesAndAddToSetStrategyTest { +public class EC2CreateNodesInGroupThenAddToSetTest { @Test public void testZoneAsALocation() { @@ -126,10 +127,10 @@ public class EC2RunNodesAndAddToSetStrategyTest { // simulate a lazy credentials fetch Credentials creds = new Credentials("foo", "bar"); expect(strategy.instanceToCredentials.apply(instance)).andReturn(creds); - expect(instance.getRegion()).andReturn(region); + expect(instance.getRegion()).andReturn(region).atLeastOnce(); expect(strategy.credentialStore.put("node#" + region + "/" + instanceCreatedId, creds)).andReturn(null); - expect(strategy.instancePresent.apply(instance)).andReturn(true); + expect(strategy.instancePresent.apply(new RegionAndName(region, instanceCreatedId))).andReturn(true); expect(input.template.getOptions()).andReturn(input.options).atLeastOnce(); expect(strategy.runningInstanceToNodeMetadata.apply(instance)).andReturn(nodeMetadata); @@ -220,13 +221,13 @@ public class EC2RunNodesAndAddToSetStrategyTest { private EC2CreateNodesInGroupThenAddToSet setupStrategy(TemplateBuilder template) { EC2Client client = createMock(EC2Client.class); CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions createKeyPairAndSecurityGroupsAsNeededAndReturncustomize = createMock(CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.class); - Predicate instanceStateRunning = createMock(Predicate.class); + InstancePresent instancePresent = createMock(InstancePresent.class); RunningInstanceToNodeMetadata runningInstanceToNodeMetadata = createMock(RunningInstanceToNodeMetadata.class); Function instanceToCredentials = createMock(Function.class); Map credentialStore = createMock(Map.class); ComputeUtils utils = createMock(ComputeUtils.class); return new EC2CreateNodesInGroupThenAddToSet(client, Providers. of(template), - createKeyPairAndSecurityGroupsAsNeededAndReturncustomize, instanceStateRunning, + createKeyPairAndSecurityGroupsAsNeededAndReturncustomize, instancePresent, runningInstanceToNodeMetadata, instanceToCredentials, credentialStore, utils); } diff --git a/compute/src/main/java/org/jclouds/compute/predicates/NodePresentAndInIntendedState.java b/compute/src/main/java/org/jclouds/compute/predicates/NodePresentAndInIntendedState.java index 1564f2ec7b..d9c27f09a2 100644 --- a/compute/src/main/java/org/jclouds/compute/predicates/NodePresentAndInIntendedState.java +++ b/compute/src/main/java/org/jclouds/compute/predicates/NodePresentAndInIntendedState.java @@ -65,6 +65,8 @@ public class NodePresentAndInIntendedState implements Predicate { } private NodeMetadata refresh(NodeMetadata node) { + if (node == null || node.getId() == null) + return null; return client.getNodeMetadata(node.getId()); } } diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/AWSEC2TemplateOptions.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/AWSEC2TemplateOptions.java index 2dbb0e83a2..dc5b5cdd24 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/AWSEC2TemplateOptions.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/AWSEC2TemplateOptions.java @@ -27,6 +27,7 @@ import java.util.Set; import javax.annotation.Nullable; +import org.jclouds.aws.ec2.options.RequestSpotInstancesOptions; import org.jclouds.compute.options.TemplateOptions; import org.jclouds.domain.Credentials; import org.jclouds.ec2.compute.options.EC2TemplateOptions; @@ -73,6 +74,10 @@ public class AWSEC2TemplateOptions extends EC2TemplateOptions implements Cloneab eTo.noPlacementGroup(); if (getPlacementGroup() != null) eTo.placementGroup(getPlacementGroup()); + if (getSpotPrice() != null) + eTo.spotPrice(getSpotPrice()); + if (getSpotOptions() != null) + eTo.spotOptions(getSpotOptions()); } } @@ -80,6 +85,8 @@ public class AWSEC2TemplateOptions extends EC2TemplateOptions implements Cloneab private String placementGroup = null; private boolean noPlacementGroup; private String subnetId; + private Float spotPrice; + private RequestSpotInstancesOptions spotOptions = RequestSpotInstancesOptions.NONE; public static final AWSEC2TemplateOptions NONE = new AWSEC2TemplateOptions(); @@ -123,6 +130,22 @@ public class AWSEC2TemplateOptions extends EC2TemplateOptions implements Cloneab return this; } + /** + * Specifies the maximum spot price to use + */ + public AWSEC2TemplateOptions spotPrice(Float spotPrice) { + this.spotPrice = spotPrice; + return this; + } + + /** + * Options for starting spot instances + */ + public AWSEC2TemplateOptions spotOptions(RequestSpotInstancesOptions spotOptions) { + this.spotOptions = spotOptions != null ? spotOptions : RequestSpotInstancesOptions.NONE; + return this; + } + public static class Builder { /** * @see EC2TemplateOptions#blockDeviceMappings @@ -136,7 +159,7 @@ public class AWSEC2TemplateOptions extends EC2TemplateOptions implements Cloneab * @see EC2TemplateOptions#mapEBSSnapshotToDeviceName */ public static AWSEC2TemplateOptions mapEBSSnapshotToDeviceName(String deviceName, String snapshotId, - @Nullable Integer sizeInGib, boolean deleteOnTermination) { + @Nullable Integer sizeInGib, boolean deleteOnTermination) { AWSEC2TemplateOptions options = new AWSEC2TemplateOptions(); return options.mapEBSSnapshotToDeviceName(deviceName, snapshotId, sizeInGib, deleteOnTermination); } @@ -145,7 +168,7 @@ public class AWSEC2TemplateOptions extends EC2TemplateOptions implements Cloneab * @see EC2TemplateOptions#mapNewVolumeToDeviceName */ public static AWSEC2TemplateOptions mapNewVolumeToDeviceName(String deviceName, int sizeInGib, - boolean deleteOnTermination) { + boolean deleteOnTermination) { AWSEC2TemplateOptions options = new AWSEC2TemplateOptions(); return options.mapNewVolumeToDeviceName(deviceName, sizeInGib, deleteOnTermination); } @@ -280,12 +303,28 @@ public class AWSEC2TemplateOptions extends EC2TemplateOptions implements Cloneab } /** - * @see TemplateOptions#withSubnetId + * @see TemplateOptions#spotPrice */ public static AWSEC2TemplateOptions subnetId(String subnetId) { AWSEC2TemplateOptions options = new AWSEC2TemplateOptions(); return options.subnetId(subnetId); } + + /** + * @see TemplateOptions#spotPrice + */ + public static AWSEC2TemplateOptions spotPrice(Float spotPrice) { + AWSEC2TemplateOptions options = new AWSEC2TemplateOptions(); + return options.spotPrice(spotPrice); + } + + /** + * @see TemplateOptions#spotOptions + */ + public static AWSEC2TemplateOptions spotOptions(RequestSpotInstancesOptions spotOptions) { + AWSEC2TemplateOptions options = new AWSEC2TemplateOptions(); + return options.spotOptions(spotOptions); + } } // methods that only facilitate returning the correct object type @@ -312,9 +351,9 @@ public class AWSEC2TemplateOptions extends EC2TemplateOptions implements Cloneab @Override public AWSEC2TemplateOptions mapEBSSnapshotToDeviceName(String deviceName, String snapshotId, Integer sizeInGib, - boolean deleteOnTermination) { + boolean deleteOnTermination) { return AWSEC2TemplateOptions.class.cast(super.mapEBSSnapshotToDeviceName(deviceName, snapshotId, sizeInGib, - deleteOnTermination)); + deleteOnTermination)); } /** @@ -331,7 +370,7 @@ public class AWSEC2TemplateOptions extends EC2TemplateOptions implements Cloneab @Override public AWSEC2TemplateOptions mapNewVolumeToDeviceName(String deviceName, int sizeInGib, boolean deleteOnTermination) { return AWSEC2TemplateOptions.class.cast(super - .mapNewVolumeToDeviceName(deviceName, sizeInGib, deleteOnTermination)); + .mapNewVolumeToDeviceName(deviceName, sizeInGib, deleteOnTermination)); } /** @@ -525,6 +564,20 @@ public class AWSEC2TemplateOptions extends EC2TemplateOptions implements Cloneab return subnetId; } + /** + * @return maximum spot price or null. + */ + public Float getSpotPrice() { + return spotPrice; + } + + /** + * @return options for controlling spot instance requests. + */ + public RequestSpotInstancesOptions getSpotOptions() { + return spotOptions; + } + @Override public int hashCode() { final int prime = 31; @@ -532,6 +585,8 @@ public class AWSEC2TemplateOptions extends EC2TemplateOptions implements Cloneab result = prime * result + (monitoringEnabled ? 1231 : 1237); result = prime * result + (noPlacementGroup ? 1231 : 1237); result = prime * result + ((placementGroup == null) ? 0 : placementGroup.hashCode()); + result = prime * result + ((spotOptions == null) ? 0 : spotOptions.hashCode()); + result = prime * result + ((spotPrice == null) ? 0 : spotPrice.hashCode()); result = prime * result + ((subnetId == null) ? 0 : subnetId.hashCode()); return result; } @@ -554,6 +609,16 @@ public class AWSEC2TemplateOptions extends EC2TemplateOptions implements Cloneab return false; } else if (!placementGroup.equals(other.placementGroup)) return false; + if (spotOptions == null) { + if (other.spotOptions != null) + return false; + } else if (!spotOptions.equals(other.spotOptions)) + return false; + if (spotPrice == null) { + if (other.spotPrice != null) + return false; + } else if (!spotPrice.equals(other.spotPrice)) + return false; if (subnetId == null) { if (other.subnetId != null) return false; @@ -566,9 +631,10 @@ public class AWSEC2TemplateOptions extends EC2TemplateOptions implements Cloneab public String toString() { return "[groupIds=" + getGroupIds() + ", keyPair=" + getKeyPair() + ", noKeyPair=" - + !shouldAutomaticallyCreateKeyPair() + ", monitoringEnabled=" + monitoringEnabled + ", placementGroup=" - + placementGroup + ", noPlacementGroup=" + noPlacementGroup + ", subnetId=" + subnetId + ", userData=" - + Arrays.toString(getUserData()) + ", blockDeviceMappings=" + getBlockDeviceMappings() + "]"; + + !shouldAutomaticallyCreateKeyPair() + ", monitoringEnabled=" + monitoringEnabled + ", placementGroup=" + + placementGroup + ", noPlacementGroup=" + noPlacementGroup + ", subnetId=" + subnetId + ", userData=" + + Arrays.toString(getUserData()) + ", blockDeviceMappings=" + getBlockDeviceMappings() + ", spotPrice=" + + spotPrice + ", spotOptions=" + spotOptions + "]"; } } diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceContextModule.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceContextModule.java index 0ea1bd580c..7ae5e9ce33 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceContextModule.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceContextModule.java @@ -22,14 +22,26 @@ package org.jclouds.aws.ec2.compute.config; import static org.jclouds.compute.domain.OsFamily.AMZN_LINUX; import org.jclouds.aws.ec2.compute.AWSEC2TemplateBuilderImpl; +import org.jclouds.aws.ec2.compute.functions.AWSRunningInstanceToNodeMetadata; +import org.jclouds.aws.ec2.compute.predicates.AWSEC2InstancePresent; +import org.jclouds.aws.ec2.compute.strategy.AWSEC2CreateNodesInGroupThenAddToSet; +import org.jclouds.aws.ec2.compute.strategy.AWSEC2DestroyNodeStrategy; +import org.jclouds.aws.ec2.compute.strategy.AWSEC2GetNodeMetadataStrategy; +import org.jclouds.aws.ec2.compute.strategy.AWSEC2ListNodesStrategy; import org.jclouds.aws.ec2.compute.strategy.AWSEC2ReviseParsedImage; import org.jclouds.aws.ec2.compute.strategy.CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions; import org.jclouds.aws.ec2.compute.suppliers.AWSEC2HardwareSupplier; import org.jclouds.aws.ec2.compute.suppliers.AWSRegionAndNameToImageSupplier; import org.jclouds.compute.domain.TemplateBuilder; import org.jclouds.ec2.compute.config.EC2ComputeServiceContextModule; +import org.jclouds.ec2.compute.functions.RunningInstanceToNodeMetadata; import org.jclouds.ec2.compute.internal.EC2TemplateBuilderImpl; +import org.jclouds.ec2.compute.predicates.InstancePresent; import org.jclouds.ec2.compute.strategy.CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions; +import org.jclouds.ec2.compute.strategy.EC2CreateNodesInGroupThenAddToSet; +import org.jclouds.ec2.compute.strategy.EC2DestroyNodeStrategy; +import org.jclouds.ec2.compute.strategy.EC2GetNodeMetadataStrategy; +import org.jclouds.ec2.compute.strategy.EC2ListNodesStrategy; import org.jclouds.ec2.compute.strategy.ReviseParsedImage; import org.jclouds.ec2.compute.suppliers.EC2HardwareSupplier; import org.jclouds.ec2.compute.suppliers.RegionAndNameToImageSupplier; @@ -56,6 +68,12 @@ public class AWSEC2ComputeServiceContextModule extends EC2ComputeServiceContextM bind(EC2HardwareSupplier.class).to(AWSEC2HardwareSupplier.class); bind(RegionAndNameToImageSupplier.class).to(AWSRegionAndNameToImageSupplier.class); bind(EC2TemplateBuilderImpl.class).to(AWSEC2TemplateBuilderImpl.class); + bind(EC2GetNodeMetadataStrategy.class).to(AWSEC2GetNodeMetadataStrategy.class); + bind(EC2ListNodesStrategy.class).to(AWSEC2ListNodesStrategy.class); + bind(EC2DestroyNodeStrategy.class).to(AWSEC2DestroyNodeStrategy.class); + bind(InstancePresent.class).to(AWSEC2InstancePresent.class); + bind(EC2CreateNodesInGroupThenAddToSet.class).to(AWSEC2CreateNodesInGroupThenAddToSet.class); + bind(RunningInstanceToNodeMetadata.class).to(AWSRunningInstanceToNodeMetadata.class); } @Override diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/functions/AWSRunningInstanceToNodeMetadata.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/functions/AWSRunningInstanceToNodeMetadata.java new file mode 100644 index 0000000000..e663f8542f --- /dev/null +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/functions/AWSRunningInstanceToNodeMetadata.java @@ -0,0 +1,69 @@ +/** + * + * 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.compute.functions; + +import java.util.Map; +import java.util.Set; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import org.jclouds.aws.ec2.domain.AWSRunningInstance; +import org.jclouds.collect.Memoized; +import org.jclouds.compute.domain.Hardware; +import org.jclouds.compute.domain.Image; +import org.jclouds.compute.domain.NodeMetadataBuilder; +import org.jclouds.compute.domain.NodeState; +import org.jclouds.domain.Credentials; +import org.jclouds.domain.Location; +import org.jclouds.ec2.compute.domain.RegionAndName; +import org.jclouds.ec2.compute.functions.RunningInstanceToNodeMetadata; +import org.jclouds.ec2.domain.InstanceState; +import org.jclouds.ec2.domain.RunningInstance; + +import com.google.common.base.Supplier; + +/** + * @author Adrian Cole + */ +@Singleton +public class AWSRunningInstanceToNodeMetadata extends RunningInstanceToNodeMetadata { + + @Inject + protected AWSRunningInstanceToNodeMetadata(Map instanceToNodeState, + Map credentialStore, Map instanceToImage, + @Memoized Supplier> locations, @Memoized Supplier> hardware) { + super(instanceToNodeState, credentialStore, instanceToImage, locations, hardware); + } + + @Override + protected void addCredentialsForInstance(NodeMetadataBuilder builder, RunningInstance instance) { + Credentials creds = credentialStore.get("node#" + instance.getRegion() + "/" + instance.getId()); + String spotRequestId = AWSRunningInstance.class.cast(instance).getSpotInstanceRequestId(); + if (creds == null && spotRequestId != null) { + creds = credentialStore.get("node#" + instance.getRegion() + "/" + spotRequestId); + if (creds != null) + credentialStore.put("node#" + instance.getRegion() + "/" + instance.getId(), creds); + } + if (creds != null) + builder.credentials(creds); + } + +} diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/predicates/AWSEC2InstancePresent.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/predicates/AWSEC2InstancePresent.java new file mode 100644 index 0000000000..f1865dc647 --- /dev/null +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/predicates/AWSEC2InstancePresent.java @@ -0,0 +1,56 @@ +/** + * + * 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.compute.predicates; + +import static com.google.common.base.Preconditions.checkNotNull; + +import javax.inject.Singleton; + +import org.jclouds.aws.ec2.AWSEC2Client; +import org.jclouds.ec2.compute.domain.RegionAndName; +import org.jclouds.ec2.compute.predicates.InstancePresent; + +import com.google.common.collect.Iterables; +import com.google.inject.Inject; + +/** + * + * @author Adrian Cole + */ +@Singleton +public class AWSEC2InstancePresent extends InstancePresent { + + private final AWSEC2Client client; + + @Inject + public AWSEC2InstancePresent(AWSEC2Client client) { + super(client); + this.client = checkNotNull(client, "client"); + } + + @Override + protected void refresh(RegionAndName instance) { + if (instance.getName().indexOf("sir-") != 0) + super.refresh(instance); + else + Iterables.getOnlyElement(client.getSpotInstanceServices().describeSpotInstanceRequestsInRegion( + instance.getRegion(), instance.getName())); + } +} diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/strategy/AWSEC2CreateNodesInGroupThenAddToSet.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/strategy/AWSEC2CreateNodesInGroupThenAddToSet.java new file mode 100644 index 0000000000..f8399e968e --- /dev/null +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/strategy/AWSEC2CreateNodesInGroupThenAddToSet.java @@ -0,0 +1,109 @@ +/** + * + * 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.compute.strategy; + +import static com.google.common.base.Preconditions.checkNotNull; + +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.AWSEC2Client; +import org.jclouds.aws.ec2.compute.AWSEC2TemplateOptions; +import org.jclouds.aws.ec2.compute.predicates.AWSEC2InstancePresent; +import org.jclouds.aws.ec2.domain.LaunchSpecification; +import org.jclouds.aws.ec2.functions.SpotInstanceRequestToAWSRunningInstance; +import org.jclouds.aws.ec2.options.AWSRunInstancesOptions; +import org.jclouds.aws.ec2.options.RequestSpotInstancesOptions; +import org.jclouds.compute.domain.NodeMetadata; +import org.jclouds.compute.domain.Template; +import org.jclouds.compute.domain.TemplateBuilder; +import org.jclouds.compute.options.TemplateOptions; +import org.jclouds.compute.reference.ComputeServiceConstants; +import org.jclouds.compute.util.ComputeUtils; +import org.jclouds.domain.Credentials; +import org.jclouds.ec2.compute.strategy.EC2CreateNodesInGroupThenAddToSet; +import org.jclouds.ec2.domain.RunningInstance; +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.collect.Iterables; + +/** + * + * @author Adrian Cole + */ +@Singleton +public class AWSEC2CreateNodesInGroupThenAddToSet extends EC2CreateNodesInGroupThenAddToSet { + + @Resource + @Named(ComputeServiceConstants.COMPUTE_LOGGER) + protected Logger logger = Logger.NULL; + + @VisibleForTesting + final AWSEC2Client client; + final SpotInstanceRequestToAWSRunningInstance spotConverter; + + @Inject + protected AWSEC2CreateNodesInGroupThenAddToSet( + AWSEC2Client client, + Provider templateBuilderProvider, + CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions createKeyPairAndSecurityGroupsAsNeededAndReturncustomize, + AWSEC2InstancePresent instancePresent, + Function runningInstanceToNodeMetadata, + Function instanceToCredentials, Map credentialStore, + ComputeUtils utils, SpotInstanceRequestToAWSRunningInstance spotConverter) { + + super(client, templateBuilderProvider, createKeyPairAndSecurityGroupsAsNeededAndReturncustomize, instancePresent, + runningInstanceToNodeMetadata, instanceToCredentials, credentialStore, utils); + this.client = checkNotNull(client, "client"); + this.spotConverter = checkNotNull(spotConverter, "spotConverter"); + } + + protected Iterable createNodesInRegionAndZone(String region, String zone, int count, + Template template, RunInstancesOptions instanceOptions) { + Float spotPrice = getSpotPriceOrNull(template.getOptions()); + if (spotPrice != null) { + LaunchSpecification spec = AWSRunInstancesOptions.class.cast(instanceOptions).getLaunchSpecificationBuilder() + .imageId(template.getImage().getProviderId()).availabilityZone(zone).build(); + RequestSpotInstancesOptions options = AWSEC2TemplateOptions.class.cast(template.getOptions()).getSpotOptions(); + if (logger.isDebugEnabled()) + logger.debug(">> requesting %d spot instances region(%s) price(%f) spec(%s) options(%s)", count, region, + spotPrice, spec, options); + + return Iterables.transform(client.getSpotInstanceServices().requestSpotInstancesInRegion(region, spotPrice, + count, spec, options), spotConverter); + } else { + return super.createNodesInRegionAndZone(zone, zone, count, template, instanceOptions); + } + + } + + private Float getSpotPriceOrNull(TemplateOptions options) { + return options instanceof AWSEC2TemplateOptions ? AWSEC2TemplateOptions.class.cast(options).getSpotPrice() : null; + } + +} \ No newline at end of file diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/strategy/AWSEC2DestroyNodeStrategy.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/strategy/AWSEC2DestroyNodeStrategy.java new file mode 100644 index 0000000000..c98eb2567d --- /dev/null +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/strategy/AWSEC2DestroyNodeStrategy.java @@ -0,0 +1,35 @@ +package org.jclouds.aws.ec2.compute.strategy; + +import static com.google.common.base.Preconditions.checkNotNull; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import org.jclouds.aws.ec2.AWSEC2Client; +import org.jclouds.compute.strategy.GetNodeMetadataStrategy; +import org.jclouds.ec2.compute.strategy.EC2DestroyNodeStrategy; + +/** + * + * @author Adrian Cole + */ +@Singleton +public class AWSEC2DestroyNodeStrategy extends EC2DestroyNodeStrategy { + + protected final AWSEC2Client client; + + @Inject + protected AWSEC2DestroyNodeStrategy(AWSEC2Client client, GetNodeMetadataStrategy getNode) { + super(client, getNode); + this.client = checkNotNull(client, "client"); + } + + @Override + protected void destroyInstanceInRegion(String region, String id) { + if (id.indexOf("sir-") != 0) { + super.destroyInstanceInRegion(region, id); + } else { + client.getSpotInstanceServices().cancelSpotInstanceRequestsInRegion(region, id); + } + } +} diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/strategy/AWSEC2GetNodeMetadataStrategy.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/strategy/AWSEC2GetNodeMetadataStrategy.java new file mode 100644 index 0000000000..996cc32402 --- /dev/null +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/strategy/AWSEC2GetNodeMetadataStrategy.java @@ -0,0 +1,68 @@ +/** + * + * 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.compute.strategy; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.Iterables.getOnlyElement; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import org.jclouds.aws.ec2.AWSEC2Client; +import org.jclouds.aws.ec2.domain.SpotInstanceRequest; +import org.jclouds.aws.ec2.functions.SpotInstanceRequestToAWSRunningInstance; +import org.jclouds.compute.domain.NodeMetadata; +import org.jclouds.ec2.compute.strategy.EC2GetNodeMetadataStrategy; +import org.jclouds.ec2.domain.RunningInstance; + +import com.google.common.base.Function; + +/** + * + * @author Adrian Cole + */ +@Singleton +public class AWSEC2GetNodeMetadataStrategy extends EC2GetNodeMetadataStrategy { + + private final AWSEC2Client client; + private final SpotInstanceRequestToAWSRunningInstance spotConverter; + + @Inject + protected AWSEC2GetNodeMetadataStrategy(AWSEC2Client client, + Function runningInstanceToNodeMetadata, + SpotInstanceRequestToAWSRunningInstance spotConverter) { + super(client, runningInstanceToNodeMetadata); + this.client = checkNotNull(client, "client"); + this.spotConverter = checkNotNull(spotConverter, "spotConverter"); + } + + @Override + public RunningInstance getRunningInstanceInRegion(String region, String id) { + if (id.indexOf("sir-") != 0) + return super.getRunningInstanceInRegion(region, id); + SpotInstanceRequest spot = getOnlyElement(client.getSpotInstanceServices().describeSpotInstanceRequestsInRegion( + region, id)); + if (spot.getState() == SpotInstanceRequest.State.ACTIVE) + return super.getRunningInstanceInRegion(region, spot.getInstanceId()); + else + return spotConverter.apply(spot); + } + +} diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/strategy/AWSEC2ListNodesStrategy.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/strategy/AWSEC2ListNodesStrategy.java new file mode 100644 index 0000000000..dd9cf3863b --- /dev/null +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/strategy/AWSEC2ListNodesStrategy.java @@ -0,0 +1,85 @@ +/** + * + * 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.compute.strategy; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Predicates.notNull; +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.concurrent.FutureIterables.transformParallel; + +import java.util.Set; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.jclouds.Constants; +import org.jclouds.aws.ec2.AWSEC2AsyncClient; +import org.jclouds.aws.ec2.domain.AWSRunningInstance; +import org.jclouds.aws.ec2.domain.SpotInstanceRequest; +import org.jclouds.aws.ec2.functions.SpotInstanceRequestToAWSRunningInstance; +import org.jclouds.compute.domain.NodeMetadata; +import org.jclouds.ec2.compute.strategy.EC2ListNodesStrategy; +import org.jclouds.ec2.domain.RunningInstance; +import org.jclouds.location.Region; + +import com.google.common.base.Function; + +/** + * + * @author Adrian Cole + */ +@Singleton +public class AWSEC2ListNodesStrategy extends EC2ListNodesStrategy { + + protected final AWSEC2AsyncClient client; + protected final SpotInstanceRequestToAWSRunningInstance spotConverter; + + @Inject + protected AWSEC2ListNodesStrategy(AWSEC2AsyncClient client, @Region Set regions, + Function runningInstanceToNodeMetadata, + @Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor, + SpotInstanceRequestToAWSRunningInstance spotConverter) { + super(client, regions, runningInstanceToNodeMetadata, executor); + this.client = checkNotNull(client, "client"); + this.spotConverter = checkNotNull(spotConverter, "spotConverter"); + } + + @Override + protected Iterable pollRunningInstances() { + Iterable spots = filter(transform(concat(transformParallel(regions, + new Function>>() { + + @SuppressWarnings("unchecked") + @Override + public Future> apply(String from) { + return (Future>) client.getSpotInstanceServices() + .describeSpotInstanceRequestsInRegion(from); + } + + }, executor, null, logger, "reservations")), spotConverter), notNull()); + + return concat(super.pollRunningInstances(), spots); + } +} diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/domain/AWSRunningInstance.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/domain/AWSRunningInstance.java index 03f4ad0e75..508772f601 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/domain/AWSRunningInstance.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/domain/AWSRunningInstance.java @@ -42,6 +42,9 @@ import com.google.common.collect.Sets; * @author Adrian Cole */ public class AWSRunningInstance extends RunningInstance { + public static Builder builder() { + return new Builder(); + } public static class Builder extends org.jclouds.ec2.domain.RunningInstance.Builder { private MonitoringState monitoringState; diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/functions/SpotInstanceRequestToAWSRunningInstance.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/functions/SpotInstanceRequestToAWSRunningInstance.java new file mode 100644 index 0000000000..8c700e3eb7 --- /dev/null +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/functions/SpotInstanceRequestToAWSRunningInstance.java @@ -0,0 +1,64 @@ +/** + * + * 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 javax.inject.Singleton; + +import org.jclouds.aws.ec2.domain.AWSRunningInstance; +import org.jclouds.aws.ec2.domain.LaunchSpecification; +import org.jclouds.aws.ec2.domain.MonitoringState; +import org.jclouds.aws.ec2.domain.SpotInstanceRequest; +import org.jclouds.ec2.domain.InstanceState; + +import com.google.common.base.Function; + +/** + * @author Adrian Cole + */ +@Singleton +public class SpotInstanceRequestToAWSRunningInstance implements Function { + + @Override + public AWSRunningInstance apply(SpotInstanceRequest request) { + if (request == null) + return null; + if (request.getState() != SpotInstanceRequest.State.OPEN) + return null; + AWSRunningInstance.Builder builder = AWSRunningInstance.builder(); + builder.spotInstanceRequestId(request.getId()); + builder.instanceId(request.getId()); + builder.instanceState(InstanceState.PENDING); + builder.region(request.getRegion()); + LaunchSpecification spec = request.getLaunchSpecification(); + builder.availabilityZone(spec.getAvailabilityZone()); + // TODO convert + // builder.devices(spec.getBlockDeviceMappings()); + builder.groupIds(spec.getGroupIds()); + builder.imageId(spec.getImageId()); + builder.instanceType(spec.getInstanceType()); + builder.kernelId(spec.getKernelId()); + builder.keyName(spec.getKeyName()); + builder.ramdiskId(spec.getRamdiskId()); + builder.monitoringState(Boolean.TRUE.equals(spec.isMonitoringEnabled()) ? MonitoringState.PENDING + : MonitoringState.DISABLED); + return builder.build(); + } + +} diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/options/AWSRunInstancesOptions.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/options/AWSRunInstancesOptions.java index a4cf04183c..13860ce69e 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/options/AWSRunInstancesOptions.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/options/AWSRunInstancesOptions.java @@ -23,10 +23,13 @@ import static com.google.common.base.Preconditions.checkNotNull; import java.util.Set; +import org.jclouds.aws.ec2.domain.LaunchSpecification; import org.jclouds.ec2.domain.BlockDeviceMapping; import org.jclouds.ec2.domain.InstanceType; import org.jclouds.ec2.options.RunInstancesOptions; +import com.google.common.collect.ImmutableSet; + /** * Contains options supported in the Form API for the RunInstances operation.

* Usage

The recommended way to instantiate a RunInstancesOptions object is to statically @@ -46,6 +49,7 @@ import org.jclouds.ec2.options.RunInstancesOptions; * /> */ public class AWSRunInstancesOptions extends RunInstancesOptions { + private LaunchSpecification.Builder launchSpecificationBuilder = LaunchSpecification.builder(); public static final AWSRunInstancesOptions NONE = new AWSRunInstancesOptions(); /** @@ -60,22 +64,15 @@ public class AWSRunInstancesOptions extends RunInstancesOptions { return this; } - String getPlacementGroup() { - return getFirstFormOrNull("Placement.GroupName"); - } - /** * Enables monitoring for the instance. */ public AWSRunInstancesOptions enableMonitoring() { formParameters.put("Monitoring.Enabled", "true"); + launchSpecificationBuilder.monitoringEnabled(true); return this; } - String getMonitoringEnabled() { - return getFirstFormOrNull("Monitoring.Enabled"); - } - /** * Specifies the subnet ID within which to launch the instance(s) for Amazon Virtual Private * Cloud. @@ -85,10 +82,6 @@ public class AWSRunInstancesOptions extends RunInstancesOptions { return this; } - String getSubnetId() { - return getFirstFormOrNull("SubnetId"); - } - public static class Builder extends RunInstancesOptions.Builder { /** @@ -175,46 +168,63 @@ public class AWSRunInstancesOptions extends RunInstancesOptions { @Override public AWSRunInstancesOptions withBlockDeviceMappings(Set mappings) { + launchSpecificationBuilder.blockDeviceMappings(mappings); return AWSRunInstancesOptions.class.cast(super.withBlockDeviceMappings(mappings)); } @Override public AWSRunInstancesOptions withKernelId(String kernelId) { + launchSpecificationBuilder.kernelId(kernelId); return AWSRunInstancesOptions.class.cast(super.withKernelId(kernelId)); } @Override public AWSRunInstancesOptions withKeyName(String keyName) { + launchSpecificationBuilder.keyName(keyName); return AWSRunInstancesOptions.class.cast(super.withKeyName(keyName)); } @Override public AWSRunInstancesOptions withRamdisk(String ramDiskId) { + launchSpecificationBuilder.ramdiskId(ramDiskId); return AWSRunInstancesOptions.class.cast(super.withRamdisk(ramDiskId)); } @Override public AWSRunInstancesOptions withSecurityGroup(String securityGroup) { + launchSpecificationBuilder.groupId(securityGroup); return AWSRunInstancesOptions.class.cast(super.withSecurityGroup(securityGroup)); } @Override public AWSRunInstancesOptions withSecurityGroups(Iterable securityGroups) { + launchSpecificationBuilder.groupIds(securityGroups); return AWSRunInstancesOptions.class.cast(super.withSecurityGroups(securityGroups)); } @Override public AWSRunInstancesOptions withSecurityGroups(String... securityGroups) { + launchSpecificationBuilder.groupIds(ImmutableSet.copyOf(securityGroups)); return AWSRunInstancesOptions.class.cast(super.withSecurityGroups(securityGroups)); } @Override public AWSRunInstancesOptions withUserData(byte[] unencodedData) { + launchSpecificationBuilder.userData(unencodedData); return AWSRunInstancesOptions.class.cast(super.withUserData(unencodedData)); } @Override public AWSRunInstancesOptions asType(String type) { + launchSpecificationBuilder.instanceType(type); return AWSRunInstancesOptions.class.cast(super.asType(type)); } + + public synchronized LaunchSpecification.Builder getLaunchSpecificationBuilder() { + try { + return launchSpecificationBuilder.imageId("fake").build().toBuilder().imageId(null); + } finally { + launchSpecificationBuilder.imageId(null); + } + } } 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 b02b8e7dea..d9ec64ff43 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"; @@ -76,6 +76,7 @@ public class AWSEC2ComputeServiceLiveTest extends EC2ComputeServiceLiveTest { options.as(AWSEC2TemplateOptions.class).securityGroups(group); options.as(AWSEC2TemplateOptions.class).keyPair(group); options.as(AWSEC2TemplateOptions.class).enableMonitoring(); + options.as(AWSEC2TemplateOptions.class).spotPrice(0.3f); String startedId = null; try { @@ -117,17 +118,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)); @@ -151,13 +152,13 @@ public class AWSEC2ComputeServiceLiveTest extends EC2ComputeServiceLiveTest { } InstanceClient instanceClient = EC2Client.class.cast(context.getProviderSpecificContext().getApi()) - .getInstanceServices(); + .getInstanceServices(); String group = this.group + "g"; TemplateOptions options = client.templateOptions(); - options.as(AWSEC2TemplateOptions.class).subnetId(subnetId); + options.as(AWSEC2TemplateOptions.class).subnetId(subnetId).spotPrice(0.3f); String startedId = null; String nodeId = null; diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/functions/SpotInstanceRequestToAWSRunningInstanceTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/functions/SpotInstanceRequestToAWSRunningInstanceTest.java new file mode 100644 index 0000000000..e4703bbedb --- /dev/null +++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/functions/SpotInstanceRequestToAWSRunningInstanceTest.java @@ -0,0 +1,74 @@ +/** + * + * Copyright (C) 2010 Cloud Conscious, LLC. .info@cloudconscious.com(" + * + * ==================================================================== + * 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.testng.Assert.assertEquals; + +import org.jclouds.aws.ec2.domain.AWSRunningInstance; +import org.jclouds.aws.ec2.domain.LaunchSpecification; +import org.jclouds.aws.ec2.domain.MonitoringState; +import org.jclouds.aws.ec2.domain.SpotInstanceRequest; +import org.jclouds.aws.ec2.functions.SpotInstanceRequestToAWSRunningInstance; +import org.jclouds.date.internal.SimpleDateFormatDateService; +import org.jclouds.ec2.domain.InstanceState; +import org.testng.annotations.Test; + +/** + * Tests behavior of {@code SpotInstanceRequestToAWSRunningInstance} + * + * @author Adrian Cole + */ +// NOTE:without testName, this will not call @Before* and fail w/NPE during surefire +@Test(groups = "unit", testName = "SpotInstanceRequestToAWSRunningInstanceTest") +public class SpotInstanceRequestToAWSRunningInstanceTest { + + public void testConvert() { + + SpotInstanceRequest input = SpotInstanceRequest.builder().region("us-east-1").id("sir-228e6406") + .spotPrice(0.001f).type(SpotInstanceRequest.Type.ONE_TIME).state(SpotInstanceRequest.State.OPEN) + .launchSpecification( + LaunchSpecification.builder().imageId("ami-595a0a1c").groupId("default").instanceType( + "m1.large").mapNewVolumeToDevice("/dev/sda1", 1, true).mapEBSSnapshotToDevice( + "/dev/sda2", "snap-1ea27576", 1, true).mapEphemeralDeviceToDevice("/dev/sda3", "vre1") + .monitoringEnabled(false).build()).createTime( + new SimpleDateFormatDateService().iso8601DateParse("2011-03-08T03:30:36.000Z")) + .productDescription("Linux/UNIX").build(); + + assertEquals(new SpotInstanceRequestToAWSRunningInstance().apply(input), AWSRunningInstance.builder().region( + "us-east-1").instanceId("sir-228e6406").spotInstanceRequestId("sir-228e6406").instanceState( + InstanceState.PENDING).imageId("ami-595a0a1c").groupId("default").instanceType("m1.large") + .monitoringState(MonitoringState.PENDING).build()); + } + + public void testConvertWhenNotOpenReturnsNull() { + + assertEquals(new SpotInstanceRequestToAWSRunningInstance().apply(SpotInstanceRequest.builder() + .region("us-east-1").id("sir-228e6406").type(SpotInstanceRequest.Type.ONE_TIME).state( + SpotInstanceRequest.State.ACTIVE).build()), null); + + assertEquals(new SpotInstanceRequestToAWSRunningInstance().apply(SpotInstanceRequest.builder() + .region("us-east-1").id("sir-228e6406").type(SpotInstanceRequest.Type.ONE_TIME).state( + SpotInstanceRequest.State.CANCELLED).build()), null); + + assertEquals(new SpotInstanceRequestToAWSRunningInstance().apply(SpotInstanceRequest.builder() + .region("us-east-1").id("sir-228e6406").type(SpotInstanceRequest.Type.ONE_TIME).state( + SpotInstanceRequest.State.UNRECOGNIZED).build()), null); + } +} 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 5e95f7c655..97b9947f7d 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 @@ -41,6 +41,7 @@ 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.compute.AWSEC2TemplateOptions; import org.jclouds.aws.ec2.domain.AWSRunningInstance; import org.jclouds.compute.ComputeServiceContext; import org.jclouds.compute.ComputeServiceContextFactory; @@ -49,10 +50,7 @@ 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; @@ -84,7 +82,7 @@ public class AWSKeyPairClientLiveTest { protected void setupCredentials() { identity = checkNotNull(System.getProperty("test." + provider + ".identity"), "test." + provider + ".identity"); credential = checkNotNull(System.getProperty("test." + provider + ".credential"), "test." + provider - + ".credential"); + + ".credential"); endpoint = System.getProperty("test." + provider + ".endpoint", null); apiversion = System.getProperty("test." + provider + ".apiversion", null); } @@ -106,8 +104,8 @@ public class AWSKeyPairClientLiveTest { public void setupClient() { setupCredentials(); Properties overrides = setupProperties(); - computeContext = new ComputeServiceContextFactory().createContext(provider, - ImmutableSet. of(new Log4JLoggingModule(), new JschSshClientModule()), overrides); + computeContext = new ComputeServiceContextFactory().createContext(provider, ImmutableSet. of( + new Log4JLoggingModule(), new JschSshClientModule()), overrides); context = computeContext.getProviderSpecificContext(); client = context.getApi().getKeyPairServices(); } @@ -116,19 +114,19 @@ public class AWSKeyPairClientLiveTest { Map keyPair = ComputeTestUtils.setupKeyPair(); - InstanceClient instanceClient = EC2Client.class.cast(context.getApi()).getInstanceServices(); + AWSInstanceClient instanceClient = AWSEC2Client.class.cast(context.getApi()).getInstanceServices(); String group = PREFIX + "unssh"; computeContext.getComputeService().destroyNodesMatching(inGroup(group)); TemplateOptions options = computeContext.getComputeService().templateOptions(); - options.authorizePublicKey(keyPair.get("public")); + options.authorizePublicKey(keyPair.get("public")).as(AWSEC2TemplateOptions.class).spotPrice(0.3f); ComputeServiceContext noSshContext = null; try { - noSshContext = new ComputeServiceContextFactory().createContext(provider, - ImmutableSet.of(new Log4JLoggingModule()), setupProperties()); + noSshContext = new ComputeServiceContextFactory().createContext(provider, ImmutableSet + .of(new Log4JLoggingModule()), setupProperties()); Set nodes = noSshContext.getComputeService().createNodesInGroup(group, 1, options); @@ -136,18 +134,19 @@ public class AWSKeyPairClientLiveTest { assert first.getCredentials() != null : first; assert first.getCredentials().identity != null : first; - AWSRunningInstance instance = AWSRunningInstance.class - .cast(getInstance(instanceClient, first.getProviderId())); + AWSRunningInstance instance = getInstance(instanceClient, first.getProviderId()); + assert instance.getSpotInstanceRequestId() != null : instance; 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)); + .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"); @@ -235,9 +234,8 @@ 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; + protected AWSRunningInstance getInstance(AWSInstanceClient instanceClient, String id) { + return getOnlyElement(getOnlyElement(instanceClient.describeInstancesInRegion(null, id))); } @AfterTest