Issue 308: added compute service hooks for spot instances

This commit is contained in:
Adrian Cole 2011-03-08 13:48:04 -08:00
parent af0cff189c
commit b0c4b9be9a
25 changed files with 859 additions and 191 deletions

View File

@ -25,7 +25,6 @@ import static org.jclouds.ec2.reference.EC2Constants.PROPERTY_EC2_AMI_OWNERS;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.Map; import java.util.Map;
import java.util.concurrent.TimeUnit;
import javax.inject.Named; import javax.inject.Named;
import javax.inject.Singleton; 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.InstanceState;
import org.jclouds.ec2.domain.KeyPair; import org.jclouds.ec2.domain.KeyPair;
import org.jclouds.ec2.domain.RunningInstance; 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.RestContext;
import org.jclouds.rest.internal.RestContextImpl; import org.jclouds.rest.internal.RestContextImpl;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Splitter; import com.google.common.base.Splitter;
import com.google.common.base.Supplier; import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
@ -77,11 +73,11 @@ import com.google.inject.TypeLiteral;
public class EC2ComputeServiceDependenciesModule extends AbstractModule { public class EC2ComputeServiceDependenciesModule extends AbstractModule {
public static final Map<InstanceState, NodeState> instanceToNodeState = ImmutableMap public static final Map<InstanceState, NodeState> instanceToNodeState = ImmutableMap
.<InstanceState, NodeState> builder().put(InstanceState.PENDING, NodeState.PENDING) .<InstanceState, NodeState> builder().put(InstanceState.PENDING, NodeState.PENDING).put(
.put(InstanceState.RUNNING, NodeState.RUNNING).put(InstanceState.SHUTTING_DOWN, NodeState.PENDING) InstanceState.RUNNING, NodeState.RUNNING).put(InstanceState.SHUTTING_DOWN, NodeState.PENDING).put(
.put(InstanceState.TERMINATED, NodeState.TERMINATED).put(InstanceState.STOPPING, NodeState.PENDING) InstanceState.TERMINATED, NodeState.TERMINATED).put(InstanceState.STOPPING, NodeState.PENDING)
.put(InstanceState.STOPPED, NodeState.SUSPENDED).put(InstanceState.UNRECOGNIZED, NodeState.UNRECOGNIZED) .put(InstanceState.STOPPED, NodeState.SUSPENDED).put(InstanceState.UNRECOGNIZED, NodeState.UNRECOGNIZED)
.build(); .build();
@Singleton @Singleton
@Provides @Provides
@ -89,13 +85,6 @@ public class EC2ComputeServiceDependenciesModule extends AbstractModule {
return instanceToNodeState; return instanceToNodeState;
} }
@Provides
@Singleton
@Named("PRESENT")
protected Predicate<RunningInstance> instancePresent(InstancePresent present) {
return new RetryablePredicate<RunningInstance>(present, 5000, 200, TimeUnit.MILLISECONDS);
}
@Override @Override
protected void configure() { protected void configure() {
bind(TemplateBuilder.class).to(EC2TemplateBuilderImpl.class); bind(TemplateBuilder.class).to(EC2TemplateBuilderImpl.class);

View File

@ -73,7 +73,7 @@ public class RunningInstanceToNodeMetadata implements Function<RunningInstance,
protected final Map<InstanceState, NodeState> instanceToNodeState; protected final Map<InstanceState, NodeState> instanceToNodeState;
@Inject @Inject
RunningInstanceToNodeMetadata(Map<InstanceState, NodeState> instanceToNodeState, protected RunningInstanceToNodeMetadata(Map<InstanceState, NodeState> instanceToNodeState,
Map<String, Credentials> credentialStore, Map<RegionAndName, Image> instanceToImage, Map<String, Credentials> credentialStore, Map<RegionAndName, Image> instanceToImage,
@Memoized Supplier<Set<? extends Location>> locations, @Memoized Supplier<Set<? extends Hardware>> hardware) { @Memoized Supplier<Set<? extends Location>> locations, @Memoized Supplier<Set<? extends Hardware>> hardware) {
this.locations = checkNotNull(locations, "locations"); this.locations = checkNotNull(locations, "locations");
@ -85,13 +85,14 @@ public class RunningInstanceToNodeMetadata implements Function<RunningInstance,
@Override @Override
public NodeMetadata apply(RunningInstance instance) { public NodeMetadata apply(RunningInstance instance) {
if (instance == null || instance.getId() == null)
return null;
NodeMetadataBuilder builder = new NodeMetadataBuilder(); NodeMetadataBuilder builder = new NodeMetadataBuilder();
String providerId = checkNotNull(instance, "instance").getId(); builder.providerId(instance.getId());
builder.providerId(providerId); builder.id(instance.getRegion() + "/" + instance.getId());
builder.id(instance.getRegion() + "/" + providerId);
String group = getGroupForInstance(instance); String group = getGroupForInstance(instance);
builder.group(group); builder.group(group);
builder.credentials(credentialStore.get("node#" + instance.getRegion() + "/" + providerId)); addCredentialsForInstance(builder, instance);
builder.state(instanceToNodeState.get(instance.getInstanceState())); builder.state(instanceToNodeState.get(instance.getInstanceState()));
builder.publicAddresses(NullSafeCollections.nullSafeSet(instance.getIpAddress())); builder.publicAddresses(NullSafeCollections.nullSafeSet(instance.getIpAddress()));
builder.privateAddresses(NullSafeCollections.nullSafeSet(instance.getPrivateIpAddress())); builder.privateAddresses(NullSafeCollections.nullSafeSet(instance.getPrivateIpAddress()));
@ -104,21 +105,25 @@ public class RunningInstanceToNodeMetadata implements Function<RunningInstance,
RegionAndName regionAndName = new RegionAndName(instance.getRegion(), instance.getImageId()); RegionAndName regionAndName = new RegionAndName(instance.getRegion(), instance.getImageId());
try { try {
Image image = instanceToImage.get(regionAndName); Image image = instanceToImage.get(regionAndName);
if (image != null) if (image != null)
builder.operatingSystem(image.getOperatingSystem()); builder.operatingSystem(image.getOperatingSystem());
} } catch (NullPointerException e) {
catch (NullPointerException e) { // The instanceToImage Map may throw NullPointerException (actually subclass
// The instanceToImage Map may throw NullPointerException (actually subclass NullOutputException) if the // NullOutputException) if the
// computing Function returns a null value. // computing Function returns a null value.
// //
// See the following for more information: // See the following for more information:
// MapMaker.makeComputingMap() // MapMaker.makeComputingMap()
// RegionAndIdToImage.apply() // RegionAndIdToImage.apply()
} }
return builder.build(); return builder.build();
} }
protected void addCredentialsForInstance(NodeMetadataBuilder builder, RunningInstance instance) {
builder.credentials(credentialStore.get("node#" + instance.getRegion() + "/" + instance.getId()));
}
protected Hardware parseHardware(final RunningInstance instance) { protected Hardware parseHardware(final RunningInstance instance) {
Hardware hardware = getHardwareForInstance(instance); Hardware hardware = getHardwareForInstance(instance);
@ -171,9 +176,8 @@ public class RunningInstanceToNodeMetadata implements Function<RunningInstance,
} catch (NoSuchElementException e) { } catch (NoSuchElementException e) {
logger.debug("no group parsed from %s's security groups: %s", instance.getId(), instance.getGroupIds()); logger.debug("no group parsed from %s's security groups: %s", instance.getId(), instance.getGroupIds());
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
logger logger.debug("too many groups match %s; %s's security groups: %s", "jclouds#", instance.getId(), instance
.debug("too many groups match %s; %s's security groups: %s", "jclouds#", instance.getId(), instance .getGroupIds());
.getGroupIds());
} }
return group; return group;
} }
@ -203,6 +207,8 @@ public class RunningInstanceToNodeMetadata implements Function<RunningInstance,
} }
private Location findLocationWithId(final String locationId) { private Location findLocationWithId(final String locationId) {
if (locationId == null)
return null;
try { try {
Location location = Iterables.find(locations.get(), new Predicate<Location>() { Location location = Iterables.find(locations.get(), new Predicate<Location>() {

View File

@ -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; import java.util.NoSuchElementException;
@ -25,7 +27,7 @@ import javax.annotation.Resource;
import javax.inject.Singleton; import javax.inject.Singleton;
import org.jclouds.ec2.EC2Client; 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.logging.Logger;
import org.jclouds.rest.ResourceNotFoundException; import org.jclouds.rest.ResourceNotFoundException;
@ -34,13 +36,11 @@ import com.google.common.collect.Iterables;
import com.google.inject.Inject; import com.google.inject.Inject;
/** /**
*
* Tests to see if a task succeeds.
* *
* @author Adrian Cole * @author Adrian Cole
*/ */
@Singleton @Singleton
public class InstancePresent implements Predicate<RunningInstance> { public class InstancePresent implements Predicate<RegionAndName> {
private final EC2Client client; private final EC2Client client;
@ -49,13 +49,13 @@ public class InstancePresent implements Predicate<RunningInstance> {
@Inject @Inject
public InstancePresent(EC2Client client) { public InstancePresent(EC2Client client) {
this.client = client; this.client = checkNotNull(client, "client");
} }
public boolean apply(RunningInstance instance) { public boolean apply(RegionAndName instance) {
logger.trace("looking for instance %s", instance); logger.trace("looking for instance %s/%s", instance.getRegion(), instance.getName());
try { try {
instance = refresh(instance); refresh(instance);
return true; return true;
} catch (ResourceNotFoundException e) { } catch (ResourceNotFoundException e) {
return false; return false;
@ -64,8 +64,8 @@ public class InstancePresent implements Predicate<RunningInstance> {
} }
} }
private RunningInstance refresh(RunningInstance instance) { protected void refresh(RegionAndName instance) {
return Iterables.getOnlyElement(Iterables.getOnlyElement(client.getInstanceServices().describeInstancesInRegion( Iterables.getOnlyElement(Iterables.getOnlyElement(client.getInstanceServices().describeInstancesInRegion(
instance.getRegion(), instance.getId()))); instance.getRegion(), instance.getName())));
} }
} }

View File

@ -19,10 +19,10 @@
package org.jclouds.ec2.compute.strategy; 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.all;
import static com.google.common.collect.Iterables.transform; 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.getZoneFromLocationOrNull;
import static org.jclouds.ec2.compute.util.EC2ComputeUtils.instanceToId;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -44,6 +44,8 @@ import org.jclouds.compute.strategy.CreateNodesInGroupThenAddToSet;
import org.jclouds.compute.util.ComputeUtils; import org.jclouds.compute.util.ComputeUtils;
import org.jclouds.domain.Credentials; import org.jclouds.domain.Credentials;
import org.jclouds.ec2.EC2Client; 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.domain.RunningInstance;
import org.jclouds.ec2.options.RunInstancesOptions; import org.jclouds.ec2.options.RunInstancesOptions;
import org.jclouds.logging.Logger; import org.jclouds.logging.Logger;
@ -51,7 +53,6 @@ import org.jclouds.logging.Logger;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.base.Joiner; import com.google.common.base.Joiner;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Multimap; import com.google.common.collect.Multimap;
@ -76,49 +77,58 @@ public class EC2CreateNodesInGroupThenAddToSet implements CreateNodesInGroupThen
final Function<RunningInstance, NodeMetadata> runningInstanceToNodeMetadata; final Function<RunningInstance, NodeMetadata> runningInstanceToNodeMetadata;
@VisibleForTesting @VisibleForTesting
final ComputeUtils utils; final ComputeUtils utils;
final Predicate<RunningInstance> instancePresent; final InstancePresent instancePresent;
final Function<RunningInstance, Credentials> instanceToCredentials; final Function<RunningInstance, Credentials> instanceToCredentials;
final Map<String, Credentials> credentialStore; final Map<String, Credentials> credentialStore;
final Provider<TemplateBuilder> templateBuilderProvider; final Provider<TemplateBuilder> templateBuilderProvider;
@Inject @Inject
EC2CreateNodesInGroupThenAddToSet( protected EC2CreateNodesInGroupThenAddToSet(
EC2Client client, EC2Client client,
Provider<TemplateBuilder> templateBuilderProvider, Provider<TemplateBuilder> templateBuilderProvider,
CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions createKeyPairAndSecurityGroupsAsNeededAndReturncustomize, CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions createKeyPairAndSecurityGroupsAsNeededAndReturncustomize,
@Named("PRESENT") Predicate<RunningInstance> instancePresent, InstancePresent instancePresent, Function<RunningInstance, NodeMetadata> runningInstanceToNodeMetadata,
Function<RunningInstance, NodeMetadata> runningInstanceToNodeMetadata, Function<RunningInstance, Credentials> instanceToCredentials, Map<String, Credentials> credentialStore,
Function<RunningInstance, Credentials> instanceToCredentials, Map<String, Credentials> credentialStore, ComputeUtils utils) {
ComputeUtils utils) { this.client = checkNotNull(client, "client");
this.client = client; this.templateBuilderProvider = checkNotNull(templateBuilderProvider, "templateBuilderProvider");
this.templateBuilderProvider = templateBuilderProvider; this.instancePresent = checkNotNull(instancePresent, "instancePresent");
this.instancePresent = instancePresent; this.createKeyPairAndSecurityGroupsAsNeededAndReturncustomize = checkNotNull(
this.createKeyPairAndSecurityGroupsAsNeededAndReturncustomize = createKeyPairAndSecurityGroupsAsNeededAndReturncustomize; createKeyPairAndSecurityGroupsAsNeededAndReturncustomize,
this.runningInstanceToNodeMetadata = runningInstanceToNodeMetadata; "createKeyPairAndSecurityGroupsAsNeededAndReturncustomize");
this.instanceToCredentials = instanceToCredentials; this.runningInstanceToNodeMetadata = checkNotNull(runningInstanceToNodeMetadata, "runningInstanceToNodeMetadata");
this.credentialStore = credentialStore; this.instanceToCredentials = checkNotNull(instanceToCredentials, "instanceToCredentials");
this.utils = utils; this.credentialStore = checkNotNull(credentialStore, "credentialStore");
this.utils = checkNotNull(utils, "utils");
} }
public static Function<RunningInstance, RegionAndName> instanceToRegionAndName = new Function<RunningInstance, RegionAndName>() {
@Override
public RegionAndName apply(RunningInstance from) {
return new RegionAndName(from.getRegion(), from.getId());
}
};
@Override @Override
public Map<?, Future<Void>> execute(String group, int count, Template template, Set<NodeMetadata> goodNodes, public Map<?, Future<Void>> execute(String group, int count, Template template, Set<NodeMetadata> goodNodes,
Map<NodeMetadata, Exception> badNodes, Multimap<NodeMetadata, CustomizationResponse> customizationResponses) { Map<NodeMetadata, Exception> badNodes, Multimap<NodeMetadata, CustomizationResponse> customizationResponses) {
// ensure we don't mutate the input template // ensure we don't mutate the input template
template = templateBuilderProvider.get().fromTemplate(template).build(); template = templateBuilderProvider.get().fromTemplate(template).build();
Iterable<? extends RunningInstance> started = createKeyPairAndSecurityGroupsAsNeededThenRunInstances(group, Iterable<? extends RunningInstance> started = createKeyPairAndSecurityGroupsAsNeededThenRunInstances(group,
count, template); count, template);
Iterable<String> ids = transform(started, instanceToId);
Iterable<RegionAndName> ids = transform(started, instanceToRegionAndName);
String idsString = Joiner.on(',').join(ids); String idsString = Joiner.on(',').join(ids);
if (Iterables.size(ids) > 0) { if (Iterables.size(ids) > 0) {
logger.debug("<< started instances(%s)", idsString); logger.debug("<< started instances(%s)", idsString);
all(started, instancePresent); all(ids, instancePresent);
logger.debug("<< present instances(%s)", idsString); logger.debug("<< present instances(%s)", idsString);
populateCredentials(started); populateCredentials(started);
} }
return utils.customizeNodesAndAddToGoodMapOrPutExceptionIntoBadMap(template.getOptions(), return utils.customizeNodesAndAddToGoodMapOrPutExceptionIntoBadMap(template.getOptions(), transform(started,
transform(started, runningInstanceToNodeMetadata), goodNodes, badNodes, customizationResponses); runningInstanceToNodeMetadata), goodNodes, badNodes, customizationResponses);
} }
protected void populateCredentials(Iterable<? extends RunningInstance> started) { protected void populateCredentials(Iterable<? extends RunningInstance> started) {
@ -131,19 +141,20 @@ public class EC2CreateNodesInGroupThenAddToSet implements CreateNodesInGroupThen
if (credentials != null) if (credentials != null)
for (RunningInstance instance : started) for (RunningInstance instance : started)
credentialStore.put("node#" + instance.getRegion() + "/" + instance.getId(), credentials); credentialStore.put("node#" + instance.getRegion() + "/" + instance.getId(), credentials);
} }
// TODO write test for this // TODO write test for this
@VisibleForTesting protected Iterable<? extends RunningInstance> createKeyPairAndSecurityGroupsAsNeededThenRunInstances(String group,
Iterable<? extends RunningInstance> createKeyPairAndSecurityGroupsAsNeededThenRunInstances(String group, int count, int count, Template template) {
Template template) {
String region = AWSUtils.getRegionFromLocationOrNull(template.getLocation()); String region = AWSUtils.getRegionFromLocationOrNull(template.getLocation());
String zone = getZoneFromLocationOrNull(template.getLocation()); String zone = getZoneFromLocationOrNull(template.getLocation());
RunInstancesOptions instanceOptions = createKeyPairAndSecurityGroupsAsNeededAndReturncustomize.execute(region, RunInstancesOptions instanceOptions = createKeyPairAndSecurityGroupsAsNeededAndReturncustomize.execute(region,
group, template); group, template);
return createNodesInRegionAndZone(region, zone, count, template, instanceOptions);
}
protected Iterable<? extends RunningInstance> createNodesInRegionAndZone(String region, String zone, int count,
Template template, RunInstancesOptions instanceOptions) {
int countStarted = 0; int countStarted = 0;
int tries = 0; int tries = 0;
Iterable<? extends RunningInstance> started = ImmutableSet.<RunningInstance> of(); Iterable<? extends RunningInstance> started = ImmutableSet.<RunningInstance> of();
@ -151,12 +162,10 @@ public class EC2CreateNodesInGroupThenAddToSet implements CreateNodesInGroupThen
while (countStarted < count && tries++ < count) { while (countStarted < count && tries++ < count) {
if (logger.isDebugEnabled()) if (logger.isDebugEnabled())
logger.debug(">> running %d instance region(%s) zone(%s) ami(%s) params(%s)", count - countStarted, region, 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 = Iterables.concat(started, client.getInstanceServices().runInstancesInRegion(region, zone,
started, template.getImage().getProviderId(), 1, count - countStarted, instanceOptions));
client.getInstanceServices().runInstancesInRegion(region, zone, template.getImage().getProviderId(), 1,
count - countStarted, instanceOptions));
countStarted = Iterables.size(started); countStarted = Iterables.size(started);
if (countStarted < count) if (countStarted < count)

View File

@ -19,6 +19,8 @@
package org.jclouds.ec2.compute.strategy; package org.jclouds.ec2.compute.strategy;
import static com.google.common.base.Preconditions.checkNotNull;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
@ -41,14 +43,13 @@ public class EC2DestroyNodeStrategy implements DestroyNodeStrategy {
@Resource @Resource
@Named(ComputeServiceConstants.COMPUTE_LOGGER) @Named(ComputeServiceConstants.COMPUTE_LOGGER)
protected Logger logger = Logger.NULL; protected Logger logger = Logger.NULL;
protected final EC2Client ec2Client; protected final EC2Client client;
protected final GetNodeMetadataStrategy getNode; protected final GetNodeMetadataStrategy getNode;
@Inject @Inject
protected EC2DestroyNodeStrategy(EC2Client ec2Client, protected EC2DestroyNodeStrategy(EC2Client client, GetNodeMetadataStrategy getNode) {
GetNodeMetadataStrategy getNodeMetadataStrategy) { this.client = checkNotNull(client, "client");
this.ec2Client = ec2Client; this.getNode = checkNotNull(getNode, "getNode");
this.getNode = getNodeMetadataStrategy;
} }
@Override @Override
@ -56,8 +57,11 @@ public class EC2DestroyNodeStrategy implements DestroyNodeStrategy {
String[] parts = AWSUtils.parseHandle(id); String[] parts = AWSUtils.parseHandle(id);
String region = parts[0]; String region = parts[0];
String instanceId = parts[1]; String instanceId = parts[1];
ec2Client.getInstanceServices().terminateInstancesInRegion(region, destroyInstanceInRegion(region, instanceId);
instanceId);
return getNode.getNode(id); return getNode.getNode(id);
} }
protected void destroyInstanceInRegion(String region, String instanceId) {
client.getInstanceServices().terminateInstancesInRegion(region, instanceId);
}
} }

View File

@ -19,6 +19,7 @@
package org.jclouds.ec2.compute.strategy; package org.jclouds.ec2.compute.strategy;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.Iterables.getOnlyElement; import static com.google.common.collect.Iterables.getOnlyElement;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
@ -31,7 +32,6 @@ import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.strategy.GetNodeMetadataStrategy; import org.jclouds.compute.strategy.GetNodeMetadataStrategy;
import org.jclouds.ec2.EC2Client; import org.jclouds.ec2.EC2Client;
import org.jclouds.ec2.domain.RunningInstance; import org.jclouds.ec2.domain.RunningInstance;
import org.jclouds.ec2.services.InstanceClient;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
@ -48,28 +48,27 @@ public class EC2GetNodeMetadataStrategy implements GetNodeMetadataStrategy {
@Inject @Inject
protected EC2GetNodeMetadataStrategy(EC2Client client, protected EC2GetNodeMetadataStrategy(EC2Client client,
Function<RunningInstance, NodeMetadata> runningInstanceToNodeMetadata) { Function<RunningInstance, NodeMetadata> runningInstanceToNodeMetadata) {
this.client = client; this.client = checkNotNull(client, "client");
this.runningInstanceToNodeMetadata = runningInstanceToNodeMetadata; this.runningInstanceToNodeMetadata = checkNotNull(runningInstanceToNodeMetadata, "runningInstanceToNodeMetadata");
} }
@Override @Override
public NodeMetadata getNode(String id) { public NodeMetadata getNode(String id) {
checkNotNull(id, "id");
String[] parts = AWSUtils.parseHandle(id); String[] parts = AWSUtils.parseHandle(id);
String region = parts[0]; String region = parts[0];
String instanceId = parts[1]; String instanceId = parts[1];
try { try {
RunningInstance runningInstance = getOnlyElement(getAllRunningInstancesInRegion(client.getInstanceServices(), RunningInstance runningInstance = getRunningInstanceInRegion(region, instanceId);
region, instanceId));
return runningInstanceToNodeMetadata.apply(runningInstance); return runningInstanceToNodeMetadata.apply(runningInstance);
} catch (NoSuchElementException e) { } catch (NoSuchElementException e) {
return null; return null;
} }
} }
public static Iterable<RunningInstance> getAllRunningInstancesInRegion(InstanceClient client, String region, public RunningInstance getRunningInstanceInRegion(String region, String id) {
String id) { return getOnlyElement(Iterables.concat(client.getInstanceServices().describeInstancesInRegion(region, id)));
return Iterables.concat(client.describeInstancesInRegion(region, id));
} }
} }

View File

@ -19,10 +19,12 @@
package org.jclouds.ec2.compute.strategy; 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.concat;
import static com.google.common.collect.Iterables.filter; import static com.google.common.collect.Iterables.filter;
import static com.google.common.collect.Iterables.transform; import static com.google.common.collect.Iterables.transform;
import static com.google.common.collect.Sets.newLinkedHashSet;
import static org.jclouds.concurrent.FutureIterables.transformParallel; import static org.jclouds.concurrent.FutureIterables.transformParallel;
import java.util.Set; import java.util.Set;
@ -35,19 +37,20 @@ import javax.inject.Named;
import javax.inject.Singleton; import javax.inject.Singleton;
import org.jclouds.Constants; 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.ComputeMetadata;
import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.predicates.NodePredicates; import org.jclouds.compute.predicates.NodePredicates;
import org.jclouds.compute.reference.ComputeServiceConstants; import org.jclouds.compute.reference.ComputeServiceConstants;
import org.jclouds.compute.strategy.ListNodesStrategy; 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.location.Region;
import org.jclouds.logging.Logger; import org.jclouds.logging.Logger;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.base.Predicate; 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) @Named(ComputeServiceConstants.COMPUTE_LOGGER)
protected Logger logger = Logger.NULL; protected Logger logger = Logger.NULL;
private final EC2AsyncClient client; protected final EC2AsyncClient client;
private final Set<String> regions; protected final Set<String> regions;
private final Function<RunningInstance, NodeMetadata> runningInstanceToNodeMetadata; protected final Function<RunningInstance, NodeMetadata> runningInstanceToNodeMetadata;
private final ExecutorService executor; protected final ExecutorService executor;
@Inject @Inject
protected EC2ListNodesStrategy(EC2AsyncClient client, @Region Set<String> regions, protected EC2ListNodesStrategy(EC2AsyncClient client, @Region Set<String> regions,
Function<RunningInstance, NodeMetadata> runningInstanceToNodeMetadata, Function<RunningInstance, NodeMetadata> runningInstanceToNodeMetadata,
@Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor) { @Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor) {
this.client = client; this.client = checkNotNull(client, "client");
this.regions = regions; this.regions = checkNotNull(regions, "regions");
this.runningInstanceToNodeMetadata = runningInstanceToNodeMetadata; this.runningInstanceToNodeMetadata = checkNotNull(runningInstanceToNodeMetadata, "runningInstanceToNodeMetadata");
this.executor = executor; this.executor = checkNotNull(executor, "executor");
} }
@Override @Override
@ -81,19 +84,25 @@ public class EC2ListNodesStrategy implements ListNodesStrategy {
@Override @Override
public Set<? extends NodeMetadata> listDetailsOnNodesMatching(Predicate<ComputeMetadata> filter) { public Set<? extends NodeMetadata> listDetailsOnNodesMatching(Predicate<ComputeMetadata> filter) {
Iterable<? extends RunningInstance> instances = pollRunningInstances();
Iterable<? extends NodeMetadata> nodes = filter(transform(filter(instances, notNull()),
runningInstanceToNodeMetadata), and(notNull(), filter));
return ImmutableSet.copyOf(nodes);
}
protected Iterable<? extends RunningInstance> pollRunningInstances() {
Iterable<? extends Set<? extends Reservation<? extends RunningInstance>>> reservations = transformParallel( Iterable<? extends Set<? extends Reservation<? extends RunningInstance>>> reservations = transformParallel(
regions, new Function<String, Future<Set<? extends Reservation<? extends RunningInstance>>>>() { regions, new Function<String, Future<Set<? extends Reservation<? extends RunningInstance>>>>() {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public Future<Set<? extends Reservation<? extends RunningInstance>>> apply(String from) { public Future<Set<? extends Reservation<? extends RunningInstance>>> apply(String from) {
return (Future<Set<? extends Reservation<? extends RunningInstance>>>) client.getInstanceServices().describeInstancesInRegion(from); return (Future<Set<? extends Reservation<? extends RunningInstance>>>) client
} .getInstanceServices().describeInstancesInRegion(from);
}
}, executor, null, logger, "reservations"); }, executor, null, logger, "reservations");
Iterable<? extends RunningInstance> instances = concat(concat(reservations)); return concat(concat(reservations));
Iterable<? extends NodeMetadata> nodes = filter(transform(instances, runningInstanceToNodeMetadata), filter);
return newLinkedHashSet(nodes);
} }
} }

View File

@ -21,12 +21,9 @@ package org.jclouds.ec2.compute.util;
import javax.inject.Singleton; import javax.inject.Singleton;
import org.jclouds.ec2.domain.RunningInstance;
import org.jclouds.domain.Location; import org.jclouds.domain.Location;
import org.jclouds.domain.LocationScope; import org.jclouds.domain.LocationScope;
import com.google.common.base.Function;
/** /**
* *
* @author Adrian Cole * @author Adrian Cole
@ -34,13 +31,6 @@ import com.google.common.base.Function;
@Singleton @Singleton
public class EC2ComputeUtils { public class EC2ComputeUtils {
public static Function<RunningInstance, String> instanceToId = new Function<RunningInstance, String>() {
@Override
public String apply(RunningInstance from) {
return from.getId();
}
};
public static String getZoneFromLocationOrNull(Location location) { public static String getZoneFromLocationOrNull(Location location) {
return location.getScope() == LocationScope.ZONE ? location.getId() : null; return location.getScope() == LocationScope.ZONE ? location.getId() : null;
} }

View File

@ -39,6 +39,9 @@ import com.google.common.collect.Sets;
* @author Adrian Cole * @author Adrian Cole
*/ */
public class RunningInstance implements Comparable<RunningInstance> { public class RunningInstance implements Comparable<RunningInstance> {
public static Builder builder() {
return new Builder();
}
public static class Builder { public static class Builder {
protected String region; protected String region;
@ -265,8 +268,8 @@ public class RunningInstance implements Comparable<RunningInstance> {
this.ipAddress = ipAddress; this.ipAddress = ipAddress;
this.kernelId = kernelId; this.kernelId = kernelId;
this.keyName = keyName; this.keyName = keyName;
this.launchTime = checkNotNull(launchTime, "launchTime"); this.launchTime = launchTime;// nullable on spot.
this.availabilityZone = checkNotNull(availabilityZone, "availabilityZone"); this.availabilityZone = availabilityZone;// nullable on spot.
this.virtualizationType = virtualizationType; this.virtualizationType = virtualizationType;
this.platform = platform; this.platform = platform;
this.privateDnsName = privateDnsName;// nullable on runinstances. this.privateDnsName = privateDnsName;// nullable on runinstances.

View File

@ -42,8 +42,10 @@ import org.jclouds.domain.Location;
import org.jclouds.domain.LocationBuilder; import org.jclouds.domain.LocationBuilder;
import org.jclouds.domain.LocationScope; import org.jclouds.domain.LocationScope;
import org.jclouds.ec2.EC2Client; import org.jclouds.ec2.EC2Client;
import org.jclouds.ec2.compute.domain.RegionAndName;
import org.jclouds.ec2.compute.functions.RunningInstanceToNodeMetadata; import org.jclouds.ec2.compute.functions.RunningInstanceToNodeMetadata;
import org.jclouds.ec2.compute.options.EC2TemplateOptions; 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.Reservation;
import org.jclouds.ec2.domain.RunningInstance; import org.jclouds.ec2.domain.RunningInstance;
import org.jclouds.ec2.options.RunInstancesOptions; import org.jclouds.ec2.options.RunInstancesOptions;
@ -51,7 +53,6 @@ import org.jclouds.ec2.services.InstanceClient;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Multimap; import com.google.common.collect.Multimap;
@ -61,7 +62,7 @@ import com.google.inject.util.Providers;
* @author Adrian Cole * @author Adrian Cole
*/ */
@Test(groups = "unit") @Test(groups = "unit")
public class EC2RunNodesAndAddToSetStrategyTest { public class EC2CreateNodesInGroupThenAddToSetTest {
@Test @Test
public void testZoneAsALocation() { public void testZoneAsALocation() {
@ -126,10 +127,10 @@ public class EC2RunNodesAndAddToSetStrategyTest {
// simulate a lazy credentials fetch // simulate a lazy credentials fetch
Credentials creds = new Credentials("foo", "bar"); Credentials creds = new Credentials("foo", "bar");
expect(strategy.instanceToCredentials.apply(instance)).andReturn(creds); 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.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(input.template.getOptions()).andReturn(input.options).atLeastOnce();
expect(strategy.runningInstanceToNodeMetadata.apply(instance)).andReturn(nodeMetadata); expect(strategy.runningInstanceToNodeMetadata.apply(instance)).andReturn(nodeMetadata);
@ -220,13 +221,13 @@ public class EC2RunNodesAndAddToSetStrategyTest {
private EC2CreateNodesInGroupThenAddToSet setupStrategy(TemplateBuilder template) { private EC2CreateNodesInGroupThenAddToSet setupStrategy(TemplateBuilder template) {
EC2Client client = createMock(EC2Client.class); EC2Client client = createMock(EC2Client.class);
CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions createKeyPairAndSecurityGroupsAsNeededAndReturncustomize = createMock(CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.class); CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions createKeyPairAndSecurityGroupsAsNeededAndReturncustomize = createMock(CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.class);
Predicate<RunningInstance> instanceStateRunning = createMock(Predicate.class); InstancePresent instancePresent = createMock(InstancePresent.class);
RunningInstanceToNodeMetadata runningInstanceToNodeMetadata = createMock(RunningInstanceToNodeMetadata.class); RunningInstanceToNodeMetadata runningInstanceToNodeMetadata = createMock(RunningInstanceToNodeMetadata.class);
Function<RunningInstance, Credentials> instanceToCredentials = createMock(Function.class); Function<RunningInstance, Credentials> instanceToCredentials = createMock(Function.class);
Map<String, Credentials> credentialStore = createMock(Map.class); Map<String, Credentials> credentialStore = createMock(Map.class);
ComputeUtils utils = createMock(ComputeUtils.class); ComputeUtils utils = createMock(ComputeUtils.class);
return new EC2CreateNodesInGroupThenAddToSet(client, Providers.<TemplateBuilder> of(template), return new EC2CreateNodesInGroupThenAddToSet(client, Providers.<TemplateBuilder> of(template),
createKeyPairAndSecurityGroupsAsNeededAndReturncustomize, instanceStateRunning, createKeyPairAndSecurityGroupsAsNeededAndReturncustomize, instancePresent,
runningInstanceToNodeMetadata, instanceToCredentials, credentialStore, utils); runningInstanceToNodeMetadata, instanceToCredentials, credentialStore, utils);
} }

View File

@ -65,6 +65,8 @@ public class NodePresentAndInIntendedState implements Predicate<NodeMetadata> {
} }
private NodeMetadata refresh(NodeMetadata node) { private NodeMetadata refresh(NodeMetadata node) {
if (node == null || node.getId() == null)
return null;
return client.getNodeMetadata(node.getId()); return client.getNodeMetadata(node.getId());
} }
} }

View File

@ -27,6 +27,7 @@ import java.util.Set;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import org.jclouds.aws.ec2.options.RequestSpotInstancesOptions;
import org.jclouds.compute.options.TemplateOptions; import org.jclouds.compute.options.TemplateOptions;
import org.jclouds.domain.Credentials; import org.jclouds.domain.Credentials;
import org.jclouds.ec2.compute.options.EC2TemplateOptions; import org.jclouds.ec2.compute.options.EC2TemplateOptions;
@ -73,6 +74,10 @@ public class AWSEC2TemplateOptions extends EC2TemplateOptions implements Cloneab
eTo.noPlacementGroup(); eTo.noPlacementGroup();
if (getPlacementGroup() != null) if (getPlacementGroup() != null)
eTo.placementGroup(getPlacementGroup()); 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 String placementGroup = null;
private boolean noPlacementGroup; private boolean noPlacementGroup;
private String subnetId; private String subnetId;
private Float spotPrice;
private RequestSpotInstancesOptions spotOptions = RequestSpotInstancesOptions.NONE;
public static final AWSEC2TemplateOptions NONE = new AWSEC2TemplateOptions(); public static final AWSEC2TemplateOptions NONE = new AWSEC2TemplateOptions();
@ -123,6 +130,22 @@ public class AWSEC2TemplateOptions extends EC2TemplateOptions implements Cloneab
return this; 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 { public static class Builder {
/** /**
* @see EC2TemplateOptions#blockDeviceMappings * @see EC2TemplateOptions#blockDeviceMappings
@ -136,7 +159,7 @@ public class AWSEC2TemplateOptions extends EC2TemplateOptions implements Cloneab
* @see EC2TemplateOptions#mapEBSSnapshotToDeviceName * @see EC2TemplateOptions#mapEBSSnapshotToDeviceName
*/ */
public static AWSEC2TemplateOptions mapEBSSnapshotToDeviceName(String deviceName, String snapshotId, public static AWSEC2TemplateOptions mapEBSSnapshotToDeviceName(String deviceName, String snapshotId,
@Nullable Integer sizeInGib, boolean deleteOnTermination) { @Nullable Integer sizeInGib, boolean deleteOnTermination) {
AWSEC2TemplateOptions options = new AWSEC2TemplateOptions(); AWSEC2TemplateOptions options = new AWSEC2TemplateOptions();
return options.mapEBSSnapshotToDeviceName(deviceName, snapshotId, sizeInGib, deleteOnTermination); return options.mapEBSSnapshotToDeviceName(deviceName, snapshotId, sizeInGib, deleteOnTermination);
} }
@ -145,7 +168,7 @@ public class AWSEC2TemplateOptions extends EC2TemplateOptions implements Cloneab
* @see EC2TemplateOptions#mapNewVolumeToDeviceName * @see EC2TemplateOptions#mapNewVolumeToDeviceName
*/ */
public static AWSEC2TemplateOptions mapNewVolumeToDeviceName(String deviceName, int sizeInGib, public static AWSEC2TemplateOptions mapNewVolumeToDeviceName(String deviceName, int sizeInGib,
boolean deleteOnTermination) { boolean deleteOnTermination) {
AWSEC2TemplateOptions options = new AWSEC2TemplateOptions(); AWSEC2TemplateOptions options = new AWSEC2TemplateOptions();
return options.mapNewVolumeToDeviceName(deviceName, sizeInGib, deleteOnTermination); 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) { public static AWSEC2TemplateOptions subnetId(String subnetId) {
AWSEC2TemplateOptions options = new AWSEC2TemplateOptions(); AWSEC2TemplateOptions options = new AWSEC2TemplateOptions();
return options.subnetId(subnetId); 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 // methods that only facilitate returning the correct object type
@ -312,9 +351,9 @@ public class AWSEC2TemplateOptions extends EC2TemplateOptions implements Cloneab
@Override @Override
public AWSEC2TemplateOptions mapEBSSnapshotToDeviceName(String deviceName, String snapshotId, Integer sizeInGib, public AWSEC2TemplateOptions mapEBSSnapshotToDeviceName(String deviceName, String snapshotId, Integer sizeInGib,
boolean deleteOnTermination) { boolean deleteOnTermination) {
return AWSEC2TemplateOptions.class.cast(super.mapEBSSnapshotToDeviceName(deviceName, snapshotId, sizeInGib, return AWSEC2TemplateOptions.class.cast(super.mapEBSSnapshotToDeviceName(deviceName, snapshotId, sizeInGib,
deleteOnTermination)); deleteOnTermination));
} }
/** /**
@ -331,7 +370,7 @@ public class AWSEC2TemplateOptions extends EC2TemplateOptions implements Cloneab
@Override @Override
public AWSEC2TemplateOptions mapNewVolumeToDeviceName(String deviceName, int sizeInGib, boolean deleteOnTermination) { public AWSEC2TemplateOptions mapNewVolumeToDeviceName(String deviceName, int sizeInGib, boolean deleteOnTermination) {
return AWSEC2TemplateOptions.class.cast(super 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 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 @Override
public int hashCode() { public int hashCode() {
final int prime = 31; final int prime = 31;
@ -532,6 +585,8 @@ public class AWSEC2TemplateOptions extends EC2TemplateOptions implements Cloneab
result = prime * result + (monitoringEnabled ? 1231 : 1237); result = prime * result + (monitoringEnabled ? 1231 : 1237);
result = prime * result + (noPlacementGroup ? 1231 : 1237); result = prime * result + (noPlacementGroup ? 1231 : 1237);
result = prime * result + ((placementGroup == null) ? 0 : placementGroup.hashCode()); 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()); result = prime * result + ((subnetId == null) ? 0 : subnetId.hashCode());
return result; return result;
} }
@ -554,6 +609,16 @@ public class AWSEC2TemplateOptions extends EC2TemplateOptions implements Cloneab
return false; return false;
} else if (!placementGroup.equals(other.placementGroup)) } else if (!placementGroup.equals(other.placementGroup))
return false; 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 (subnetId == null) {
if (other.subnetId != null) if (other.subnetId != null)
return false; return false;
@ -566,9 +631,10 @@ public class AWSEC2TemplateOptions extends EC2TemplateOptions implements Cloneab
public String toString() { public String toString() {
return "[groupIds=" + getGroupIds() + ", keyPair=" + getKeyPair() + ", noKeyPair=" return "[groupIds=" + getGroupIds() + ", keyPair=" + getKeyPair() + ", noKeyPair="
+ !shouldAutomaticallyCreateKeyPair() + ", monitoringEnabled=" + monitoringEnabled + ", placementGroup=" + !shouldAutomaticallyCreateKeyPair() + ", monitoringEnabled=" + monitoringEnabled + ", placementGroup="
+ placementGroup + ", noPlacementGroup=" + noPlacementGroup + ", subnetId=" + subnetId + ", userData=" + placementGroup + ", noPlacementGroup=" + noPlacementGroup + ", subnetId=" + subnetId + ", userData="
+ Arrays.toString(getUserData()) + ", blockDeviceMappings=" + getBlockDeviceMappings() + "]"; + Arrays.toString(getUserData()) + ", blockDeviceMappings=" + getBlockDeviceMappings() + ", spotPrice="
+ spotPrice + ", spotOptions=" + spotOptions + "]";
} }
} }

View File

@ -22,14 +22,26 @@ package org.jclouds.aws.ec2.compute.config;
import static org.jclouds.compute.domain.OsFamily.AMZN_LINUX; import static org.jclouds.compute.domain.OsFamily.AMZN_LINUX;
import org.jclouds.aws.ec2.compute.AWSEC2TemplateBuilderImpl; 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.AWSEC2ReviseParsedImage;
import org.jclouds.aws.ec2.compute.strategy.CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions; import org.jclouds.aws.ec2.compute.strategy.CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions;
import org.jclouds.aws.ec2.compute.suppliers.AWSEC2HardwareSupplier; import org.jclouds.aws.ec2.compute.suppliers.AWSEC2HardwareSupplier;
import org.jclouds.aws.ec2.compute.suppliers.AWSRegionAndNameToImageSupplier; import org.jclouds.aws.ec2.compute.suppliers.AWSRegionAndNameToImageSupplier;
import org.jclouds.compute.domain.TemplateBuilder; import org.jclouds.compute.domain.TemplateBuilder;
import org.jclouds.ec2.compute.config.EC2ComputeServiceContextModule; 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.internal.EC2TemplateBuilderImpl;
import org.jclouds.ec2.compute.predicates.InstancePresent;
import org.jclouds.ec2.compute.strategy.CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions; 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.strategy.ReviseParsedImage;
import org.jclouds.ec2.compute.suppliers.EC2HardwareSupplier; import org.jclouds.ec2.compute.suppliers.EC2HardwareSupplier;
import org.jclouds.ec2.compute.suppliers.RegionAndNameToImageSupplier; import org.jclouds.ec2.compute.suppliers.RegionAndNameToImageSupplier;
@ -56,6 +68,12 @@ public class AWSEC2ComputeServiceContextModule extends EC2ComputeServiceContextM
bind(EC2HardwareSupplier.class).to(AWSEC2HardwareSupplier.class); bind(EC2HardwareSupplier.class).to(AWSEC2HardwareSupplier.class);
bind(RegionAndNameToImageSupplier.class).to(AWSRegionAndNameToImageSupplier.class); bind(RegionAndNameToImageSupplier.class).to(AWSRegionAndNameToImageSupplier.class);
bind(EC2TemplateBuilderImpl.class).to(AWSEC2TemplateBuilderImpl.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 @Override

View File

@ -0,0 +1,69 @@
/**
*
* 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.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<InstanceState, NodeState> instanceToNodeState,
Map<String, Credentials> credentialStore, Map<RegionAndName, Image> instanceToImage,
@Memoized Supplier<Set<? extends Location>> locations, @Memoized Supplier<Set<? extends Hardware>> 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);
}
}

View File

@ -0,0 +1,56 @@
/**
*
* 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.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()));
}
}

View File

@ -0,0 +1,109 @@
/**
*
* 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.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<TemplateBuilder> templateBuilderProvider,
CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions createKeyPairAndSecurityGroupsAsNeededAndReturncustomize,
AWSEC2InstancePresent instancePresent,
Function<RunningInstance, NodeMetadata> runningInstanceToNodeMetadata,
Function<RunningInstance, Credentials> instanceToCredentials, Map<String, Credentials> 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<? extends RunningInstance> 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;
}
}

View File

@ -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);
}
}
}

View File

@ -0,0 +1,68 @@
/**
*
* 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.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<RunningInstance, NodeMetadata> 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);
}
}

View File

@ -0,0 +1,85 @@
/**
*
* 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.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<String> regions,
Function<RunningInstance, NodeMetadata> 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<? extends RunningInstance> pollRunningInstances() {
Iterable<? extends AWSRunningInstance> spots = filter(transform(concat(transformParallel(regions,
new Function<String, Future<Set<SpotInstanceRequest>>>() {
@SuppressWarnings("unchecked")
@Override
public Future<Set<SpotInstanceRequest>> apply(String from) {
return (Future<Set<SpotInstanceRequest>>) client.getSpotInstanceServices()
.describeSpotInstanceRequestsInRegion(from);
}
}, executor, null, logger, "reservations")), spotConverter), notNull());
return concat(super.pollRunningInstances(), spots);
}
}

View File

@ -42,6 +42,9 @@ import com.google.common.collect.Sets;
* @author Adrian Cole * @author Adrian Cole
*/ */
public class AWSRunningInstance extends RunningInstance { public class AWSRunningInstance extends RunningInstance {
public static Builder builder() {
return new Builder();
}
public static class Builder extends org.jclouds.ec2.domain.RunningInstance.Builder { public static class Builder extends org.jclouds.ec2.domain.RunningInstance.Builder {
private MonitoringState monitoringState; private MonitoringState monitoringState;

View File

@ -0,0 +1,64 @@
/**
*
* 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 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<SpotInstanceRequest, AWSRunningInstance> {
@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();
}
}

View File

@ -23,10 +23,13 @@ import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Set; import java.util.Set;
import org.jclouds.aws.ec2.domain.LaunchSpecification;
import org.jclouds.ec2.domain.BlockDeviceMapping; import org.jclouds.ec2.domain.BlockDeviceMapping;
import org.jclouds.ec2.domain.InstanceType; import org.jclouds.ec2.domain.InstanceType;
import org.jclouds.ec2.options.RunInstancesOptions; import org.jclouds.ec2.options.RunInstancesOptions;
import com.google.common.collect.ImmutableSet;
/** /**
* Contains options supported in the Form API for the RunInstances operation. <h2> * Contains options supported in the Form API for the RunInstances operation. <h2>
* Usage</h2> The recommended way to instantiate a RunInstancesOptions object is to statically * Usage</h2> 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 { public class AWSRunInstancesOptions extends RunInstancesOptions {
private LaunchSpecification.Builder launchSpecificationBuilder = LaunchSpecification.builder();
public static final AWSRunInstancesOptions NONE = new AWSRunInstancesOptions(); public static final AWSRunInstancesOptions NONE = new AWSRunInstancesOptions();
/** /**
@ -60,22 +64,15 @@ public class AWSRunInstancesOptions extends RunInstancesOptions {
return this; return this;
} }
String getPlacementGroup() {
return getFirstFormOrNull("Placement.GroupName");
}
/** /**
* Enables monitoring for the instance. * Enables monitoring for the instance.
*/ */
public AWSRunInstancesOptions enableMonitoring() { public AWSRunInstancesOptions enableMonitoring() {
formParameters.put("Monitoring.Enabled", "true"); formParameters.put("Monitoring.Enabled", "true");
launchSpecificationBuilder.monitoringEnabled(true);
return this; return this;
} }
String getMonitoringEnabled() {
return getFirstFormOrNull("Monitoring.Enabled");
}
/** /**
* Specifies the subnet ID within which to launch the instance(s) for Amazon Virtual Private * Specifies the subnet ID within which to launch the instance(s) for Amazon Virtual Private
* Cloud. * Cloud.
@ -85,10 +82,6 @@ public class AWSRunInstancesOptions extends RunInstancesOptions {
return this; return this;
} }
String getSubnetId() {
return getFirstFormOrNull("SubnetId");
}
public static class Builder extends RunInstancesOptions.Builder { public static class Builder extends RunInstancesOptions.Builder {
/** /**
@ -175,46 +168,63 @@ public class AWSRunInstancesOptions extends RunInstancesOptions {
@Override @Override
public AWSRunInstancesOptions withBlockDeviceMappings(Set<? extends BlockDeviceMapping> mappings) { public AWSRunInstancesOptions withBlockDeviceMappings(Set<? extends BlockDeviceMapping> mappings) {
launchSpecificationBuilder.blockDeviceMappings(mappings);
return AWSRunInstancesOptions.class.cast(super.withBlockDeviceMappings(mappings)); return AWSRunInstancesOptions.class.cast(super.withBlockDeviceMappings(mappings));
} }
@Override @Override
public AWSRunInstancesOptions withKernelId(String kernelId) { public AWSRunInstancesOptions withKernelId(String kernelId) {
launchSpecificationBuilder.kernelId(kernelId);
return AWSRunInstancesOptions.class.cast(super.withKernelId(kernelId)); return AWSRunInstancesOptions.class.cast(super.withKernelId(kernelId));
} }
@Override @Override
public AWSRunInstancesOptions withKeyName(String keyName) { public AWSRunInstancesOptions withKeyName(String keyName) {
launchSpecificationBuilder.keyName(keyName);
return AWSRunInstancesOptions.class.cast(super.withKeyName(keyName)); return AWSRunInstancesOptions.class.cast(super.withKeyName(keyName));
} }
@Override @Override
public AWSRunInstancesOptions withRamdisk(String ramDiskId) { public AWSRunInstancesOptions withRamdisk(String ramDiskId) {
launchSpecificationBuilder.ramdiskId(ramDiskId);
return AWSRunInstancesOptions.class.cast(super.withRamdisk(ramDiskId)); return AWSRunInstancesOptions.class.cast(super.withRamdisk(ramDiskId));
} }
@Override @Override
public AWSRunInstancesOptions withSecurityGroup(String securityGroup) { public AWSRunInstancesOptions withSecurityGroup(String securityGroup) {
launchSpecificationBuilder.groupId(securityGroup);
return AWSRunInstancesOptions.class.cast(super.withSecurityGroup(securityGroup)); return AWSRunInstancesOptions.class.cast(super.withSecurityGroup(securityGroup));
} }
@Override @Override
public AWSRunInstancesOptions withSecurityGroups(Iterable<String> securityGroups) { public AWSRunInstancesOptions withSecurityGroups(Iterable<String> securityGroups) {
launchSpecificationBuilder.groupIds(securityGroups);
return AWSRunInstancesOptions.class.cast(super.withSecurityGroups(securityGroups)); return AWSRunInstancesOptions.class.cast(super.withSecurityGroups(securityGroups));
} }
@Override @Override
public AWSRunInstancesOptions withSecurityGroups(String... securityGroups) { public AWSRunInstancesOptions withSecurityGroups(String... securityGroups) {
launchSpecificationBuilder.groupIds(ImmutableSet.copyOf(securityGroups));
return AWSRunInstancesOptions.class.cast(super.withSecurityGroups(securityGroups)); return AWSRunInstancesOptions.class.cast(super.withSecurityGroups(securityGroups));
} }
@Override @Override
public AWSRunInstancesOptions withUserData(byte[] unencodedData) { public AWSRunInstancesOptions withUserData(byte[] unencodedData) {
launchSpecificationBuilder.userData(unencodedData);
return AWSRunInstancesOptions.class.cast(super.withUserData(unencodedData)); return AWSRunInstancesOptions.class.cast(super.withUserData(unencodedData));
} }
@Override @Override
public AWSRunInstancesOptions asType(String type) { public AWSRunInstancesOptions asType(String type) {
launchSpecificationBuilder.instanceType(type);
return AWSRunInstancesOptions.class.cast(super.asType(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);
}
}
} }

View File

@ -59,13 +59,13 @@ public class AWSEC2ComputeServiceLiveTest extends EC2ComputeServiceLiveTest {
@Test(enabled = true, dependsOnMethods = "testCompareSizes") @Test(enabled = true, dependsOnMethods = "testCompareSizes")
public void testExtendedOptionsAndLogin() throws Exception { public void testExtendedOptionsAndLogin() throws Exception {
SecurityGroupClient securityGroupClient = EC2Client.class.cast(context.getProviderSpecificContext().getApi()) SecurityGroupClient securityGroupClient = EC2Client.class.cast(context.getProviderSpecificContext().getApi())
.getSecurityGroupServices(); .getSecurityGroupServices();
KeyPairClient keyPairClient = EC2Client.class.cast(context.getProviderSpecificContext().getApi()) KeyPairClient keyPairClient = EC2Client.class.cast(context.getProviderSpecificContext().getApi())
.getKeyPairServices(); .getKeyPairServices();
InstanceClient instanceClient = EC2Client.class.cast(context.getProviderSpecificContext().getApi()) InstanceClient instanceClient = EC2Client.class.cast(context.getProviderSpecificContext().getApi())
.getInstanceServices(); .getInstanceServices();
String group = this.group + "o"; String group = this.group + "o";
@ -76,6 +76,7 @@ public class AWSEC2ComputeServiceLiveTest extends EC2ComputeServiceLiveTest {
options.as(AWSEC2TemplateOptions.class).securityGroups(group); options.as(AWSEC2TemplateOptions.class).securityGroups(group);
options.as(AWSEC2TemplateOptions.class).keyPair(group); options.as(AWSEC2TemplateOptions.class).keyPair(group);
options.as(AWSEC2TemplateOptions.class).enableMonitoring(); options.as(AWSEC2TemplateOptions.class).enableMonitoring();
options.as(AWSEC2TemplateOptions.class).spotPrice(0.3f);
String startedId = null; String startedId = null;
try { 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 // make sure we made our dummy group and also let in the user's group
assertEquals(Sets.newTreeSet(instance.getGroupIds()), assertEquals(Sets.newTreeSet(instance.getGroupIds()), ImmutableSortedSet.<String> of("jclouds#" + group + "#"
ImmutableSortedSet.<String> of("jclouds#" + group + "#" + instance.getRegion(), group)); + instance.getRegion(), group));
// make sure our dummy group has no rules // make sure our dummy group has no rules
SecurityGroup secgroup = Iterables.getOnlyElement(securityGroupClient.describeSecurityGroupsInRegion(null, SecurityGroup secgroup = Iterables.getOnlyElement(securityGroupClient.describeSecurityGroupsInRegion(null,
"jclouds#" + group + "#" + instance.getRegion())); "jclouds#" + group + "#" + instance.getRegion()));
assert secgroup.getIpPermissions().size() == 0 : secgroup; assert secgroup.getIpPermissions().size() == 0 : secgroup;
// try to run a script with the original keyPair // try to run a script with the original keyPair
runScriptWithCreds(group, first.getOperatingSystem(), runScriptWithCreds(group, first.getOperatingSystem(), new Credentials(first.getCredentials().identity, result
new Credentials(first.getCredentials().identity, result.getKeyMaterial())); .getKeyMaterial()));
} finally { } finally {
client.destroyNodesMatching(NodePredicates.inGroup(group)); client.destroyNodesMatching(NodePredicates.inGroup(group));
@ -151,13 +152,13 @@ public class AWSEC2ComputeServiceLiveTest extends EC2ComputeServiceLiveTest {
} }
InstanceClient instanceClient = EC2Client.class.cast(context.getProviderSpecificContext().getApi()) InstanceClient instanceClient = EC2Client.class.cast(context.getProviderSpecificContext().getApi())
.getInstanceServices(); .getInstanceServices();
String group = this.group + "g"; String group = this.group + "g";
TemplateOptions options = client.templateOptions(); TemplateOptions options = client.templateOptions();
options.as(AWSEC2TemplateOptions.class).subnetId(subnetId); options.as(AWSEC2TemplateOptions.class).subnetId(subnetId).spotPrice(0.3f);
String startedId = null; String startedId = null;
String nodeId = null; String nodeId = null;

View File

@ -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);
}
}

View File

@ -41,6 +41,7 @@ import org.jclouds.Constants;
import org.jclouds.aws.domain.Region; import org.jclouds.aws.domain.Region;
import org.jclouds.aws.ec2.AWSEC2AsyncClient; import org.jclouds.aws.ec2.AWSEC2AsyncClient;
import org.jclouds.aws.ec2.AWSEC2Client; import org.jclouds.aws.ec2.AWSEC2Client;
import org.jclouds.aws.ec2.compute.AWSEC2TemplateOptions;
import org.jclouds.aws.ec2.domain.AWSRunningInstance; import org.jclouds.aws.ec2.domain.AWSRunningInstance;
import org.jclouds.compute.ComputeServiceContext; import org.jclouds.compute.ComputeServiceContext;
import org.jclouds.compute.ComputeServiceContextFactory; 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.domain.NodeMetadata;
import org.jclouds.compute.options.TemplateOptions; import org.jclouds.compute.options.TemplateOptions;
import org.jclouds.domain.Credentials; import org.jclouds.domain.Credentials;
import org.jclouds.ec2.EC2Client;
import org.jclouds.ec2.domain.KeyPair; 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.logging.log4j.config.Log4JLoggingModule;
import org.jclouds.rest.RestContext; import org.jclouds.rest.RestContext;
import org.jclouds.ssh.jsch.config.JschSshClientModule; import org.jclouds.ssh.jsch.config.JschSshClientModule;
@ -84,7 +82,7 @@ public class AWSKeyPairClientLiveTest {
protected void setupCredentials() { protected void setupCredentials() {
identity = checkNotNull(System.getProperty("test." + provider + ".identity"), "test." + provider + ".identity"); identity = checkNotNull(System.getProperty("test." + provider + ".identity"), "test." + provider + ".identity");
credential = checkNotNull(System.getProperty("test." + provider + ".credential"), "test." + provider credential = checkNotNull(System.getProperty("test." + provider + ".credential"), "test." + provider
+ ".credential"); + ".credential");
endpoint = System.getProperty("test." + provider + ".endpoint", null); endpoint = System.getProperty("test." + provider + ".endpoint", null);
apiversion = System.getProperty("test." + provider + ".apiversion", null); apiversion = System.getProperty("test." + provider + ".apiversion", null);
} }
@ -106,8 +104,8 @@ public class AWSKeyPairClientLiveTest {
public void setupClient() { public void setupClient() {
setupCredentials(); setupCredentials();
Properties overrides = setupProperties(); Properties overrides = setupProperties();
computeContext = new ComputeServiceContextFactory().createContext(provider, computeContext = new ComputeServiceContextFactory().createContext(provider, ImmutableSet.<Module> of(
ImmutableSet.<Module> of(new Log4JLoggingModule(), new JschSshClientModule()), overrides); new Log4JLoggingModule(), new JschSshClientModule()), overrides);
context = computeContext.getProviderSpecificContext(); context = computeContext.getProviderSpecificContext();
client = context.getApi().getKeyPairServices(); client = context.getApi().getKeyPairServices();
} }
@ -116,19 +114,19 @@ public class AWSKeyPairClientLiveTest {
Map<String, String> keyPair = ComputeTestUtils.setupKeyPair(); Map<String, String> keyPair = ComputeTestUtils.setupKeyPair();
InstanceClient instanceClient = EC2Client.class.cast(context.getApi()).getInstanceServices(); AWSInstanceClient instanceClient = AWSEC2Client.class.cast(context.getApi()).getInstanceServices();
String group = PREFIX + "unssh"; String group = PREFIX + "unssh";
computeContext.getComputeService().destroyNodesMatching(inGroup(group)); computeContext.getComputeService().destroyNodesMatching(inGroup(group));
TemplateOptions options = computeContext.getComputeService().templateOptions(); TemplateOptions options = computeContext.getComputeService().templateOptions();
options.authorizePublicKey(keyPair.get("public")); options.authorizePublicKey(keyPair.get("public")).as(AWSEC2TemplateOptions.class).spotPrice(0.3f);
ComputeServiceContext noSshContext = null; ComputeServiceContext noSshContext = null;
try { try {
noSshContext = new ComputeServiceContextFactory().createContext(provider, noSshContext = new ComputeServiceContextFactory().createContext(provider, ImmutableSet
ImmutableSet.of(new Log4JLoggingModule()), setupProperties()); .of(new Log4JLoggingModule()), setupProperties());
Set<? extends NodeMetadata> nodes = noSshContext.getComputeService().createNodesInGroup(group, 1, options); Set<? extends NodeMetadata> nodes = noSshContext.getComputeService().createNodesInGroup(group, 1, options);
@ -136,18 +134,19 @@ public class AWSKeyPairClientLiveTest {
assert first.getCredentials() != null : first; assert first.getCredentials() != null : first;
assert first.getCredentials().identity != null : first; assert first.getCredentials().identity != null : first;
AWSRunningInstance instance = AWSRunningInstance.class AWSRunningInstance instance = getInstance(instanceClient, first.getProviderId());
.cast(getInstance(instanceClient, first.getProviderId()));
assert instance.getSpotInstanceRequestId() != null : instance;
assertEquals(instance.getKeyName(), "jclouds#" + group); assertEquals(instance.getKeyName(), "jclouds#" + group);
assertEquals(first.getCredentials().credential, null); assertEquals(first.getCredentials().credential, null);
Map<? extends NodeMetadata, ExecResponse> responses = computeContext.getComputeService() Map<? extends NodeMetadata, ExecResponse> responses = computeContext.getComputeService()
.runScriptOnNodesMatching( .runScriptOnNodesMatching(
runningInGroup(group), runningInGroup(group),
exec("echo hello"), exec("echo hello"),
overrideCredentialsWith(new Credentials(first.getCredentials().identity, keyPair.get("private"))) overrideCredentialsWith(
.wrapInInitScript(false).runAsRoot(false)); new Credentials(first.getCredentials().identity, keyPair.get("private")))
.wrapInInitScript(false).runAsRoot(false));
ExecResponse hello = getOnlyElement(responses.values()); ExecResponse hello = getOnlyElement(responses.values());
assertEquals(hello.getOutput().trim(), "hello"); assertEquals(hello.getOutput().trim(), "hello");
@ -235,9 +234,8 @@ public class AWSKeyPairClientLiveTest {
assertEquals(listPair.getKeyFingerprint(), keyPair.getKeyFingerprint()); assertEquals(listPair.getKeyFingerprint(), keyPair.getKeyFingerprint());
} }
protected RunningInstance getInstance(InstanceClient instanceClient, String id) { protected AWSRunningInstance getInstance(AWSInstanceClient instanceClient, String id) {
RunningInstance instance = getOnlyElement(getOnlyElement(instanceClient.describeInstancesInRegion(null, id))); return getOnlyElement(getOnlyElement(instanceClient.describeInstancesInRegion(null, id)));
return instance;
} }
@AfterTest @AfterTest