Issue 249: new extended options for ec2, including securityGroups, keyPair, and noKeyPair

This commit is contained in:
Adrian Cole 2010-05-11 21:24:27 -07:00
parent 25fd278ecf
commit b6189457d5
28 changed files with 1871 additions and 217 deletions

View File

@ -22,7 +22,7 @@ import java.util.List;
import java.util.Properties; import java.util.Properties;
import org.jclouds.aws.ec2.compute.config.EC2ComputeServiceContextModule; import org.jclouds.aws.ec2.compute.config.EC2ComputeServiceContextModule;
import org.jclouds.aws.ec2.compute.config.internal.EC2ResolveImagesModule; import org.jclouds.aws.ec2.compute.config.EC2ResolveImagesModule;
import org.jclouds.aws.ec2.config.EC2RestClientModule; import org.jclouds.aws.ec2.config.EC2RestClientModule;
import org.jclouds.compute.ComputeServiceContextBuilder; import org.jclouds.compute.ComputeServiceContextBuilder;
import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule; import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule;

View File

@ -18,6 +18,8 @@
*/ */
package org.jclouds.aws.ec2.compute; package org.jclouds.aws.ec2.compute;
import static org.jclouds.util.Utils.checkNotEmpty;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
@ -31,10 +33,9 @@ import javax.inject.Singleton;
import org.jclouds.Constants; import org.jclouds.Constants;
import org.jclouds.aws.ec2.EC2Client; import org.jclouds.aws.ec2.EC2Client;
import org.jclouds.aws.ec2.compute.config.EC2ComputeServiceContextModule.GetRegionFromNodeOrDefault; import org.jclouds.aws.ec2.compute.config.EC2ComputeServiceContextModule.GetRegionFromNodeOrDefault;
import org.jclouds.aws.ec2.compute.domain.PortsRegionTag; import org.jclouds.aws.ec2.compute.domain.RegionAndName;
import org.jclouds.aws.ec2.compute.domain.RegionTag; import org.jclouds.aws.ec2.compute.domain.RegionNameAndIngressRules;
import org.jclouds.aws.ec2.domain.KeyPair; import org.jclouds.aws.ec2.domain.KeyPair;
import org.jclouds.aws.ec2.domain.RunningInstance;
import org.jclouds.compute.ComputeServiceContext; import org.jclouds.compute.ComputeServiceContext;
import org.jclouds.compute.domain.Image; import org.jclouds.compute.domain.Image;
import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.compute.domain.NodeMetadata;
@ -48,10 +49,8 @@ import org.jclouds.compute.strategy.RebootNodeStrategy;
import org.jclouds.compute.strategy.RunNodesAndAddToSetStrategy; import org.jclouds.compute.strategy.RunNodesAndAddToSetStrategy;
import org.jclouds.compute.util.ComputeUtils; import org.jclouds.compute.util.ComputeUtils;
import org.jclouds.domain.Location; import org.jclouds.domain.Location;
import static org.jclouds.util.Utils.*;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
@ -60,11 +59,10 @@ import com.google.common.collect.Sets;
*/ */
@Singleton @Singleton
public class EC2ComputeService extends BaseComputeService { public class EC2ComputeService extends BaseComputeService {
protected final EC2Client ec2Client; private final EC2Client ec2Client;
protected final GetRegionFromNodeOrDefault getRegionFromNodeOrDefault; private final GetRegionFromNodeOrDefault getRegionFromNodeOrDefault;
protected final Map<RegionTag, KeyPair> credentialsMap; private final Map<RegionAndName, KeyPair> credentialsMap;
protected final Map<PortsRegionTag, String> securityGroupMap; private final Map<RegionAndName, String> securityGroupMap;
protected final Predicate<RunningInstance> instanceStateTerminated;
@Inject @Inject
protected EC2ComputeService(ComputeServiceContext context, protected EC2ComputeService(ComputeServiceContext context,
@ -76,8 +74,8 @@ public class EC2ComputeService extends BaseComputeService {
Provider<TemplateBuilder> templateBuilderProvider, ComputeUtils utils, Provider<TemplateBuilder> templateBuilderProvider, ComputeUtils utils,
@Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor, EC2Client ec2Client, @Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor, EC2Client ec2Client,
GetRegionFromNodeOrDefault getRegionFromNodeOrDefault, GetRegionFromNodeOrDefault getRegionFromNodeOrDefault,
Map<RegionTag, KeyPair> credentialsMap, Map<PortsRegionTag, String> securityGroupMap, Map<RegionAndName, KeyPair> credentialsMap,
@Named("TERMINATED") Predicate<RunningInstance> instanceStateTerminated) { Map<RegionAndName, String> securityGroupMap) {
super(context, images, sizes, locations, listNodesStrategy, getNodeMetadataStrategy, super(context, images, sizes, locations, listNodesStrategy, getNodeMetadataStrategy,
runNodesAndAddToSetStrategy, rebootNodeStrategy, destroyNodeStrategy, runNodesAndAddToSetStrategy, rebootNodeStrategy, destroyNodeStrategy,
templateBuilderProvider, utils, executor); templateBuilderProvider, utils, executor);
@ -85,7 +83,6 @@ public class EC2ComputeService extends BaseComputeService {
this.getRegionFromNodeOrDefault = getRegionFromNodeOrDefault; this.getRegionFromNodeOrDefault = getRegionFromNodeOrDefault;
this.credentialsMap = credentialsMap; this.credentialsMap = credentialsMap;
this.securityGroupMap = securityGroupMap; this.securityGroupMap = securityGroupMap;
this.instanceStateTerminated = instanceStateTerminated;
} }
private void deleteSecurityGroup(String region, String tag) { private void deleteSecurityGroup(String region, String tag) {
@ -94,8 +91,8 @@ public class EC2ComputeService extends BaseComputeService {
if (ec2Client.getSecurityGroupServices().describeSecurityGroupsInRegion(region, group).size() > 0) { if (ec2Client.getSecurityGroupServices().describeSecurityGroupsInRegion(region, group).size() > 0) {
logger.debug(">> deleting securityGroup(%s)", group); logger.debug(">> deleting securityGroup(%s)", group);
ec2Client.getSecurityGroupServices().deleteSecurityGroupInRegion(region, group); ec2Client.getSecurityGroupServices().deleteSecurityGroupInRegion(region, group);
securityGroupMap.remove(new PortsRegionTag(region, tag, null)); // TODO: test this clear // TODO: test this clear happens
// happens securityGroupMap.remove(new RegionNameAndIngressRules(region, tag, null, false));
logger.debug("<< deleted securityGroup(%s)", group); logger.debug("<< deleted securityGroup(%s)", group);
} }
} }
@ -105,8 +102,8 @@ public class EC2ComputeService extends BaseComputeService {
if (keyPair.getKeyName().matches("jclouds#" + tag + "-[0-9]+")) { if (keyPair.getKeyName().matches("jclouds#" + tag + "-[0-9]+")) {
logger.debug(">> deleting keyPair(%s)", keyPair.getKeyName()); logger.debug(">> deleting keyPair(%s)", keyPair.getKeyName());
ec2Client.getKeyPairServices().deleteKeyPairInRegion(region, keyPair.getKeyName()); ec2Client.getKeyPairServices().deleteKeyPairInRegion(region, keyPair.getKeyName());
credentialsMap.remove(new RegionTag(region, keyPair.getKeyName())); // TODO: test this // TODO: test this clear happens
// clear happens credentialsMap.remove(new RegionAndName(region, keyPair.getKeyName()));
logger.debug("<< deleted keyPair(%s)", keyPair.getKeyName()); logger.debug("<< deleted keyPair(%s)", keyPair.getKeyName());
} }
} }

View File

@ -23,6 +23,7 @@ import static org.jclouds.aws.ec2.reference.EC2Constants.PROPERTY_EC2_AMI_OWNERS
import static org.jclouds.compute.domain.OsFamily.UBUNTU; import static org.jclouds.compute.domain.OsFamily.UBUNTU;
import java.net.URI; import java.net.URI;
import java.security.SecureRandom;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
@ -40,12 +41,12 @@ import org.jclouds.aws.ec2.EC2AsyncClient;
import org.jclouds.aws.ec2.EC2Client; import org.jclouds.aws.ec2.EC2Client;
import org.jclouds.aws.ec2.compute.EC2ComputeService; import org.jclouds.aws.ec2.compute.EC2ComputeService;
import org.jclouds.aws.ec2.compute.domain.EC2Size; import org.jclouds.aws.ec2.compute.domain.EC2Size;
import org.jclouds.aws.ec2.compute.domain.PortsRegionTag; import org.jclouds.aws.ec2.compute.domain.RegionAndName;
import org.jclouds.aws.ec2.compute.domain.RegionTag;
import org.jclouds.aws.ec2.compute.functions.CreateNewKeyPair;
import org.jclouds.aws.ec2.compute.functions.CreateSecurityGroupIfNeeded; import org.jclouds.aws.ec2.compute.functions.CreateSecurityGroupIfNeeded;
import org.jclouds.aws.ec2.compute.functions.CreateUniqueKeyPair;
import org.jclouds.aws.ec2.compute.functions.ImageParser; import org.jclouds.aws.ec2.compute.functions.ImageParser;
import org.jclouds.aws.ec2.compute.functions.RunningInstanceToNodeMetadata; import org.jclouds.aws.ec2.compute.functions.RunningInstanceToNodeMetadata;
import org.jclouds.aws.ec2.compute.options.EC2TemplateOptions;
import org.jclouds.aws.ec2.compute.strategy.EC2DestroyNodeStrategy; import org.jclouds.aws.ec2.compute.strategy.EC2DestroyNodeStrategy;
import org.jclouds.aws.ec2.compute.strategy.EC2RunNodesAndAddToSetStrategy; import org.jclouds.aws.ec2.compute.strategy.EC2RunNodesAndAddToSetStrategy;
import org.jclouds.aws.ec2.config.EC2ContextModule; import org.jclouds.aws.ec2.config.EC2ContextModule;
@ -64,6 +65,7 @@ import org.jclouds.compute.domain.TemplateBuilder;
import org.jclouds.compute.internal.ComputeServiceContextImpl; import org.jclouds.compute.internal.ComputeServiceContextImpl;
import org.jclouds.compute.internal.TemplateBuilderImpl; import org.jclouds.compute.internal.TemplateBuilderImpl;
import org.jclouds.compute.options.GetNodesOptions; import org.jclouds.compute.options.GetNodesOptions;
import org.jclouds.compute.options.TemplateOptions;
import org.jclouds.compute.predicates.ScriptStatusReturnsZero; import org.jclouds.compute.predicates.ScriptStatusReturnsZero;
import org.jclouds.compute.predicates.ScriptStatusReturnsZero.CommandUsingClient; import org.jclouds.compute.predicates.ScriptStatusReturnsZero.CommandUsingClient;
import org.jclouds.compute.reference.ComputeServiceConstants; import org.jclouds.compute.reference.ComputeServiceConstants;
@ -84,6 +86,7 @@ import com.google.common.base.Function;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import com.google.common.base.Predicates; import com.google.common.base.Predicates;
import com.google.common.base.Splitter; import com.google.common.base.Splitter;
import com.google.common.base.Supplier;
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.Maps; import com.google.common.collect.Maps;
@ -108,6 +111,7 @@ public class EC2ComputeServiceContextModule extends EC2ContextModule {
@Override @Override
protected void configure() { protected void configure() {
super.configure(); super.configure();
bind(TemplateOptions.class).to(EC2TemplateOptions.class);
bind(ComputeService.class).to(EC2ComputeService.class); bind(ComputeService.class).to(EC2ComputeService.class);
bind(RunNodesAndAddToSetStrategy.class).to(EC2RunNodesAndAddToSetStrategy.class); bind(RunNodesAndAddToSetStrategy.class).to(EC2RunNodesAndAddToSetStrategy.class);
bind(ListNodesStrategy.class).to(EC2ListNodesStrategy.class); bind(ListNodesStrategy.class).to(EC2ListNodesStrategy.class);
@ -119,6 +123,20 @@ public class EC2ComputeServiceContextModule extends EC2ContextModule {
.in(Scopes.SINGLETON); .in(Scopes.SINGLETON);
} }
@Provides
@Singleton
Supplier<String> provideSuffix() {
return new Supplier<String>() {
final SecureRandom random = new SecureRandom();
@Override
public String get() {
return random.nextInt(100) + "";
}
};
}
@Provides @Provides
TemplateBuilder provideTemplate(TemplateBuilderImpl template) { TemplateBuilder provideTemplate(TemplateBuilderImpl template) {
return template.architecture(Architecture.X86_32).osFamily(UBUNTU); return template.architecture(Architecture.X86_32).osFamily(UBUNTU);
@ -220,7 +238,7 @@ public class EC2ComputeServiceContextModule extends EC2ContextModule {
@Provides @Provides
@Singleton @Singleton
protected final Map<RegionTag, KeyPair> credentialsMap(CreateNewKeyPair in) { protected final Map<RegionAndName, KeyPair> credentialsMap(CreateUniqueKeyPair in) {
// doesn't seem to clear when someone issues remove(key) // doesn't seem to clear when someone issues remove(key)
// return new MapMaker().makeComputingMap(in); // return new MapMaker().makeComputingMap(in);
return Maps.newLinkedHashMap(); return Maps.newLinkedHashMap();
@ -228,7 +246,7 @@ public class EC2ComputeServiceContextModule extends EC2ContextModule {
@Provides @Provides
@Singleton @Singleton
protected final Map<PortsRegionTag, String> securityGroupMap(CreateSecurityGroupIfNeeded in) { protected final Map<RegionAndName, String> securityGroupMap(CreateSecurityGroupIfNeeded in) {
// doesn't seem to clear when someone issues remove(key) // doesn't seem to clear when someone issues remove(key)
// return new MapMaker().makeComputingMap(in); // return new MapMaker().makeComputingMap(in);
return Maps.newLinkedHashMap(); return Maps.newLinkedHashMap();

View File

@ -16,7 +16,7 @@
* limitations under the License. * limitations under the License.
* ==================================================================== * ====================================================================
*/ */
package org.jclouds.aws.ec2.compute.config.internal; package org.jclouds.aws.ec2.compute.config;
import com.google.inject.AbstractModule; import com.google.inject.AbstractModule;
import org.jclouds.aws.ec2.compute.strategy.EC2PopulateDefaultLoginCredentialsForImageStrategy; import org.jclouds.aws.ec2.compute.strategy.EC2PopulateDefaultLoginCredentialsForImageStrategy;

View File

@ -22,13 +22,13 @@ package org.jclouds.aws.ec2.compute.domain;
* *
* @author Adrian Cole * @author Adrian Cole
*/ */
public class RegionTag { public class RegionAndName {
protected final String region; protected final String region;
protected final String tag; protected final String name;
public RegionTag(String region, String tag) { public RegionAndName(String region, String name) {
this.region = region; this.region = region;
this.tag = tag; this.name = name;
} }
@Override @Override
@ -36,7 +36,7 @@ public class RegionTag {
final int prime = 31; final int prime = 31;
int result = 1; int result = 1;
result = prime * result + ((region == null) ? 0 : region.hashCode()); result = prime * result + ((region == null) ? 0 : region.hashCode());
result = prime * result + ((tag == null) ? 0 : tag.hashCode()); result = prime * result + ((name == null) ? 0 : name.hashCode());
return result; return result;
} }
@ -48,16 +48,16 @@ public class RegionTag {
return false; return false;
if (getClass() != obj.getClass()) if (getClass() != obj.getClass())
return false; return false;
RegionTag other = (RegionTag) obj; RegionAndName other = (RegionAndName) obj;
if (region == null) { if (region == null) {
if (other.region != null) if (other.region != null)
return false; return false;
} else if (!region.equals(other.region)) } else if (!region.equals(other.region))
return false; return false;
if (tag == null) { if (name == null) {
if (other.tag != null) if (other.name != null)
return false; return false;
} else if (!tag.equals(other.tag)) } else if (!name.equals(other.name))
return false; return false;
return true; return true;
} }
@ -66,13 +66,13 @@ public class RegionTag {
return region; return region;
} }
public String getTag() { public String getName() {
return tag; return name;
} }
@Override @Override
public String toString() { public String toString() {
return "RegionTag [region=" + region + ", tag=" + tag + "]"; return "RegionTag [region=" + region + ", name=" + name + "]";
} }
} }

View File

@ -23,12 +23,14 @@ package org.jclouds.aws.ec2.compute.domain;
* *
* @author Adrian Cole * @author Adrian Cole
*/ */
public class PortsRegionTag extends RegionTag { public class RegionNameAndIngressRules extends RegionAndName {
private final int[] ports; private final int[] ports;
private final boolean authorizeSelf;
public PortsRegionTag(String region, String tag, int[] ports) { public RegionNameAndIngressRules(String region, String tag, int[] ports, boolean authorizeSelf) {
super(region, tag); super(region, tag);
this.ports = ports; this.ports = ports;
this.authorizeSelf = authorizeSelf;
} }
// intentionally not overriding equals or hash-code so that we can search only by region/tag in a // intentionally not overriding equals or hash-code so that we can search only by region/tag in a
@ -38,4 +40,8 @@ public class PortsRegionTag extends RegionTag {
return ports; return ports;
} }
public boolean shouldAuthorizeSelf() {
return authorizeSelf;
}
} }

View File

@ -1,3 +1,21 @@
/**
*
* Copyright (C) 2009 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; package org.jclouds.aws.ec2.compute.functions;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
@ -9,7 +27,7 @@ import javax.inject.Singleton;
import org.jclouds.aws.AWSResponseException; import org.jclouds.aws.AWSResponseException;
import org.jclouds.aws.ec2.EC2Client; import org.jclouds.aws.ec2.EC2Client;
import org.jclouds.aws.ec2.compute.domain.PortsRegionTag; import org.jclouds.aws.ec2.compute.domain.RegionNameAndIngressRules;
import org.jclouds.aws.ec2.domain.IpProtocol; import org.jclouds.aws.ec2.domain.IpProtocol;
import org.jclouds.aws.ec2.domain.UserIdGroupPair; import org.jclouds.aws.ec2.domain.UserIdGroupPair;
import org.jclouds.compute.reference.ComputeServiceConstants; import org.jclouds.compute.reference.ComputeServiceConstants;
@ -18,8 +36,12 @@ import org.jclouds.logging.Logger;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
/**
*
* @author Adrian Cole
*/
@Singleton @Singleton
public class CreateSecurityGroupIfNeeded implements Function<PortsRegionTag, String> { public class CreateSecurityGroupIfNeeded implements Function<RegionNameAndIngressRules, String> {
@Resource @Resource
@Named(ComputeServiceConstants.COMPUTE_LOGGER) @Named(ComputeServiceConstants.COMPUTE_LOGGER)
protected Logger logger = Logger.NULL; protected Logger logger = Logger.NULL;
@ -31,9 +53,9 @@ public class CreateSecurityGroupIfNeeded implements Function<PortsRegionTag, Str
} }
@Override @Override
public String apply(PortsRegionTag from) { public String apply(RegionNameAndIngressRules from) {
createSecurityGroupInRegion(from.getRegion(), from.getTag(), from.getPorts()); createSecurityGroupInRegion(from.getRegion(), from.getName(), from.getPorts());
return from.getTag(); return from.getName();
} }
private void createSecurityGroupInRegion(String region, String name, int... ports) { private void createSecurityGroupInRegion(String region, String name, int... ports) {
@ -44,21 +66,11 @@ public class CreateSecurityGroupIfNeeded implements Function<PortsRegionTag, Str
ec2Client.getSecurityGroupServices().createSecurityGroupInRegion(region, name, name); ec2Client.getSecurityGroupServices().createSecurityGroupInRegion(region, name, name);
logger.debug("<< created securityGroup(%s)", name); logger.debug("<< created securityGroup(%s)", name);
for (int port : ports) { for (int port : ports) {
logger.debug(">> authorizing securityGroup region(%s) name(%s) port(%s)", region, name, createIngressRuleForTCPPort(region, name, port);
port); }
ec2Client.getSecurityGroupServices().authorizeSecurityGroupIngressInRegion(region, if (ports.length > 0) {
name, IpProtocol.TCP, port, port, "0.0.0.0/0"); authorizeGroupToItself(region, name);
logger.debug("<< authorized securityGroup(%s)", name);
} }
logger.debug(">> authorizing securityGroup region(%s) name(%s) permission to itself",
region, name);
String myOwnerId = Iterables.get(
ec2Client.getSecurityGroupServices().describeSecurityGroupsInRegion(region), 0)
.getOwnerId();
ec2Client.getSecurityGroupServices().authorizeSecurityGroupIngressInRegion(region, name,
new UserIdGroupPair(myOwnerId, name));
logger.debug("<< authorized securityGroup(%s)", name);
} catch (AWSResponseException e) { } catch (AWSResponseException e) {
if (e.getError().getCode().equals("InvalidGroup.Duplicate")) { if (e.getError().getCode().equals("InvalidGroup.Duplicate")) {
logger.debug("<< reused securityGroup(%s)", name); logger.debug("<< reused securityGroup(%s)", name);
@ -68,4 +80,22 @@ public class CreateSecurityGroupIfNeeded implements Function<PortsRegionTag, Str
} }
} }
private void createIngressRuleForTCPPort(String region, String name, int port) {
logger.debug(">> authorizing securityGroup region(%s) name(%s) port(%s)", region, name, port);
ec2Client.getSecurityGroupServices().authorizeSecurityGroupIngressInRegion(region, name,
IpProtocol.TCP, port, port, "0.0.0.0/0");
logger.debug("<< authorized securityGroup(%s)", name);
}
private void authorizeGroupToItself(String region, String name) {
logger.debug(">> authorizing securityGroup region(%s) name(%s) permission to itself", region,
name);
String myOwnerId = Iterables.get(
ec2Client.getSecurityGroupServices().describeSecurityGroupsInRegion(region), 0)
.getOwnerId();
ec2Client.getSecurityGroupServices().authorizeSecurityGroupIngressInRegion(region, name,
new UserIdGroupPair(myOwnerId, name));
logger.debug("<< authorized securityGroup(%s)", name);
}
} }

View File

@ -21,8 +21,6 @@ package org.jclouds.aws.ec2.compute.functions;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import java.security.SecureRandom;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
@ -30,43 +28,46 @@ import javax.inject.Singleton;
import org.jclouds.aws.AWSResponseException; import org.jclouds.aws.AWSResponseException;
import org.jclouds.aws.ec2.EC2Client; import org.jclouds.aws.ec2.EC2Client;
import org.jclouds.aws.ec2.compute.domain.RegionTag; import org.jclouds.aws.ec2.compute.domain.RegionAndName;
import org.jclouds.aws.ec2.domain.KeyPair; import org.jclouds.aws.ec2.domain.KeyPair;
import org.jclouds.compute.reference.ComputeServiceConstants; import org.jclouds.compute.reference.ComputeServiceConstants;
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.Supplier;
/** /**
* *
* @author Adrian Cole * @author Adrian Cole
*/ */
@Singleton @Singleton
public class CreateNewKeyPair implements Function<RegionTag, KeyPair> { public class CreateUniqueKeyPair implements Function<RegionAndName, KeyPair> {
@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 ec2Client;
protected Supplier<String> randomSuffix;
@Inject @Inject
public CreateNewKeyPair(EC2Client ec2Client) { public CreateUniqueKeyPair(EC2Client ec2Client, Supplier<String> randomSuffix) {
this.ec2Client = ec2Client; this.ec2Client = ec2Client;
this.randomSuffix = randomSuffix;
} }
@Override @Override
public KeyPair apply(RegionTag from) { public KeyPair apply(RegionAndName from) {
return createNewKeyPairInRegion(from.getRegion(), from.getTag()); return createNewKeyPairInRegion(from.getRegion(), from.getName());
} }
private KeyPair createNewKeyPairInRegion(String region, String tag) { private KeyPair createNewKeyPairInRegion(String region, String keyPairName) {
checkNotNull(region, "region"); checkNotNull(region, "region");
checkNotNull(tag, "tag"); checkNotNull(keyPairName, "keyPairName");
logger.debug(">> creating keyPair region(%s) tag(%s)", region, tag); logger.debug(">> creating keyPair region(%s) keyPairName(%s)", region, keyPairName);
KeyPair keyPair = null; KeyPair keyPair = null;
while (keyPair == null) { while (keyPair == null) {
try { try {
keyPair = ec2Client.getKeyPairServices().createKeyPairInRegion(region, keyPair = ec2Client.getKeyPairServices().createKeyPairInRegion(region,
"jclouds#" + tag + "-" + new SecureRandom().nextInt(100)); getNextName(keyPairName));
logger.debug("<< created keyPair(%s)", keyPair.getKeyName()); logger.debug("<< created keyPair(%s)", keyPair.getKeyName());
} catch (AWSResponseException e) { } catch (AWSResponseException e) {
if (!e.getError().getCode().equals("InvalidKeyPair.Duplicate")) { if (!e.getError().getCode().equals("InvalidKeyPair.Duplicate")) {
@ -76,4 +77,8 @@ public class CreateNewKeyPair implements Function<RegionTag, KeyPair> {
} }
return keyPair; return keyPair;
} }
private String getNextName(String keyPairName) {
return "jclouds#" + keyPairName + "-" + randomSuffix.get();
}
} }

View File

@ -32,7 +32,7 @@ import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
import javax.inject.Singleton; import javax.inject.Singleton;
import org.jclouds.aws.ec2.compute.domain.RegionTag; import org.jclouds.aws.ec2.compute.domain.RegionAndName;
import org.jclouds.aws.ec2.domain.InstanceState; import org.jclouds.aws.ec2.domain.InstanceState;
import org.jclouds.aws.ec2.domain.KeyPair; import org.jclouds.aws.ec2.domain.KeyPair;
import org.jclouds.aws.ec2.domain.RunningInstance; import org.jclouds.aws.ec2.domain.RunningInstance;
@ -86,7 +86,7 @@ public class RunningInstanceToNodeMetadata implements Function<RunningInstance,
NodeState.PENDING).put(InstanceState.TERMINATED, NodeState.TERMINATED).build(); NodeState.PENDING).put(InstanceState.TERMINATED, NodeState.TERMINATED).build();
private final AMIClient amiClient; private final AMIClient amiClient;
private final Map<RegionTag, KeyPair> credentialsMap; private final Map<RegionAndName, KeyPair> credentialsMap;
private final PopulateDefaultLoginCredentialsForImageStrategy credentialProvider; private final PopulateDefaultLoginCredentialsForImageStrategy credentialProvider;
private final Set<? extends Image> images; private final Set<? extends Image> images;
private final Set<? extends Location> locations; private final Set<? extends Location> locations;
@ -95,7 +95,7 @@ public class RunningInstanceToNodeMetadata implements Function<RunningInstance,
@Inject @Inject
RunningInstanceToNodeMetadata( RunningInstanceToNodeMetadata(
AMIClient amiClient, AMIClient amiClient,
Map<RegionTag, KeyPair> credentialsMap, Map<RegionAndName, KeyPair> credentialsMap,
PopulateDefaultLoginCredentialsForImageStrategy credentialProvider, PopulateDefaultLoginCredentialsForImageStrategy credentialProvider,
Set<? extends Image> images, Set<? extends Image> images,
Set<? extends Location> locations, Set<? extends Location> locations,
@ -194,7 +194,7 @@ public class RunningInstanceToNodeMetadata implements Function<RunningInstance,
@VisibleForTesting @VisibleForTesting
String getPrivateKeyOrNull(RunningInstance instance, String tag) { String getPrivateKeyOrNull(RunningInstance instance, String tag) {
KeyPair keyPair = credentialsMap.get(new RegionTag(instance.getRegion(), instance KeyPair keyPair = credentialsMap.get(new RegionAndName(instance.getRegion(), instance
.getKeyName())); .getKeyName()));
return keyPair != null ? keyPair.getKeyMaterial() : null; return keyPair != null ? keyPair.getKeyMaterial() : null;
} }

View File

@ -0,0 +1,303 @@
/**
*
* Copyright (C) 2009 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.options;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import java.util.Arrays;
import java.util.Set;
import org.jclouds.compute.options.TemplateOptions;
import org.jclouds.util.Utils;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
/**
* Contains options supported in the {@code ComputeService#runNode} operation on the "ec2" provider.
* <h2>
* Usage</h2> The recommended way to instantiate a EC2TemplateOptions object is to statically import
* EC2TemplateOptions.* and invoke a static creation method followed by an instance mutator (if
* needed):
* <p/>
* <code>
* import static org.jclouds.aws.ec2.compute.options.EC2TemplateOptions.Builder.*;
* <p/>
* ComputeService client = // get connection
* templateBuilder.options(inboundPorts(22, 80, 8080, 443));
* Set<? extends NodeMetadata> set = client.runNodesWithTag(tag, 2, templateBuilder.build());
* <code>
*
* @author Adrian Cole
*/
public class EC2TemplateOptions extends TemplateOptions {
private Set<String> groupIds = ImmutableSet.of();
private String keyPair = null;
private boolean noKeyPair;
public static final EC2TemplateOptions NONE = new EC2TemplateOptions();
/**
*
* @see EC2TemplateOptions#securityGroups(Iterable<String>)
*/
public EC2TemplateOptions securityGroups(String... groupIds) {
return securityGroups(ImmutableSet.copyOf(groupIds));
}
/**
* Specifies the security groups to be used for nodes with this template
*/
public EC2TemplateOptions securityGroups(Iterable<String> groupIds) {
checkArgument(Iterables.size(groupIds) > 0, "you must specify at least one security group");
for (String groupId : groupIds)
Utils.checkNotEmpty(groupId, "all security groups must be non-empty");
this.groupIds = ImmutableSet.copyOf(groupIds);
return this;
}
/**
* Specifies the keypair used to run instances with
*/
public EC2TemplateOptions keyPair(String keyPair) {
checkNotNull(keyPair, "use noKeyPair option to request boot without a keypair");
checkState(!noKeyPair, "you cannot specify both options keyPair and noKeyPair");
Utils.checkNotEmpty(keyPair, "keypair must be non-empty");
this.keyPair = keyPair;
return this;
}
/**
* Do not use a keypair on instances
*/
public EC2TemplateOptions noKeyPair() {
checkState(keyPair == null, "you cannot specify both options keyPair and noKeyPair");
this.noKeyPair = true;
return this;
}
public static class Builder {
/**
* @see EC2TemplateOptions#securityGroups(Iterable<String>)
*/
public static EC2TemplateOptions securityGroups(String... groupIds) {
EC2TemplateOptions options = new EC2TemplateOptions();
return EC2TemplateOptions.class.cast(options.securityGroups(groupIds));
}
/**
* @see EC2TemplateOptions#securityGroups(Iterable<String>)
*/
public static EC2TemplateOptions securityGroups(Iterable<String> groupIds) {
EC2TemplateOptions options = new EC2TemplateOptions();
return EC2TemplateOptions.class.cast(options.securityGroups(groupIds));
}
/**
* @see EC2TemplateOptions#keyPair
*/
public static EC2TemplateOptions keyPair(String keyPair) {
EC2TemplateOptions options = new EC2TemplateOptions();
return EC2TemplateOptions.class.cast(options.keyPair(keyPair));
}
/**
* @see EC2TemplateOptions#noKeyPair
*/
public static EC2TemplateOptions noKeyPair() {
EC2TemplateOptions options = new EC2TemplateOptions();
return EC2TemplateOptions.class.cast(options.noKeyPair());
}
// methods that only facilitate returning the correct object type
/**
* @see TemplateOptions#inboundPorts
*/
public static EC2TemplateOptions inboundPorts(int... ports) {
EC2TemplateOptions options = new EC2TemplateOptions();
return EC2TemplateOptions.class.cast(options.inboundPorts(ports));
}
/**
* @see TemplateOptions#port
*/
public static EC2TemplateOptions blockOnPort(int port, int seconds) {
EC2TemplateOptions options = new EC2TemplateOptions();
return EC2TemplateOptions.class.cast(options.blockOnPort(port, seconds));
}
/**
* @see TemplateOptions#runScript
*/
public static EC2TemplateOptions runScript(byte[] script) {
EC2TemplateOptions options = new EC2TemplateOptions();
return EC2TemplateOptions.class.cast(options.runScript(script));
}
/**
* @see TemplateOptions#installPrivateKey
*/
public static EC2TemplateOptions installPrivateKey(String rsaKey) {
EC2TemplateOptions options = new EC2TemplateOptions();
return EC2TemplateOptions.class.cast(options.installPrivateKey(rsaKey));
}
/**
* @see TemplateOptions#authorizePublicKey
*/
public static EC2TemplateOptions authorizePublicKey(String rsaKey) {
EC2TemplateOptions options = new EC2TemplateOptions();
return EC2TemplateOptions.class.cast(options.authorizePublicKey(rsaKey));
}
/**
* @see TemplateOptions#withDetails
*/
public static EC2TemplateOptions withDetails() {
EC2TemplateOptions options = new EC2TemplateOptions();
return EC2TemplateOptions.class.cast(options.withMetadata());
}
}
// methods that only facilitate returning the correct object type
/**
* @see TemplateOptions#authorizePublicKey
*/
@Override
public EC2TemplateOptions authorizePublicKey(String publicKey) {
return EC2TemplateOptions.class.cast(super.authorizePublicKey(publicKey));
}
/**
* @see TemplateOptions#blockOnPort
*/
@Override
public EC2TemplateOptions blockOnPort(int port, int seconds) {
return EC2TemplateOptions.class.cast(super.blockOnPort(port, seconds));
}
/**
*
* special thing is that we do assume if you are passing groups that you have everything you need
* already defined. for example, our option inboundPorts normally creates ingress rules
* accordingly but if we notice you've specified securityGroups, we do not mess with rules at all
*
* @see TemplateOptions#inboundPorts
*/
@Override
public EC2TemplateOptions inboundPorts(int... ports) {
return EC2TemplateOptions.class.cast(super.inboundPorts(ports));
}
/**
* @see TemplateOptions#installPrivateKey
*/
@Override
public EC2TemplateOptions installPrivateKey(String privateKey) {
return EC2TemplateOptions.class.cast(super.installPrivateKey(privateKey));
}
/**
* @see TemplateOptions#runScript
*/
@Override
public EC2TemplateOptions runScript(byte[] script) {
return EC2TemplateOptions.class.cast(super.runScript(script));
}
/**
* @see TemplateOptions#withMetadata
*/
@Override
public EC2TemplateOptions withMetadata() {
return EC2TemplateOptions.class.cast(super.withMetadata());
}
/**
* @return groupIds the user specified to run instances with, or zero length set to create an
* implicit group
*/
public Set<String> getGroupIds() {
return groupIds;
}
/**
* @return keyPair to use when running the instance or null, to generate a keypair.
*/
public String getKeyPair() {
return keyPair;
}
/**
* @return true (default) if we are supposed to use a keypair
*/
public boolean shouldAutomaticallyCreateKeyPair() {
return !noKeyPair;
}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + ((groupIds == null) ? 0 : groupIds.hashCode());
result = prime * result + ((keyPair == null) ? 0 : keyPair.hashCode());
result = prime * result + (noKeyPair ? 1231 : 1237);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!super.equals(obj))
return false;
if (getClass() != obj.getClass())
return false;
EC2TemplateOptions other = (EC2TemplateOptions) obj;
if (groupIds == null) {
if (other.groupIds != null)
return false;
} else if (!groupIds.equals(other.groupIds))
return false;
if (keyPair == null) {
if (other.keyPair != null)
return false;
} else if (!keyPair.equals(other.keyPair))
return false;
if (noKeyPair != other.noKeyPair)
return false;
return true;
}
@Override
public String toString() {
return "EC2TemplateOptions [groupIds=" + groupIds + ", keyPair=" + keyPair + ", noKeyPair="
+ noKeyPair + ", inboundPorts=" + Arrays.toString(inboundPorts) + ", privateKey="
+ (privateKey != null) + ", publicKey=" + (publicKey != null) + ", runScript="
+ (script != null) + ", port:seconds=" + port + ":" + seconds
+ ", metadata/details: " + includeMetadata + "]";
}
}

View File

@ -0,0 +1,138 @@
/**
*
* Copyright (C) 2009 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.checkArgument;
import static org.jclouds.aws.ec2.options.RunInstancesOptions.Builder.*;
import java.util.Map;
import java.util.Set;
import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.jclouds.aws.ec2.compute.domain.EC2Size;
import org.jclouds.aws.ec2.compute.domain.RegionAndName;
import org.jclouds.aws.ec2.compute.domain.RegionNameAndIngressRules;
import org.jclouds.aws.ec2.compute.functions.CreateSecurityGroupIfNeeded;
import org.jclouds.aws.ec2.compute.functions.CreateUniqueKeyPair;
import org.jclouds.aws.ec2.compute.options.EC2TemplateOptions;
import org.jclouds.aws.ec2.domain.KeyPair;
import org.jclouds.aws.ec2.options.RunInstancesOptions;
import org.jclouds.compute.domain.Template;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Sets;
/**
*
* @author Adrian Cole
*/
@Singleton
public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions {
@VisibleForTesting
final Map<RegionAndName, KeyPair> credentialsMap;
@VisibleForTesting
final Map<RegionAndName, String> securityGroupMap;
@VisibleForTesting
final CreateUniqueKeyPair createUniqueKeyPair;
@VisibleForTesting
final CreateSecurityGroupIfNeeded createSecurityGroupIfNeeded;
@Inject
CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions(
Map<RegionAndName, KeyPair> credentialsMap,
Map<RegionAndName, String> securityGroupMap, CreateUniqueKeyPair createUniqueKeyPair,
CreateSecurityGroupIfNeeded createSecurityGroupIfNeeded) {
this.credentialsMap = credentialsMap;
this.securityGroupMap = securityGroupMap;
this.createUniqueKeyPair = createUniqueKeyPair;
this.createSecurityGroupIfNeeded = createSecurityGroupIfNeeded;
}
public RunInstancesOptions execute(String region, String tag, Template template) {
checkArgument(template.getSize() instanceof EC2Size,
"unexpected image type. should be EC2Size, was: " + template.getSize().getClass());
EC2Size ec2Size = EC2Size.class.cast(template.getSize());
checkArgument(template.getOptions() instanceof EC2TemplateOptions,
"unexpected options type. should be EC2Options, was: "
+ template.getOptions().getClass());
EC2TemplateOptions options = EC2TemplateOptions.class.cast(template.getOptions());
String keyPairName = createNewKeyPairUnlessUserSpecifiedOtherwise(region, tag, options);
Set<String> groups = getSecurityGroupsForTagAndOptions(region, tag, options);
RunInstancesOptions instanceOptions = asType(ec2Size.getInstanceType())//
.withSecurityGroups(groups)//
.withAdditionalInfo(tag);
if (keyPairName != null)
instanceOptions.withKeyName(keyPairName);
return instanceOptions;
}
@VisibleForTesting
String createNewKeyPairUnlessUserSpecifiedOtherwise(String region, String tag,
EC2TemplateOptions options) {
String keyPairName = options.getKeyPair();
if (keyPairName == null && options.shouldAutomaticallyCreateKeyPair()) {
RegionAndName regionAndName = new RegionAndName(region, tag);
KeyPair keyPair = createUniqueKeyPair.apply(regionAndName);
// get or create incidental resources
// TODO race condition. we were using MapMaker, but it doesn't seem to refresh properly
// when
// another thread
// deletes a key
credentialsMap.put(new RegionAndName(region, keyPair.getKeyName()), keyPair);
keyPairName = keyPair.getKeyName();
}
return keyPairName;
}
@VisibleForTesting
Set<String> getSecurityGroupsForTagAndOptions(String region, @Nullable String tag,
EC2TemplateOptions options) {
Set<String> groups = Sets.newLinkedHashSet();
if (tag != null) {
String markerGroup = "jclouds#" + tag;
groups.add(markerGroup);
RegionNameAndIngressRules regionNameAndIngessRulesForMarkerGroup;
if (options.getGroupIds().size() == 0) {
regionNameAndIngessRulesForMarkerGroup = new RegionNameAndIngressRules(region,
markerGroup, options.getInboundPorts(), true);
} else {
regionNameAndIngessRulesForMarkerGroup = new RegionNameAndIngressRules(region,
markerGroup, new int[] {}, false);
}
if (!securityGroupMap.containsKey(regionNameAndIngessRulesForMarkerGroup)) {
securityGroupMap.put(regionNameAndIngessRulesForMarkerGroup,
createSecurityGroupIfNeeded.apply(regionNameAndIngessRulesForMarkerGroup));
}
}
groups.addAll(options.getGroupIds());
return groups;
}
}

View File

@ -19,48 +19,37 @@
package org.jclouds.aws.ec2.compute.strategy; package org.jclouds.aws.ec2.compute.strategy;
import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.collect.Iterables.all;
import static org.jclouds.aws.ec2.options.RunInstancesOptions.Builder.withKeyName; import static com.google.common.collect.Iterables.concat;
import static org.jclouds.concurrent.ConcurrentUtils.makeListenable; import static com.google.common.collect.Iterables.toArray;
import static com.google.common.collect.Iterables.transform;
import static org.jclouds.aws.ec2.compute.util.EC2ComputeUtils.getRegionFromLocationOrNull;
import static org.jclouds.aws.ec2.compute.util.EC2ComputeUtils.getZoneFromLocationOrNull;
import static org.jclouds.aws.ec2.compute.util.EC2ComputeUtils.instanceToId;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
import javax.inject.Singleton; import javax.inject.Singleton;
import org.jclouds.Constants;
import org.jclouds.aws.ec2.EC2Client;
import org.jclouds.aws.ec2.compute.domain.EC2Size;
import org.jclouds.aws.ec2.compute.domain.PortsRegionTag;
import org.jclouds.aws.ec2.compute.domain.RegionTag;
import org.jclouds.aws.ec2.compute.functions.CreateNewKeyPair;
import org.jclouds.aws.ec2.compute.functions.CreateSecurityGroupIfNeeded;
import org.jclouds.aws.ec2.compute.functions.RunningInstanceToNodeMetadata; import org.jclouds.aws.ec2.compute.functions.RunningInstanceToNodeMetadata;
import org.jclouds.aws.ec2.domain.KeyPair;
import org.jclouds.aws.ec2.domain.Reservation; import org.jclouds.aws.ec2.domain.Reservation;
import org.jclouds.aws.ec2.domain.RunningInstance; import org.jclouds.aws.ec2.domain.RunningInstance;
import org.jclouds.aws.ec2.options.RunInstancesOptions; import org.jclouds.aws.ec2.options.RunInstancesOptions;
import org.jclouds.compute.ComputeService; import org.jclouds.aws.ec2.services.InstanceClient;
import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.domain.Template; import org.jclouds.compute.domain.Template;
import org.jclouds.compute.options.TemplateOptions;
import org.jclouds.compute.reference.ComputeServiceConstants; import org.jclouds.compute.reference.ComputeServiceConstants;
import org.jclouds.compute.strategy.RunNodesAndAddToSetStrategy; import org.jclouds.compute.strategy.RunNodesAndAddToSetStrategy;
import org.jclouds.compute.util.ComputeUtils; import org.jclouds.compute.util.ComputeUtils;
import org.jclouds.domain.LocationScope;
import org.jclouds.logging.Logger; import org.jclouds.logging.Logger;
import com.google.common.base.Function; import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Joiner; import com.google.common.base.Joiner;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import com.google.common.base.Throwables;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
/** /**
@ -70,125 +59,84 @@ import com.google.common.util.concurrent.ListenableFuture;
*/ */
@Singleton @Singleton
public class EC2RunNodesAndAddToSetStrategy implements RunNodesAndAddToSetStrategy { public class EC2RunNodesAndAddToSetStrategy implements RunNodesAndAddToSetStrategy {
@Resource @Resource
@Named(ComputeServiceConstants.COMPUTE_LOGGER) @Named(ComputeServiceConstants.COMPUTE_LOGGER)
protected Logger logger = Logger.NULL; protected Logger logger = Logger.NULL;
private static Function<RunningInstance, String> instanceToId = new Function<RunningInstance, String>() { @VisibleForTesting
@Override final InstanceClient instanceClient;
public String apply(RunningInstance from) { @VisibleForTesting
return from.getId(); final CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions createKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions;
} @VisibleForTesting
}; final Predicate<RunningInstance> instanceStateRunning;
protected final ComputeService computeService; @VisibleForTesting
protected final EC2Client ec2Client; final RunningInstanceToNodeMetadata runningInstanceToNodeMetadata;
protected final Map<RegionTag, KeyPair> credentialsMap; @VisibleForTesting
protected final Map<PortsRegionTag, String> securityGroupMap; final ComputeUtils utils;
protected final CreateNewKeyPair createNewKeyPair;
protected final CreateSecurityGroupIfNeeded createSecurityGroupIfNeeded;
protected final Predicate<RunningInstance> instanceStateRunning;
protected final RunningInstanceToNodeMetadata runningInstanceToNodeMetadata;
protected final ComputeUtils utils;
@Inject @Inject
protected EC2RunNodesAndAddToSetStrategy(ComputeService computeService, EC2Client ec2Client, EC2RunNodesAndAddToSetStrategy(
Map<RegionTag, KeyPair> credentialsMap, Map<PortsRegionTag, String> securityGroupMap, InstanceClient instanceClient,
CreateNewKeyPair createKeyPairIfNeeded, CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions createKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions,
CreateSecurityGroupIfNeeded createSecurityGroupIfNeeded,
@Named("RUNNING") Predicate<RunningInstance> instanceStateRunning, @Named("RUNNING") Predicate<RunningInstance> instanceStateRunning,
RunningInstanceToNodeMetadata runningInstanceToNodeMetadata, ComputeUtils utils, RunningInstanceToNodeMetadata runningInstanceToNodeMetadata, ComputeUtils utils) {
@Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor) { this.instanceClient = instanceClient;
this.computeService = computeService; this.createKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions = createKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions;
this.ec2Client = ec2Client;
this.credentialsMap = credentialsMap;
this.securityGroupMap = securityGroupMap;
this.createNewKeyPair = createKeyPairIfNeeded;
this.createSecurityGroupIfNeeded = createSecurityGroupIfNeeded;
this.instanceStateRunning = instanceStateRunning; this.instanceStateRunning = instanceStateRunning;
this.runningInstanceToNodeMetadata = runningInstanceToNodeMetadata; this.runningInstanceToNodeMetadata = runningInstanceToNodeMetadata;
this.utils = utils; this.utils = utils;
this.executor = executor;
} }
protected final ExecutorService executor;
@Override @Override
public Map<?, ListenableFuture<Void>> execute(final String tag, final int count, public Map<?, ListenableFuture<Void>> execute(String tag, int count, Template template,
final Template template, final Set<NodeMetadata> nodes, Set<NodeMetadata> goodNodes, Map<NodeMetadata, Exception> badNodes) {
final Map<NodeMetadata, Exception> badNodes) {
checkArgument(template.getSize() instanceof EC2Size,
"unexpected image type. should be EC2Size, was: " + template.getSize().getClass());
EC2Size ec2Size = EC2Size.class.cast(template.getSize());
// parse the availability zone of the request Reservation reservation = createKeyPairAndSecurityGroupsAsNeededThenRunInstances(tag, count,
String zone = template.getLocation().getScope() == LocationScope.ZONE ? template template);
.getLocation().getId() : null;
// if the location has a parent, it must be an availability zone. Iterable<NodeMetadata> runningNodes = blockUntilInstancesAreRunningAndConvertToNodes(reservation);
String region = zone == null ? template.getLocation().getId() : template.getLocation()
.getParent().getId();
// get or create incidental resources return utils.runOptionsOnNodesAndAddToGoodSetOrPutExceptionIntoBadMap(template.getOptions(),
// TODO race condition. we were using MapMaker, but it doesn't seem to refresh properly when runningNodes, goodNodes, badNodes);
// another thread
// deletes a key
RegionTag regionTag = new RegionTag(region, tag);
KeyPair keyPair = createNewKeyPair.apply(regionTag);
credentialsMap.put(new RegionTag(region, keyPair.getKeyName()), keyPair);
TemplateOptions options = template.getOptions();
String group = "jclouds#" +tag;
PortsRegionTag portsRegionTag = new PortsRegionTag(region, group, options.getInboundPorts());
if (!securityGroupMap.containsKey(portsRegionTag)) {
securityGroupMap.put(portsRegionTag, createSecurityGroupIfNeeded.apply(portsRegionTag));
} }
logger @VisibleForTesting
.debug( Iterable<NodeMetadata> blockUntilInstancesAreRunningAndConvertToNodes(Reservation reservation) {
">> running %d instance region(%s) zone(%s) ami(%s) type(%s) keyPair(%s) securityGroup(%s)", return transform(blockUntilInstancesAreRunning(reservation), runningInstanceToNodeMetadata);
count, region, zone, template.getImage().getId(), }
ec2Size.getInstanceType(), keyPair.getKeyName(), group);
RunInstancesOptions instanceOptions = withKeyName(keyPair.getKeyName())// key
.asType(ec2Size.getInstanceType())// instance size
.withSecurityGroup(group)// group I created above
.withAdditionalInfo(tag);
Reservation reservation = ec2Client.getInstanceServices().runInstancesInRegion(region, zone, @VisibleForTesting
template.getImage().getId(), 1, count, instanceOptions); Iterable<RunningInstance> blockUntilInstancesAreRunning(Reservation reservation) {
Iterable<String> ids = Iterables.transform(reservation, instanceToId); Iterable<String> ids = transform(reservation, instanceToId);
String idsString = Joiner.on(',').join(ids); String idsString = Joiner.on(',').join(ids);
logger.debug("<< started instances(%s)", idsString); logger.debug("<< started instances(%s)", idsString);
Iterables.all(reservation, instanceStateRunning); all(reservation, instanceStateRunning);
logger.debug("<< running instances(%s)", idsString); logger.debug("<< running instances(%s)", idsString);
Map<NodeMetadata, ListenableFuture<Void>> responses = Maps.newHashMap();
for (final NodeMetadata node : Iterables.transform(getInstances(region, ids), return getInstances(reservation.getRegion(), ids);
runningInstanceToNodeMetadata)) {
responses.put(node, makeListenable(executor.submit(new Callable<Void>() {
@Override
public Void call() throws Exception {
try {
utils.runOptionsOnNode(node, template.getOptions());
logger.debug("<< options applied node(%s)", node.getId());
nodes.add(computeService.getNodeMetadata(node));
} catch (Exception e) {
logger.error(e, "<< problem applying options to node(%s): ", node.getId(),
Throwables.getRootCause(e).getMessage());
badNodes.put(computeService.getNodeMetadata(node), e);
}
return null;
} }
}), executor)); @VisibleForTesting
} Reservation createKeyPairAndSecurityGroupsAsNeededThenRunInstances(String tag, int count,
return responses; Template template) {
String region = getRegionFromLocationOrNull(template.getLocation());
String zone = getZoneFromLocationOrNull(template.getLocation());
RunInstancesOptions instanceOptions = createKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions
.execute(region, tag, template);
if (logger.isDebugEnabled())
logger.debug(">> running %d instance region(%s) zone(%s) ami(%s) params(%s)", count,
region, zone, template.getImage().getId(), instanceOptions.buildFormParameters());
return instanceClient.runInstancesInRegion(region, zone, template.getImage().getId(), 1,
count, instanceOptions);
} }
private Iterable<RunningInstance> getInstances(String region, Iterable<String> ids) { private Iterable<RunningInstance> getInstances(String region, Iterable<String> ids) {
return Iterables.concat(ec2Client.getInstanceServices().describeInstancesInRegion(region, return concat(instanceClient.describeInstancesInRegion(region, toArray(ids, String.class)));
Iterables.toArray(ids, String.class)));
} }
} }

View File

@ -0,0 +1,53 @@
/**
*
* Copyright (C) 2009 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.util;
import javax.inject.Singleton;
import org.jclouds.aws.ec2.domain.RunningInstance;
import org.jclouds.domain.Location;
import org.jclouds.domain.LocationScope;
import com.google.common.base.Function;
/**
*
* @author Adrian Cole
*/
@Singleton
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 getRegionFromLocationOrNull(Location location) {
return location.getScope() == LocationScope.ZONE ? location.getParent().getId() : location
.getId();
}
public static String getZoneFromLocationOrNull(Location location) {
return location.getScope() == LocationScope.ZONE ? location.getId() : null;
}
}

View File

@ -129,4 +129,10 @@ public class IpPermission implements Comparable<IpPermission> {
return true; return true;
} }
@Override
public String toString() {
return "IpPermission [fromPort=" + fromPort + ", groups=" + groups + ", ipProtocol="
+ ipProtocol + ", ipRanges=" + ipRanges + ", toPort=" + toPort + "]";
}
} }

View File

@ -137,4 +137,10 @@ public class SecurityGroup implements Comparable<SecurityGroup> {
return false; return false;
return true; return true;
} }
@Override
public String toString() {
return "SecurityGroup [description=" + description + ", ipPermissions=" + ipPermissions
+ ", name=" + name + ", ownerId=" + ownerId + ", region=" + region + "]";
}
} }

View File

@ -66,6 +66,14 @@ public class RunInstancesOptions extends BaseEC2RequestOptions {
return this; return this;
} }
/**
* Attach multiple security groups
*/
public RunInstancesOptions withSecurityGroups(Iterable<String> securityGroups) {
indexFormValuesWithPrefix("SecurityGroup", securityGroups);
return this;
}
/** /**
* Attaches a single security group. Multiple calls to this method * Attaches a single security group. Multiple calls to this method
* won't add more groups. * won't add more groups.

View File

@ -41,6 +41,10 @@ public class BaseEC2RequestOptions extends BaseHttpRequestOptions {
} }
} }
protected void indexFormValuesWithPrefix(String prefix, Iterable<String> values) {
indexFormValuesWithPrefix(prefix, Iterables.toArray(values, String.class));
}
protected Set<String> getFormValuesWithKeysPrefixedBy(final String prefix) { protected Set<String> getFormValuesWithKeysPrefixedBy(final String prefix) {
Set<String> values = Sets.newLinkedHashSet(); Set<String> values = Sets.newLinkedHashSet();
for (String key : Iterables.filter(formParameters.keySet(), new Predicate<String>() { for (String key : Iterables.filter(formParameters.keySet(), new Predicate<String>() {

View File

@ -20,15 +20,29 @@ package org.jclouds.aws.ec2.compute;
import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertEquals;
import java.util.Set;
import org.jclouds.aws.ec2.EC2Client;
import org.jclouds.aws.ec2.compute.options.EC2TemplateOptions;
import org.jclouds.aws.ec2.domain.IpProtocol;
import org.jclouds.aws.ec2.domain.KeyPair;
import org.jclouds.aws.ec2.domain.RunningInstance;
import org.jclouds.aws.ec2.domain.SecurityGroup;
import org.jclouds.aws.ec2.services.InstanceClient;
import org.jclouds.aws.ec2.services.KeyPairClient;
import org.jclouds.aws.ec2.services.SecurityGroupClient;
import org.jclouds.compute.BaseComputeServiceLiveTest; import org.jclouds.compute.BaseComputeServiceLiveTest;
import org.jclouds.compute.domain.Architecture; import org.jclouds.compute.domain.Architecture;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.domain.OsFamily; import org.jclouds.compute.domain.OsFamily;
import org.jclouds.compute.domain.Template; import org.jclouds.compute.domain.Template;
import org.jclouds.compute.domain.TemplateBuilder; import org.jclouds.compute.domain.TemplateBuilder;
import org.jclouds.domain.Credentials;
import org.jclouds.ssh.jsch.config.JschSshClientModule; import org.jclouds.ssh.jsch.config.JschSshClientModule;
import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
/** /**
@ -73,4 +87,136 @@ public class EC2ComputeServiceLiveTest extends BaseComputeServiceLiveTest {
return templateBuilder.imageId("ami-714ba518").build(); return templateBuilder.imageId("ami-714ba518").build();
} }
@Test
public void testExtendedOptionsAndLogin() throws Exception {
SecurityGroupClient securityGroupClient = EC2Client.class.cast(
context.getProviderSpecificContext().getApi()).getSecurityGroupServices();
KeyPairClient keyPairClient = EC2Client.class.cast(
context.getProviderSpecificContext().getApi()).getKeyPairServices();
InstanceClient instanceClient = EC2Client.class.cast(
context.getProviderSpecificContext().getApi()).getInstanceServices();
String tag = this.tag + "optionsandlogin";
Template template = buildTemplate(client.templateBuilder());
template.getOptions().as(EC2TemplateOptions.class).securityGroups(tag);
template.getOptions().as(EC2TemplateOptions.class).keyPair(tag);
String startedId = null;
try {
// create a security group that allows ssh in so that our scripts later will work
securityGroupClient.createSecurityGroupInRegion(null, tag, tag);
securityGroupClient.authorizeSecurityGroupIngressInRegion(null, tag, IpProtocol.TCP, 22,
22, "0.0.0.0/0");
// create a keypair to pass in as well
KeyPair result = keyPairClient.createKeyPairInRegion(null, tag);
Set<? extends NodeMetadata> nodes = client.runNodesWithTag(tag, 1, template);
Credentials good = nodes.iterator().next().getCredentials();
assert good.account != null;
startedId = Iterables.getOnlyElement(nodes).getId();
RunningInstance instance = getInstance(instanceClient, startedId);
assertEquals(instance.getKeyName(), tag);
// make sure we made our dummy group and also let in the user's group
assertEquals(instance.getGroupIds(), ImmutableSet.<String> of(tag, "jclouds#" + tag));
// make sure our dummy group has no rules
SecurityGroup group = Iterables.getOnlyElement(securityGroupClient
.describeSecurityGroupsInRegion(null, "jclouds#" + tag));
assert group.getIpPermissions().size() == 0 : group;
// try to run a script with the original keyPair
runScriptWithCreds(tag, template.getImage().getOsFamily(), new Credentials(good.account,
result.getKeyMaterial()));
} finally {
client.destroyNodesWithTag(tag);
if (startedId != null) {
// ensure we didn't delete these resources!
assertEquals(keyPairClient.describeKeyPairsInRegion(null, tag).size(), 1);
assertEquals(securityGroupClient.describeSecurityGroupsInRegion(null, tag).size(), 1);
}
cleanupExtendedStuff(securityGroupClient, keyPairClient, tag);
}
}
@Test
public void testExtendedOptionsNoKeyPair() throws Exception {
SecurityGroupClient securityGroupClient = EC2Client.class.cast(
context.getProviderSpecificContext().getApi()).getSecurityGroupServices();
KeyPairClient keyPairClient = EC2Client.class.cast(
context.getProviderSpecificContext().getApi()).getKeyPairServices();
InstanceClient instanceClient = EC2Client.class.cast(
context.getProviderSpecificContext().getApi()).getInstanceServices();
String tag = this.tag + "optionsnokey";
Template template = buildTemplate(client.templateBuilder());
template.getOptions().as(EC2TemplateOptions.class).securityGroups(tag);
template.getOptions().as(EC2TemplateOptions.class).noKeyPair();
String startedId = null;
try {
// create the security group
securityGroupClient.createSecurityGroupInRegion(null, tag, tag);
Set<? extends NodeMetadata> nodes = client.runNodesWithTag(tag, 1, template);
Credentials creds = nodes.iterator().next().getCredentials();
assert creds == null;
startedId = Iterables.getOnlyElement(nodes).getId();
RunningInstance instance = getInstance(instanceClient, startedId);
assertEquals(instance.getKeyName(), null);
// make sure we made our dummy group and also let in the user's group
assertEquals(instance.getGroupIds(), ImmutableSet.<String> of(tag, "jclouds#" + tag));
// make sure our dummy group has no rules
SecurityGroup group = Iterables.getOnlyElement(securityGroupClient
.describeSecurityGroupsInRegion(null, "jclouds#" + tag));
assert group.getIpPermissions().size() == 0 : group;
} finally {
client.destroyNodesWithTag(tag);
if (startedId != null) {
// ensure we didn't delete these resources!
assertEquals(securityGroupClient.describeSecurityGroupsInRegion(null, tag).size(), 1);
}
cleanupExtendedStuff(securityGroupClient, keyPairClient, tag);
}
}
private RunningInstance getInstance(InstanceClient instanceClient, String id) {
RunningInstance instance = Iterables.getOnlyElement(Iterables.getOnlyElement(instanceClient
.describeInstancesInRegion(null, id)));
return instance;
}
private void cleanupExtendedStuff(SecurityGroupClient securityGroupClient,
KeyPairClient keyPairClient, String tag) {
try {
securityGroupClient.deleteSecurityGroupInRegion(null, tag);
} catch (Exception e) {
}
try {
keyPairClient.deleteKeyPairInRegion(null, tag);
} catch (Exception e) {
}
}
} }

View File

@ -27,6 +27,7 @@ package org.jclouds.aws.ec2.compute;
import static java.lang.String.format; import static java.lang.String.format;
import org.jclouds.aws.ec2.compute.domain.EC2Size; import org.jclouds.aws.ec2.compute.domain.EC2Size;
import org.jclouds.aws.ec2.compute.options.EC2TemplateOptions;
import org.jclouds.compute.domain.Architecture; import org.jclouds.compute.domain.Architecture;
import org.jclouds.compute.domain.ComputeMetadata; import org.jclouds.compute.domain.ComputeMetadata;
import org.jclouds.compute.domain.Image; import org.jclouds.compute.domain.Image;
@ -118,7 +119,7 @@ public class EC2ComputeServiceTest {
return new TemplateBuilderImpl(ImmutableSet.of(location), ImmutableSet.of(image), return new TemplateBuilderImpl(ImmutableSet.of(location), ImmutableSet.of(image),
ImmutableSet.of(EC2Size.C1_MEDIUM, EC2Size.C1_XLARGE, EC2Size.M1_LARGE, ImmutableSet.of(EC2Size.C1_MEDIUM, EC2Size.C1_XLARGE, EC2Size.M1_LARGE,
EC2Size.M1_SMALL, EC2Size.M1_XLARGE, EC2Size.M2_XLARGE, EC2Size.M2_2XLARGE, EC2Size.M1_SMALL, EC2Size.M1_XLARGE, EC2Size.M2_XLARGE, EC2Size.M2_2XLARGE,
EC2Size.M2_4XLARGE), location) { EC2Size.M2_4XLARGE), location, new EC2TemplateOptions()) {
}; };
} }

View File

@ -31,7 +31,7 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import org.jclouds.aws.domain.Region; import org.jclouds.aws.domain.Region;
import org.jclouds.aws.ec2.compute.domain.RegionTag; import org.jclouds.aws.ec2.compute.domain.RegionAndName;
import org.jclouds.aws.ec2.domain.AvailabilityZone; import org.jclouds.aws.ec2.domain.AvailabilityZone;
import org.jclouds.aws.ec2.domain.Image; import org.jclouds.aws.ec2.domain.Image;
import org.jclouds.aws.ec2.domain.InstanceState; import org.jclouds.aws.ec2.domain.InstanceState;
@ -60,7 +60,7 @@ public class RunningInstanceToNodeMetadataTest {
public void testApplyWithNoSecurityGroupCreatesTagOfIdPrefixedByTagAndNullCredentials() public void testApplyWithNoSecurityGroupCreatesTagOfIdPrefixedByTagAndNullCredentials()
throws UnknownHostException { throws UnknownHostException {
AMIClient amiClient = createMock(AMIClient.class); AMIClient amiClient = createMock(AMIClient.class);
Map<RegionTag, KeyPair> credentialsMap = createMock(Map.class); Map<RegionAndName, KeyPair> credentialsMap = createMock(Map.class);
org.jclouds.compute.domain.Image jcImage = createMock(org.jclouds.compute.domain.Image.class); org.jclouds.compute.domain.Image jcImage = createMock(org.jclouds.compute.domain.Image.class);
Set<org.jclouds.compute.domain.Image> images = ImmutableSet Set<org.jclouds.compute.domain.Image> images = ImmutableSet
@ -117,7 +117,7 @@ public class RunningInstanceToNodeMetadataTest {
public void testApplyWithNoKeyPairCreatesTagOfParsedSecurityGroupAndNullCredentials() public void testApplyWithNoKeyPairCreatesTagOfParsedSecurityGroupAndNullCredentials()
throws UnknownHostException { throws UnknownHostException {
AMIClient amiClient = createMock(AMIClient.class); AMIClient amiClient = createMock(AMIClient.class);
Map<RegionTag, KeyPair> credentialsMap = createMock(Map.class); Map<RegionAndName, KeyPair> credentialsMap = createMock(Map.class);
org.jclouds.compute.domain.Image jcImage = createMock(org.jclouds.compute.domain.Image.class); org.jclouds.compute.domain.Image jcImage = createMock(org.jclouds.compute.domain.Image.class);
Set<org.jclouds.compute.domain.Image> images = ImmutableSet Set<org.jclouds.compute.domain.Image> images = ImmutableSet
@ -174,7 +174,7 @@ public class RunningInstanceToNodeMetadataTest {
public void testApplyWithKeyPairCreatesTagOfParsedSecurityGroupAndCredentialsBasedOnIt() public void testApplyWithKeyPairCreatesTagOfParsedSecurityGroupAndCredentialsBasedOnIt()
throws UnknownHostException { throws UnknownHostException {
AMIClient amiClient = createMock(AMIClient.class); AMIClient amiClient = createMock(AMIClient.class);
Map<RegionTag, KeyPair> credentialsMap = createMock(Map.class); Map<RegionAndName, KeyPair> credentialsMap = createMock(Map.class);
PopulateDefaultLoginCredentialsForImageStrategy credentialProvider = createMock(PopulateDefaultLoginCredentialsForImageStrategy.class); PopulateDefaultLoginCredentialsForImageStrategy credentialProvider = createMock(PopulateDefaultLoginCredentialsForImageStrategy.class);
RunningInstance instance = createMock(RunningInstance.class); RunningInstance instance = createMock(RunningInstance.class);
@ -207,7 +207,7 @@ public class RunningInstanceToNodeMetadataTest {
expect(credentialProvider.execute(image)).andReturn(new Credentials("user", "pass")); expect(credentialProvider.execute(image)).andReturn(new Credentials("user", "pass"));
expect(credentialsMap.get(new RegionTag(Region.US_EAST_1, "jclouds#keyName"))).andReturn( expect(credentialsMap.get(new RegionAndName(Region.US_EAST_1, "jclouds#keyName"))).andReturn(
new KeyPair(Region.US_EAST_1, "jclouds#keyName", "keyFingerprint", "pass")); new KeyPair(Region.US_EAST_1, "jclouds#keyName", "keyFingerprint", "pass"));
expect(instance.getAvailabilityZone()).andReturn(AvailabilityZone.US_EAST_1A).atLeastOnce(); expect(instance.getAvailabilityZone()).andReturn(AvailabilityZone.US_EAST_1A).atLeastOnce();
@ -245,7 +245,7 @@ public class RunningInstanceToNodeMetadataTest {
public void testApplyWithTwoSecurityGroups() public void testApplyWithTwoSecurityGroups()
throws UnknownHostException { throws UnknownHostException {
AMIClient amiClient = createMock(AMIClient.class); AMIClient amiClient = createMock(AMIClient.class);
Map<RegionTag, KeyPair> credentialsMap = createMock(Map.class); Map<RegionAndName, KeyPair> credentialsMap = createMock(Map.class);
PopulateDefaultLoginCredentialsForImageStrategy credentialProvider = createMock(PopulateDefaultLoginCredentialsForImageStrategy.class); PopulateDefaultLoginCredentialsForImageStrategy credentialProvider = createMock(PopulateDefaultLoginCredentialsForImageStrategy.class);
RunningInstance instance = createMock(RunningInstance.class); RunningInstance instance = createMock(RunningInstance.class);
@ -278,7 +278,7 @@ public class RunningInstanceToNodeMetadataTest {
expect(credentialProvider.execute(image)).andReturn(new Credentials("user", "pass")); expect(credentialProvider.execute(image)).andReturn(new Credentials("user", "pass"));
expect(credentialsMap.get(new RegionTag(Region.US_EAST_1, "jclouds#keyName"))).andReturn( expect(credentialsMap.get(new RegionAndName(Region.US_EAST_1, "jclouds#keyName"))).andReturn(
new KeyPair(Region.US_EAST_1, "jclouds#keyName", "keyFingerprint", "pass")); new KeyPair(Region.US_EAST_1, "jclouds#keyName", "keyFingerprint", "pass"));
expect(instance.getAvailabilityZone()).andReturn(AvailabilityZone.US_EAST_1A).atLeastOnce(); expect(instance.getAvailabilityZone()).andReturn(AvailabilityZone.US_EAST_1A).atLeastOnce();

View File

@ -0,0 +1,289 @@
/**
*
* Copyright (C) 2009 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.options;
import static org.jclouds.aws.ec2.compute.options.EC2TemplateOptions.Builder.authorizePublicKey;
import static org.jclouds.aws.ec2.compute.options.EC2TemplateOptions.Builder.blockOnPort;
import static org.jclouds.aws.ec2.compute.options.EC2TemplateOptions.Builder.inboundPorts;
import static org.jclouds.aws.ec2.compute.options.EC2TemplateOptions.Builder.installPrivateKey;
import static org.jclouds.aws.ec2.compute.options.EC2TemplateOptions.Builder.keyPair;
import static org.jclouds.aws.ec2.compute.options.EC2TemplateOptions.Builder.noKeyPair;
import static org.jclouds.aws.ec2.compute.options.EC2TemplateOptions.Builder.securityGroups;
import static org.testng.Assert.assertEquals;
import org.jclouds.compute.options.TemplateOptions;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableSet;
/**
* Tests possible uses of EC2TemplateOptions and EC2TemplateOptions.Builder.*
*
* @author Adrian Cole
*/
public class EC2TemplateOptionsTest {
public void testAs() {
TemplateOptions options = new EC2TemplateOptions();
assertEquals(options.as(EC2TemplateOptions.class), options);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testsecurityGroupsIterableBadFormat() {
EC2TemplateOptions options = new EC2TemplateOptions();
options.securityGroups(ImmutableSet.of("group1", ""));
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testsecurityGroupsIterableEmptyNotOk() {
EC2TemplateOptions options = new EC2TemplateOptions();
options.securityGroups(ImmutableSet.<String> of());
}
@Test
public void testsecurityGroupsIterable() {
EC2TemplateOptions options = new EC2TemplateOptions();
options.securityGroups(ImmutableSet.of("group1", "group2"));
assertEquals(options.getGroupIds(), ImmutableSet.of("group1", "group2"));
}
@Test
public void testsecurityGroupsIterableStatic() {
EC2TemplateOptions options = securityGroups(ImmutableSet.of("group1", "group2"));
assertEquals(options.getGroupIds(), ImmutableSet.of("group1", "group2"));
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testsecurityGroupsVarArgsBadFormat() {
EC2TemplateOptions options = new EC2TemplateOptions();
options.securityGroups("mygroup", "");
}
@Test
public void testsecurityGroupsVarArgs() {
EC2TemplateOptions options = new EC2TemplateOptions();
options.securityGroups("group1", "group2");
assertEquals(options.getGroupIds(), ImmutableSet.of("group1", "group2"));
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testsecurityGroupsVarArgsEmptyNotOk() {
EC2TemplateOptions options = new EC2TemplateOptions();
options.securityGroups();
}
@Test
public void testDefaultGroupsVarArgsEmpty() {
EC2TemplateOptions options = new EC2TemplateOptions();
assertEquals(options.getGroupIds(), ImmutableSet.of());
}
@Test
public void testsecurityGroupsVarArgsStatic() {
EC2TemplateOptions options = securityGroups("group1", "group2");
assertEquals(options.getGroupIds(), ImmutableSet.of("group1", "group2"));
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testkeyPairBadFormat() {
EC2TemplateOptions options = new EC2TemplateOptions();
options.keyPair("");
}
@Test(expectedExceptions = IllegalStateException.class)
public void testkeyPairAndNoKeyPair() {
EC2TemplateOptions options = new EC2TemplateOptions();
options.keyPair("mykeypair");
options.noKeyPair();
}
@Test(expectedExceptions = IllegalStateException.class)
public void testNoKeyPairAndKeyPair() {
EC2TemplateOptions options = new EC2TemplateOptions();
options.noKeyPair();
options.keyPair("mykeypair");
}
@Test
public void testkeyPair() {
EC2TemplateOptions options = new EC2TemplateOptions();
options.keyPair("mykeypair");
assertEquals(options.getKeyPair(), "mykeypair");
}
@Test
public void testNullkeyPair() {
EC2TemplateOptions options = new EC2TemplateOptions();
assertEquals(options.getKeyPair(), null);
}
@Test
public void testkeyPairStatic() {
EC2TemplateOptions options = keyPair("mykeypair");
assertEquals(options.getKeyPair(), "mykeypair");
}
@Test(expectedExceptions = NullPointerException.class)
public void testkeyPairNPE() {
keyPair(null);
}
@Test
public void testnoKeyPair() {
EC2TemplateOptions options = new EC2TemplateOptions();
options.noKeyPair();
assertEquals(options.getKeyPair(), null);
assert !options.shouldAutomaticallyCreateKeyPair();
}
@Test
public void testFalsenoKeyPair() {
EC2TemplateOptions options = new EC2TemplateOptions();
assertEquals(options.getKeyPair(), null);
assert options.shouldAutomaticallyCreateKeyPair();
}
@Test
public void testnoKeyPairStatic() {
EC2TemplateOptions options = noKeyPair();
assertEquals(options.getKeyPair(), null);
assert !options.shouldAutomaticallyCreateKeyPair();
}
// superclass tests
@Test(expectedExceptions = IllegalArgumentException.class)
public void testinstallPrivateKeyBadFormat() {
EC2TemplateOptions options = new EC2TemplateOptions();
options.installPrivateKey("whompy");
}
@Test
public void testinstallPrivateKey() {
EC2TemplateOptions options = new EC2TemplateOptions();
options.installPrivateKey("-----BEGIN RSA PRIVATE KEY-----");
assertEquals(options.getPrivateKey(), "-----BEGIN RSA PRIVATE KEY-----");
}
@Test
public void testNullinstallPrivateKey() {
EC2TemplateOptions options = new EC2TemplateOptions();
assertEquals(options.getPrivateKey(), null);
}
@Test
public void testinstallPrivateKeyStatic() {
EC2TemplateOptions options = installPrivateKey("-----BEGIN RSA PRIVATE KEY-----");
assertEquals(options.getPrivateKey(), "-----BEGIN RSA PRIVATE KEY-----");
}
@Test(expectedExceptions = NullPointerException.class)
public void testinstallPrivateKeyNPE() {
installPrivateKey(null);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testauthorizePublicKeyBadFormat() {
EC2TemplateOptions options = new EC2TemplateOptions();
options.authorizePublicKey("whompy");
}
@Test
public void testauthorizePublicKey() {
EC2TemplateOptions options = new EC2TemplateOptions();
options.authorizePublicKey("ssh-rsa");
assertEquals(options.getPublicKey(), "ssh-rsa");
}
@Test
public void testNullauthorizePublicKey() {
EC2TemplateOptions options = new EC2TemplateOptions();
assertEquals(options.getPublicKey(), null);
}
@Test
public void testauthorizePublicKeyStatic() {
EC2TemplateOptions options = authorizePublicKey("ssh-rsa");
assertEquals(options.getPublicKey(), "ssh-rsa");
}
@Test(expectedExceptions = NullPointerException.class)
public void testauthorizePublicKeyNPE() {
authorizePublicKey(null);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testblockOnPortBadFormat() {
EC2TemplateOptions options = new EC2TemplateOptions();
options.blockOnPort(-1, -1);
}
@Test
public void testblockOnPort() {
EC2TemplateOptions options = new EC2TemplateOptions();
options.blockOnPort(22, 30);
assertEquals(options.getPort(), 22);
assertEquals(options.getSeconds(), 30);
}
@Test
public void testNullblockOnPort() {
EC2TemplateOptions options = new EC2TemplateOptions();
assertEquals(options.getPort(), -1);
assertEquals(options.getSeconds(), -1);
}
@Test
public void testblockOnPortStatic() {
EC2TemplateOptions options = blockOnPort(22, 30);
assertEquals(options.getPort(), 22);
assertEquals(options.getSeconds(), 30);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testinboundPortsBadFormat() {
EC2TemplateOptions options = new EC2TemplateOptions();
options.inboundPorts(-1, -1);
}
@Test
public void testinboundPorts() {
EC2TemplateOptions options = new EC2TemplateOptions();
options.inboundPorts(22, 30);
assertEquals(options.getInboundPorts()[0], 22);
assertEquals(options.getInboundPorts()[1], 30);
}
@Test
public void testDefaultOpen22() {
EC2TemplateOptions options = new EC2TemplateOptions();
assertEquals(options.getInboundPorts()[0], 22);
}
@Test
public void testinboundPortsStatic() {
EC2TemplateOptions options = inboundPorts(22, 30);
assertEquals(options.getInboundPorts()[0], 22);
assertEquals(options.getInboundPorts()[1], 30);
}
}

View File

@ -0,0 +1,376 @@
/**
*
* Copyright (C) 2009 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 org.easymock.EasyMock.expect;
import static org.easymock.classextension.EasyMock.createMock;
import static org.easymock.classextension.EasyMock.replay;
import static org.easymock.classextension.EasyMock.verify;
import static org.testng.Assert.assertEquals;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Set;
import org.jclouds.aws.domain.Region;
import org.jclouds.aws.ec2.compute.domain.EC2Size;
import org.jclouds.aws.ec2.compute.domain.RegionAndName;
import org.jclouds.aws.ec2.compute.domain.RegionNameAndIngressRules;
import org.jclouds.aws.ec2.compute.functions.CreateSecurityGroupIfNeeded;
import org.jclouds.aws.ec2.compute.functions.CreateUniqueKeyPair;
import org.jclouds.aws.ec2.compute.options.EC2TemplateOptions;
import org.jclouds.aws.ec2.domain.KeyPair;
import org.jclouds.aws.ec2.options.RunInstancesOptions;
import org.jclouds.compute.domain.Template;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSet;
/**
* @author Adrian Cole
*/
@Test(groups = "unit", testName = "ec2.CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest")
public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest {
public void testExecuteWithDefaultOptions() throws SecurityException, NoSuchMethodException {
// setup constants
String region = Region.AP_SOUTHEAST_1;
String tag = "tag";
EC2Size size = EC2Size.M1_SMALL;
String systemGeneratedKeyPairName = "systemGeneratedKeyPair";
String generatedGroup = "group";
Set<String> generatedGroups = ImmutableSet.of(generatedGroup);
// create mocks
CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions strategy = createMock(
CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.class, new Method[] {
CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.class
.getDeclaredMethod("createNewKeyPairUnlessUserSpecifiedOtherwise",
String.class, String.class, EC2TemplateOptions.class),
CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.class
.getDeclaredMethod("getSecurityGroupsForTagAndOptions",
String.class, String.class, EC2TemplateOptions.class) });
EC2TemplateOptions options = createMock(EC2TemplateOptions.class);
Template template = createMock(Template.class);
// setup expectations
expect(template.getSize()).andReturn(size).atLeastOnce();
expect(template.getOptions()).andReturn(options).atLeastOnce();
expect(strategy.createNewKeyPairUnlessUserSpecifiedOtherwise(region, tag, options))
.andReturn(systemGeneratedKeyPairName);
expect(strategy.getSecurityGroupsForTagAndOptions(region, tag, options)).andReturn(
generatedGroups);
// replay mocks
replay(options);
replay(template);
replay(strategy);
// run
RunInstancesOptions runOptions = strategy.execute(region, tag, template);
assertEquals(runOptions.buildQueryParameters(), ImmutableMultimap.<String, String> of());
assertEquals(runOptions.buildFormParameters().entries(), ImmutableMultimap
.<String, String> of("InstanceType", size.getId(), "SecurityGroup.1",
generatedGroup, "AdditionalInfo", tag, "KeyName",
systemGeneratedKeyPairName).entries());
assertEquals(runOptions.buildMatrixParameters(), ImmutableMultimap.<String, String> of());
assertEquals(runOptions.buildRequestHeaders(), ImmutableMultimap.<String, String> of());
assertEquals(runOptions.buildStringPayload(), null);
// verify mocks
verify(options);
verify(template);
verify(strategy);
}
public void testCreateNewKeyPairUnlessUserSpecifiedOtherwise_reusesKeyWhenToldTo() {
// setup constants
String region = Region.AP_SOUTHEAST_1;
String tag = "tag";
String userSuppliedKeyPair = "myKeyPair";
// create mocks
CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions strategy = setupStrategy();
EC2TemplateOptions options = createMock(EC2TemplateOptions.class);
KeyPair keyPair = createMock(KeyPair.class);
// setup expectations
expect(options.getKeyPair()).andReturn(userSuppliedKeyPair);
// replay mocks
replay(options);
replay(keyPair);
replayStrategy(strategy);
// run
assertEquals(strategy.createNewKeyPairUnlessUserSpecifiedOtherwise(region, tag, options),
userSuppliedKeyPair);
// verify mocks
verify(options);
verify(keyPair);
verifyStrategy(strategy);
}
public void testCreateNewKeyPairUnlessUserSpecifiedOtherwise_createsNewKeyPairAndReturnsItsNameByDefault() {
// setup constants
String region = Region.AP_SOUTHEAST_1;
String tag = "tag";
String userSuppliedKeyPair = null;
boolean shouldAutomaticallyCreateKeyPair = true;
String systemGeneratedKeyPairName = "systemGeneratedKeyPair";
// create mocks
CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions strategy = setupStrategy();
EC2TemplateOptions options = createMock(EC2TemplateOptions.class);
KeyPair keyPair = createMock(KeyPair.class);
// setup expectations
expect(options.getKeyPair()).andReturn(userSuppliedKeyPair);
expect(options.shouldAutomaticallyCreateKeyPair())
.andReturn(shouldAutomaticallyCreateKeyPair);
expect(strategy.createUniqueKeyPair.apply(new RegionAndName(region, tag))).andReturn(keyPair);
expect(keyPair.getKeyName()).andReturn(systemGeneratedKeyPairName).atLeastOnce();
expect(
strategy.credentialsMap.put(new RegionAndName(region, systemGeneratedKeyPairName),
keyPair)).andReturn(null);
// replay mocks
replay(options);
replay(keyPair);
replayStrategy(strategy);
// run
assertEquals(strategy.createNewKeyPairUnlessUserSpecifiedOtherwise(region, tag, options),
systemGeneratedKeyPairName);
// verify mocks
verify(options);
verify(keyPair);
verifyStrategy(strategy);
}
public void testCreateNewKeyPairUnlessUserSpecifiedOtherwise_doesntCreateAKeyPairAndReturnsNullWhenToldNotTo() {
// setup constants
String region = Region.AP_SOUTHEAST_1;
String tag = "tag";
String userSuppliedKeyPair = null;
boolean shouldAutomaticallyCreateKeyPair = false; // here's the important part!
// create mocks
CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions strategy = setupStrategy();
EC2TemplateOptions options = createMock(EC2TemplateOptions.class);
KeyPair keyPair = createMock(KeyPair.class);
// setup expectations
expect(options.getKeyPair()).andReturn(userSuppliedKeyPair);
expect(options.shouldAutomaticallyCreateKeyPair())
.andReturn(shouldAutomaticallyCreateKeyPair);
// replay mocks
replay(options);
replay(keyPair);
replayStrategy(strategy);
// run
assertEquals(strategy.createNewKeyPairUnlessUserSpecifiedOtherwise(region, tag, options),
null);
// verify mocks
verify(options);
verify(keyPair);
verifyStrategy(strategy);
}
public void testGetSecurityGroupsForTagAndOptions_createsNewGroupByDefaultWhenNoPortsAreSpecifiedWhenDoesntExist() {
// setup constants
String region = Region.AP_SOUTHEAST_1;
String tag = "tag";
String generatedMarkerGroup = "jclouds#tag";
Set<String> groupIds = ImmutableSet.<String> of();
int[] ports = new int[] {};
boolean shouldAuthorizeSelf = true;
boolean groupExisted = false;
Set<String> returnVal = ImmutableSet.<String> of(generatedMarkerGroup);
// create mocks
CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions strategy = setupStrategy();
EC2TemplateOptions options = createMock(EC2TemplateOptions.class);
// setup expectations
expect(options.getGroupIds()).andReturn(groupIds).atLeastOnce();
expect(options.getInboundPorts()).andReturn(ports).atLeastOnce();
RegionNameAndIngressRules regionNameAndIngressRules = new RegionNameAndIngressRules(region,
generatedMarkerGroup, ports, shouldAuthorizeSelf);
expect(strategy.securityGroupMap.containsKey(regionNameAndIngressRules)).andReturn(
groupExisted);
expect(strategy.createSecurityGroupIfNeeded.apply(regionNameAndIngressRules)).andReturn(
generatedMarkerGroup);
expect(strategy.securityGroupMap.put(regionNameAndIngressRules, generatedMarkerGroup))
.andReturn(null);
// replay mocks
replay(options);
replayStrategy(strategy);
// run
assertEquals(strategy.getSecurityGroupsForTagAndOptions(region, tag, options), returnVal);
// verify mocks
verify(options);
verifyStrategy(strategy);
}
public void testGetSecurityGroupsForTagAndOptions_createsNewGroupByDefaultWhenPortsAreSpecifiedWhenDoesntExist() {
// setup constants
String region = Region.AP_SOUTHEAST_1;
String tag = "tag";
String generatedMarkerGroup = "jclouds#tag";
Set<String> groupIds = ImmutableSet.<String> of();
int[] ports = new int[] { 22, 80 };
boolean shouldAuthorizeSelf = true;
boolean groupExisted = false;
Set<String> returnVal = ImmutableSet.<String> of(generatedMarkerGroup);
// create mocks
CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions strategy = setupStrategy();
EC2TemplateOptions options = createMock(EC2TemplateOptions.class);
// setup expectations
expect(options.getGroupIds()).andReturn(groupIds).atLeastOnce();
expect(options.getInboundPorts()).andReturn(ports).atLeastOnce();
RegionNameAndIngressRules regionNameAndIngressRules = new RegionNameAndIngressRules(region,
generatedMarkerGroup, ports, shouldAuthorizeSelf);
expect(strategy.securityGroupMap.containsKey(regionNameAndIngressRules)).andReturn(
groupExisted);
expect(strategy.createSecurityGroupIfNeeded.apply(regionNameAndIngressRules)).andReturn(
generatedMarkerGroup);
expect(strategy.securityGroupMap.put(regionNameAndIngressRules, generatedMarkerGroup))
.andReturn(null);
// replay mocks
replay(options);
replayStrategy(strategy);
// run
assertEquals(strategy.getSecurityGroupsForTagAndOptions(region, tag, options), returnVal);
// verify mocks
verify(options);
verifyStrategy(strategy);
}
public void testGetSecurityGroupsForTagAndOptions_reusesGroupByDefaultWhenNoPortsAreSpecifiedWhenDoesExist() {
// setup constants
String region = Region.AP_SOUTHEAST_1;
String tag = "tag";
String generatedMarkerGroup = "jclouds#tag";
Set<String> groupIds = ImmutableSet.<String> of();
int[] ports = new int[] {};
boolean shouldAuthorizeSelf = true;
boolean groupExisted = true;
Set<String> returnVal = ImmutableSet.<String> of(generatedMarkerGroup);
// create mocks
CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions strategy = setupStrategy();
EC2TemplateOptions options = createMock(EC2TemplateOptions.class);
// setup expectations
expect(options.getGroupIds()).andReturn(groupIds).atLeastOnce();
expect(options.getInboundPorts()).andReturn(ports).atLeastOnce();
RegionNameAndIngressRules regionNameAndIngressRules = new RegionNameAndIngressRules(region,
generatedMarkerGroup, ports, shouldAuthorizeSelf);
expect(strategy.securityGroupMap.containsKey(regionNameAndIngressRules)).andReturn(
groupExisted);
// replay mocks
replay(options);
replayStrategy(strategy);
// run
assertEquals(strategy.getSecurityGroupsForTagAndOptions(region, tag, options), returnVal);
// verify mocks
verify(options);
verifyStrategy(strategy);
}
public void testGetSecurityGroupsForTagAndOptions_reusesGroupByDefaultWhenNoPortsAreSpecifiedWhenDoesExistAndAcceptsUserSuppliedGroups() {
// setup constants
String region = Region.AP_SOUTHEAST_1;
String tag = "tag";
String generatedMarkerGroup = "jclouds#tag";
Set<String> groupIds = ImmutableSet.<String> of("group1", "group2");
int[] ports = new int[] {};
boolean shouldAuthorizeSelf = true;
boolean groupExisted = true;
Set<String> returnVal = ImmutableSet.<String> of(generatedMarkerGroup, "group1", "group2");
// create mocks
CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions strategy = setupStrategy();
EC2TemplateOptions options = createMock(EC2TemplateOptions.class);
// setup expectations
expect(options.getGroupIds()).andReturn(groupIds).atLeastOnce();
RegionNameAndIngressRules regionNameAndIngressRules = new RegionNameAndIngressRules(region,
generatedMarkerGroup, ports, shouldAuthorizeSelf); // note this works since there's
// no equals on portsq
expect(strategy.securityGroupMap.containsKey(regionNameAndIngressRules)).andReturn(
groupExisted);
// replay mocks
replay(options);
replayStrategy(strategy);
// run
assertEquals(strategy.getSecurityGroupsForTagAndOptions(region, tag, options), returnVal);
// verify mocks
verify(options);
verifyStrategy(strategy);
}
private void verifyStrategy(CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions strategy) {
verify(strategy.credentialsMap);
verify(strategy.securityGroupMap);
verify(strategy.createUniqueKeyPair);
verify(strategy.createSecurityGroupIfNeeded);
}
@SuppressWarnings("unchecked")
private CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions setupStrategy() {
Map<RegionAndName, KeyPair> credentialsMap = createMock(Map.class);
Map<RegionAndName, String> securityGroupMap = createMock(Map.class);
CreateUniqueKeyPair createUniqueKeyPair = createMock(CreateUniqueKeyPair.class);
CreateSecurityGroupIfNeeded createSecurityGroupIfNeeded = createMock(CreateSecurityGroupIfNeeded.class);
return new CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions(credentialsMap,
securityGroupMap, createUniqueKeyPair, createSecurityGroupIfNeeded);
}
private void replayStrategy(CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions strategy) {
replay(strategy.credentialsMap);
replay(strategy.securityGroupMap);
replay(strategy.createUniqueKeyPair);
replay(strategy.createSecurityGroupIfNeeded);
}
}

View File

@ -0,0 +1,216 @@
/**
*
* Copyright (C) 2009 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 org.easymock.EasyMock.eq;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.reportMatcher;
import static org.easymock.classextension.EasyMock.createMock;
import static org.easymock.classextension.EasyMock.replay;
import static org.easymock.classextension.EasyMock.verify;
import java.util.Map;
import java.util.Set;
import org.easymock.IArgumentMatcher;
import org.jclouds.aws.domain.Region;
import org.jclouds.aws.ec2.compute.domain.EC2Size;
import org.jclouds.aws.ec2.compute.functions.RunningInstanceToNodeMetadata;
import org.jclouds.aws.ec2.compute.options.EC2TemplateOptions;
import org.jclouds.aws.ec2.domain.AvailabilityZone;
import org.jclouds.aws.ec2.domain.Reservation;
import org.jclouds.aws.ec2.domain.RunningInstance;
import org.jclouds.aws.ec2.options.RunInstancesOptions;
import org.jclouds.aws.ec2.services.InstanceClient;
import org.jclouds.compute.domain.Image;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.domain.Template;
import org.jclouds.compute.util.ComputeUtils;
import org.jclouds.domain.Location;
import org.jclouds.domain.LocationScope;
import org.jclouds.domain.internal.LocationImpl;
import org.testng.annotations.Test;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
/**
* @author Adrian Cole
*/
@Test(groups = "unit", testName = "ec2.EC2RunNodesAndAddToSetStrategyTest")
public class EC2RunNodesAndAddToSetStrategyTest {
@Test
public void testZoneAsALocation() {
assertRegionAndZoneForLocation(ZONE_AP_SOUTHEAST_1A, Region.AP_SOUTHEAST_1,
AvailabilityZone.AP_SOUTHEAST_1A);
}
@Test
public void testRegionAsALocation() {
assertRegionAndZoneForLocation(REGION_AP_SOUTHEAST_1, Region.AP_SOUTHEAST_1, null);
}
// // fixtures
public static Iterable<NodeMetadata> containsNodeMetadata(final NodeMetadata in) {
reportMatcher(new IArgumentMatcher() {
@Override
public void appendTo(StringBuffer buffer) {
buffer.append("contains(");
buffer.append(in);
buffer.append(")");
}
@Override
public boolean matches(Object arg) {
return Iterables.contains((Iterable<?>) arg, in);
}
});
return null;
}
private void assertRegionAndZoneForLocation(Location location, String region, String zone) {
String imageId = "ami1";
String instanceCreatedId = "instance1";
// setup mocks
EC2RunNodesAndAddToSetStrategy strategy = setupStrategy();
InputParams input = new InputParams(location);
RunInstancesOptions ec2Options = createMock(RunInstancesOptions.class);
RunningInstance instance = createMock(RunningInstance.class);
Reservation reservation = new Reservation(region, ImmutableSet.<String> of(), ImmutableSet
.<RunningInstance> of(instance), "ownerId", "requesterId", "reservationId");
NodeMetadata nodeMetadata = createMock(NodeMetadata.class);
// setup expectations
expect(
strategy.createKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.execute(region,
input.tag, input.template)).andReturn(ec2Options);
expect(input.template.getLocation()).andReturn(input.location).atLeastOnce();
expect(input.template.getImage()).andReturn(input.image).atLeastOnce();
expect(input.image.getId()).andReturn(imageId).atLeastOnce();
expect(
strategy.instanceClient.runInstancesInRegion(region, zone, imageId, 1, input.count,
ec2Options)).andReturn(reservation);
expect(instance.getId()).andReturn(instanceCreatedId).atLeastOnce();
expect(strategy.instanceStateRunning.apply(instance)).andReturn(true);
expect(strategy.instanceClient.describeInstancesInRegion(region, instanceCreatedId))
.andReturn(ImmutableSet.of(reservation));
expect(input.template.getOptions()).andReturn(input.options).atLeastOnce();
expect(strategy.runningInstanceToNodeMetadata.apply(instance)).andReturn(nodeMetadata);
expect(
strategy.utils.runOptionsOnNodesAndAddToGoodSetOrPutExceptionIntoBadMap(
eq(input.options), containsNodeMetadata(nodeMetadata), eq(input.nodes),
eq(input.badNodes))).andReturn(null);
// replay mocks
replay(ec2Options);
replay(instance);
replay(nodeMetadata);
input.replayMe();
replayStrategy(strategy);
// run
strategy.execute(input.tag, input.count, input.template, input.nodes, input.badNodes);
// verify mocks
verify(ec2Options);
verify(instance);
verify(nodeMetadata);
input.verifyMe();
verifyStrategy(strategy);
}
private static final Location REGION_AP_SOUTHEAST_1 = new LocationImpl(LocationScope.REGION,
Region.AP_SOUTHEAST_1, Region.AP_SOUTHEAST_1, new LocationImpl(LocationScope.PROVIDER,
"ec2", "ec2", null));
private static final Location ZONE_AP_SOUTHEAST_1A = new LocationImpl(LocationScope.ZONE,
AvailabilityZone.AP_SOUTHEAST_1A, AvailabilityZone.AP_SOUTHEAST_1A,
REGION_AP_SOUTHEAST_1);
// /////////////////////////////////////////////////////////////////////
@SuppressWarnings("unchecked")
private static class InputParams {
String tag = "foo";
int count = 1;
Template template = createMock(Template.class);
Set<NodeMetadata> nodes = createMock(Set.class);
Map<NodeMetadata, Exception> badNodes = createMock(Map.class);
EC2Size size = createMock(EC2Size.class);
Image image = createMock(Image.class);
final Location location;
EC2TemplateOptions options = createMock(EC2TemplateOptions.class);
public InputParams(Location location) {
this.location = location;
}
void replayMe() {
replay(template);
replay(size);
replay(image);
replay(nodes);
replay(badNodes);
replay(options);
}
void verifyMe() {
verify(template);
verify(size);
verify(image);
verify(nodes);
verify(badNodes);
verify(options);
}
}
private void verifyStrategy(EC2RunNodesAndAddToSetStrategy strategy) {
verify(strategy.createKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions);
verify(strategy.instanceClient);
verify(strategy.instanceStateRunning);
verify(strategy.runningInstanceToNodeMetadata);
verify(strategy.utils);
}
@SuppressWarnings("unchecked")
private EC2RunNodesAndAddToSetStrategy setupStrategy() {
InstanceClient instanceClient = createMock(InstanceClient.class);
CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions createKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions = createMock(CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.class);
Predicate<RunningInstance> instanceStateRunning = createMock(Predicate.class);
RunningInstanceToNodeMetadata runningInstanceToNodeMetadata = createMock(RunningInstanceToNodeMetadata.class);
ComputeUtils utils = createMock(ComputeUtils.class);
return new EC2RunNodesAndAddToSetStrategy(instanceClient,
createKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions, instanceStateRunning,
runningInstanceToNodeMetadata, utils);
}
private void replayStrategy(EC2RunNodesAndAddToSetStrategy strategy) {
replay(strategy.createKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions);
replay(strategy.instanceClient);
replay(strategy.instanceStateRunning);
replay(strategy.runningInstanceToNodeMetadata);
replay(strategy.utils);
}
}

View File

@ -93,15 +93,16 @@ public class TemplateBuilderImpl implements TemplateBuilder {
@VisibleForTesting @VisibleForTesting
boolean fastest; boolean fastest;
private TemplateOptions options = TemplateOptions.NONE; private TemplateOptions options;
@Inject @Inject
protected TemplateBuilderImpl(Set<? extends Location> locations, Set<? extends Image> images, protected TemplateBuilderImpl(Set<? extends Location> locations, Set<? extends Image> images,
Set<? extends Size> sizes, Location defaultLocation) { Set<? extends Size> sizes, Location defaultLocation, TemplateOptions options) {
this.locations = locations; this.locations = locations;
this.images = images; this.images = images;
this.sizes = sizes; this.sizes = sizes;
this.locationId = defaultLocation.getId(); this.locationId = defaultLocation.getId();
this.options = options;
} }
private final Predicate<ComputeMetadata> locationPredicate = new Predicate<ComputeMetadata>() { private final Predicate<ComputeMetadata> locationPredicate = new Predicate<ComputeMetadata>() {

View File

@ -1,3 +1,21 @@
/**
*
* Copyright (C) 2009 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.compute.options; package org.jclouds.compute.options;
import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
@ -6,7 +24,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Arrays; import java.util.Arrays;
/** /**
* Contains options supported in the {@code ComputeService#runNode} operation. <h2> * Contains options supported in the {@code ComputeService#runNodesWithTag} operation. <h2>
* Usage</h2> The recommended way to instantiate a TemplateOptions object is to statically import * Usage</h2> The recommended way to instantiate a TemplateOptions object is to statically import
* TemplateOptions.* and invoke a static creation method followed by an instance mutator (if * TemplateOptions.* and invoke a static creation method followed by an instance mutator (if
* needed): * needed):
@ -15,7 +33,8 @@ import java.util.Arrays;
* import static org.jclouds.compute.options.TemplateOptions.Builder.*; * import static org.jclouds.compute.options.TemplateOptions.Builder.*;
* <p/> * <p/>
* ComputeService client = // get connection * ComputeService client = // get connection
* NodeSet set = client.runNode(name, template.options(inboundPorts(22, 80, 8080, 443))); * templateBuilder.options(inboundPorts(22, 80, 8080, 443));
* Set<? extends NodeMetadata> set = client.runNodesWithTag(tag, 2, templateBuilder.build());
* <code> * <code>
* *
* @author Adrian Cole * @author Adrian Cole
@ -24,19 +43,19 @@ public class TemplateOptions {
public static final TemplateOptions NONE = new TemplateOptions(); public static final TemplateOptions NONE = new TemplateOptions();
private int[] inboundPorts = new int[] { 22 }; protected int[] inboundPorts = new int[] { 22 };
private byte[] script; protected byte[] script;
private String privateKey; protected String privateKey;
private String publicKey; protected String publicKey;
private int port = -1; protected int port = -1;
private int seconds = -1; protected int seconds = -1;
private boolean includeMetadata; protected boolean includeMetadata;
public int getPort() { public int getPort() {
return port; return port;
@ -66,6 +85,10 @@ public class TemplateOptions {
return includeMetadata; return includeMetadata;
} }
public <T extends TemplateOptions> T as(Class<T> clazz) {
return clazz.cast(this);
}
/** /**
* When the node is started, wait until the following port is active * When the node is started, wait until the following port is active
*/ */
@ -180,4 +203,50 @@ public class TemplateOptions {
+ (script != null) + ", port:seconds=" + port + ":" + seconds + (script != null) + ", port:seconds=" + port + ":" + seconds
+ ", metadata/details: " + includeMetadata + "]"; + ", metadata/details: " + includeMetadata + "]";
} }
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + Arrays.hashCode(inboundPorts);
result = prime * result + (includeMetadata ? 1231 : 1237);
result = prime * result + port;
result = prime * result + ((privateKey == null) ? 0 : privateKey.hashCode());
result = prime * result + ((publicKey == null) ? 0 : publicKey.hashCode());
result = prime * result + Arrays.hashCode(script);
result = prime * result + seconds;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
TemplateOptions other = (TemplateOptions) obj;
if (!Arrays.equals(inboundPorts, other.inboundPorts))
return false;
if (includeMetadata != other.includeMetadata)
return false;
if (port != other.port)
return false;
if (privateKey == null) {
if (other.privateKey != null)
return false;
} else if (!privateKey.equals(other.privateKey))
return false;
if (publicKey == null) {
if (other.publicKey != null)
return false;
} else if (!publicKey.equals(other.publicKey))
return false;
if (!Arrays.equals(script, other.script))
return false;
if (seconds != other.seconds)
return false;
return true;
}
} }

View File

@ -21,6 +21,7 @@ package org.jclouds.compute.util;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Preconditions.checkState;
import static org.jclouds.concurrent.ConcurrentUtils.awaitCompletion; import static org.jclouds.concurrent.ConcurrentUtils.awaitCompletion;
import static org.jclouds.concurrent.ConcurrentUtils.makeListenable;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
@ -85,6 +86,15 @@ public class ComputeUtils {
private final Predicate<InetSocketAddress> socketTester; private final Predicate<InetSocketAddress> socketTester;
private final ExecutorService executor; private final ExecutorService executor;
@Inject
public ComputeUtils(Predicate<InetSocketAddress> socketTester,
@Named("NOT_RUNNING") Predicate<CommandUsingClient> runScriptNotRunning,
@Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor) {
this.socketTester = socketTester;
this.runScriptNotRunning = runScriptNotRunning;
this.executor = executor;
}
public static String createExecutionErrorMessage(Map<?, Exception> executionExceptions) { public static String createExecutionErrorMessage(Map<?, Exception> executionExceptions) {
Formatter fmt = new Formatter().format("Execution failures:%n%n"); Formatter fmt = new Formatter().format("Execution failures:%n%n");
int index = 1; int index = 1;
@ -96,6 +106,38 @@ public class ComputeUtils {
return fmt.format("%s error[s]", executionExceptions.size()).toString(); return fmt.format("%s error[s]", executionExceptions.size()).toString();
} }
public Map<?, ListenableFuture<Void>> runOptionsOnNodesAndAddToGoodSetOrPutExceptionIntoBadMap(
final TemplateOptions options, Iterable<NodeMetadata> runningNodes,
final Set<NodeMetadata> goodNodes, final Map<NodeMetadata, Exception> badNodes) {
Map<NodeMetadata, ListenableFuture<Void>> responses = Maps.newHashMap();
for (final NodeMetadata node : runningNodes) {
responses.put(node, makeListenable(executor
.submit(runOptionsOnNodeAndAddToGoodSetOrPutExceptionIntoBadMap(node, badNodes,
goodNodes, options)), executor));
}
return responses;
}
public Callable<Void> runOptionsOnNodeAndAddToGoodSetOrPutExceptionIntoBadMap(
final NodeMetadata node, final Map<NodeMetadata, Exception> badNodes,
final Set<NodeMetadata> goodNodes, final TemplateOptions options) {
return new Callable<Void>() {
@Override
public Void call() throws Exception {
try {
runOptionsOnNode(node, options);
logger.debug("<< options applied node(%s)", node.getId());
goodNodes.add(node);
} catch (Exception e) {
logger.error(e, "<< problem applying options to node(%s): ", node.getId(),
Throwables.getRootCause(e).getMessage());
badNodes.put(node, e);
}
return null;
}
};
}
public static String createNodeErrorMessage( public static String createNodeErrorMessage(
Map<? extends NodeMetadata, ? extends Throwable> failedNodes) { Map<? extends NodeMetadata, ? extends Throwable> failedNodes) {
Formatter fmt = new Formatter().format("Node failures:%n%n"); Formatter fmt = new Formatter().format("Node failures:%n%n");
@ -108,15 +150,6 @@ public class ComputeUtils {
return fmt.format("%s error[s]", failedNodes.size()).toString(); return fmt.format("%s error[s]", failedNodes.size()).toString();
} }
@Inject
public ComputeUtils(Predicate<InetSocketAddress> socketTester,
@Named("NOT_RUNNING") Predicate<CommandUsingClient> runScriptNotRunning,
@Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor) {
this.socketTester = socketTester;
this.runScriptNotRunning = runScriptNotRunning;
this.executor = executor;
}
public static Iterable<? extends ComputeMetadata> filterByName( public static Iterable<? extends ComputeMetadata> filterByName(
Iterable<? extends ComputeMetadata> nodes, final String name) { Iterable<? extends ComputeMetadata> nodes, final String name) {
return Iterables.filter(nodes, new Predicate<ComputeMetadata>() { return Iterables.filter(nodes, new Predicate<ComputeMetadata>() {

View File

@ -252,7 +252,7 @@ public abstract class BaseComputeServiceLiveTest {
} }
} }
private Map<NodeMetadata, ExecResponse> runScriptWithCreds(final String tag, OsFamily osFamily, protected Map<NodeMetadata, ExecResponse> runScriptWithCreds(final String tag, OsFamily osFamily,
Credentials creds) throws RunScriptOnNodesException { Credentials creds) throws RunScriptOnNodesException {
try { try {
return client.runScriptOnNodesMatching(new Predicate<NodeMetadata>() { return client.runScriptOnNodesMatching(new Predicate<NodeMetadata>() {

View File

@ -24,6 +24,7 @@ import org.jclouds.compute.domain.Architecture;
import org.jclouds.compute.domain.Image; import org.jclouds.compute.domain.Image;
import org.jclouds.compute.domain.OsFamily; import org.jclouds.compute.domain.OsFamily;
import org.jclouds.compute.domain.Size; import org.jclouds.compute.domain.Size;
import org.jclouds.compute.options.TemplateOptions;
import org.jclouds.domain.Location; import org.jclouds.domain.Location;
import org.jclouds.domain.LocationScope; import org.jclouds.domain.LocationScope;
import org.jclouds.domain.internal.LocationImpl; import org.jclouds.domain.internal.LocationImpl;
@ -41,8 +42,8 @@ public class TemplateBuilderImplTest {
@Test @Test
public void testImageIdNullsEverythingElse() { public void testImageIdNullsEverythingElse() {
TemplateBuilderImpl template = new TemplateBuilderImpl(ImmutableSet.<Location> of(), TemplateBuilderImpl template = new TemplateBuilderImpl(ImmutableSet.<Location> of(),
ImmutableSet.<Image> of(), ImmutableSet.<Size> of(), ImmutableSet.<Image> of(), ImmutableSet.<Size> of(), new LocationImpl(
new LocationImpl(LocationScope.REGION, " id", "description", null)); LocationScope.REGION, " id", "description", null), new TemplateOptions());
template.architecture(Architecture.X86_32); template.architecture(Architecture.X86_32);
template.imageDescriptionMatches("imageDescriptionMatches"); template.imageDescriptionMatches("imageDescriptionMatches");
template.imageNameMatches("imageNameMatches"); template.imageNameMatches("imageNameMatches");