From d8e21ad8d0e4ef1a066be9cbe0238d08ea3546b8 Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Mon, 17 May 2010 17:22:22 -0700 Subject: [PATCH] Issue 252: created templateBuilder.any() which can be used to get the known bootable params of a template, as well helper functions --- .../aws/ec2/compute/EC2ComputeService.java | 19 +- .../EC2ComputeServiceContextModule.java | 4 +- .../compute/EC2ComputeServiceLiveTest.java | 32 ++- .../ec2/compute/EC2ComputeServiceTest.java | 20 +- .../org/jclouds/compute/ComputeService.java | 21 +- .../compute/domain/TemplateBuilder.java | 8 +- .../compute/internal/BaseComputeService.java | 93 +++++++-- .../compute/internal/TemplateBuilderImpl.java | 43 ++++- .../compute/options/TemplateOptions.java | 2 +- .../compute/predicates/NodePredicates.java | 6 +- .../compute/BaseComputeServiceLiveTest.java | 19 +- .../internal/TemplateBuilderImplTest.java | 182 +++++++++++++++++- .../GoGridComputeServiceContextModule.java | 4 +- ...oudServersComputeServiceContextModule.java | 4 +- ...imuHostingComputeServiceContextModule.java | 4 +- .../VCloudComputeServiceContextModule.java | 4 +- ...tComVCloudComputeServiceContextModule.java | 3 +- ...markVCloudComputeServiceContextModule.java | 3 +- 18 files changed, 385 insertions(+), 86 deletions(-) diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/compute/EC2ComputeService.java b/aws/core/src/main/java/org/jclouds/aws/ec2/compute/EC2ComputeService.java index 4375c03038..cf1ba9a953 100644 --- a/aws/core/src/main/java/org/jclouds/aws/ec2/compute/EC2ComputeService.java +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/compute/EC2ComputeService.java @@ -35,6 +35,7 @@ import org.jclouds.aws.ec2.EC2Client; import org.jclouds.aws.ec2.compute.config.EC2ComputeServiceContextModule.GetRegionFromLocation; import org.jclouds.aws.ec2.compute.domain.RegionAndName; import org.jclouds.aws.ec2.compute.domain.RegionNameAndIngressRules; +import org.jclouds.aws.ec2.compute.options.EC2TemplateOptions; import org.jclouds.aws.ec2.domain.KeyPair; import org.jclouds.compute.ComputeServiceContext; import org.jclouds.compute.domain.Image; @@ -42,6 +43,7 @@ import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.compute.domain.Size; import org.jclouds.compute.domain.TemplateBuilder; import org.jclouds.compute.internal.BaseComputeService; +import org.jclouds.compute.options.TemplateOptions; import org.jclouds.compute.strategy.DestroyNodeStrategy; import org.jclouds.compute.strategy.GetNodeMetadataStrategy; import org.jclouds.compute.strategy.ListNodesStrategy; @@ -70,13 +72,14 @@ public class EC2ComputeService extends BaseComputeService { GetNodeMetadataStrategy getNodeMetadataStrategy, RunNodesAndAddToSetStrategy runNodesAndAddToSetStrategy, RebootNodeStrategy rebootNodeStrategy, DestroyNodeStrategy destroyNodeStrategy, - Provider templateBuilderProvider, ComputeUtils utils, + Provider templateBuilderProvider, + Provider templateOptionsProvider, ComputeUtils utils, @Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor, EC2Client ec2Client, GetRegionFromLocation getRegionFromLocation, Map credentialsMap, Map securityGroupMap) { super(context, images, sizes, locations, listNodesStrategy, getNodeMetadataStrategy, runNodesAndAddToSetStrategy, rebootNodeStrategy, destroyNodeStrategy, - templateBuilderProvider, utils, executor); + templateBuilderProvider, templateOptionsProvider, utils, executor); this.ec2Client = ec2Client; this.getRegionFromLocation = getRegionFromLocation; this.credentialsMap = credentialsMap; @@ -107,6 +110,10 @@ public class EC2ComputeService extends BaseComputeService { } } + /** + * like {@link BaseComputeService#destroyNodesMatching} except that this will clean implicit + * keypairs and security groups. + */ @Override public Set destroyNodesMatching(Predicate filter) { Set deadOnes = super.destroyNodesMatching(filter); @@ -122,4 +129,12 @@ public class EC2ComputeService extends BaseComputeService { } return deadOnes; } + + /** + * returns template options, except of type {@link EC2TemplateOptions}. + */ + @Override + public EC2TemplateOptions templateOptions() { + return EC2TemplateOptions.class.cast(super.templateOptions()); + } } \ No newline at end of file diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/compute/config/EC2ComputeServiceContextModule.java b/aws/core/src/main/java/org/jclouds/aws/ec2/compute/config/EC2ComputeServiceContextModule.java index 0147f5a2ff..52f4cebb9f 100755 --- a/aws/core/src/main/java/org/jclouds/aws/ec2/compute/config/EC2ComputeServiceContextModule.java +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/compute/config/EC2ComputeServiceContextModule.java @@ -64,7 +64,6 @@ import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.compute.domain.Size; import org.jclouds.compute.domain.TemplateBuilder; import org.jclouds.compute.internal.ComputeServiceContextImpl; -import org.jclouds.compute.internal.TemplateBuilderImpl; import org.jclouds.compute.options.TemplateOptions; import org.jclouds.compute.predicates.NodePredicates; import org.jclouds.compute.predicates.ScriptStatusReturnsZero; @@ -139,7 +138,8 @@ public class EC2ComputeServiceContextModule extends EC2ContextModule { } @Provides - TemplateBuilder provideTemplate(TemplateBuilderImpl template) { + @Named("DEFAULT") + protected TemplateBuilder provideTemplate(TemplateBuilder template) { return template.architecture(Architecture.X86_32).osFamily(UBUNTU); } diff --git a/aws/core/src/test/java/org/jclouds/aws/ec2/compute/EC2ComputeServiceLiveTest.java b/aws/core/src/test/java/org/jclouds/aws/ec2/compute/EC2ComputeServiceLiveTest.java index 5fd4a86cfe..a6250aff69 100644 --- a/aws/core/src/test/java/org/jclouds/aws/ec2/compute/EC2ComputeServiceLiveTest.java +++ b/aws/core/src/test/java/org/jclouds/aws/ec2/compute/EC2ComputeServiceLiveTest.java @@ -36,7 +36,7 @@ import org.jclouds.compute.domain.Architecture; import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.compute.domain.OsFamily; import org.jclouds.compute.domain.Template; -import org.jclouds.compute.domain.TemplateBuilder; +import org.jclouds.compute.options.TemplateOptions; import org.jclouds.compute.predicates.NodePredicates; import org.jclouds.domain.Credentials; import org.jclouds.ssh.jsch.config.JschSshClientModule; @@ -83,11 +83,6 @@ public class EC2ComputeServiceLiveTest extends BaseComputeServiceLiveTest { return new JschSshClientModule(); } - @Override - protected Template buildTemplate(TemplateBuilder templateBuilder) { - return templateBuilder.imageId("ami-714ba518").build(); - } - @Test public void testExtendedOptionsAndLogin() throws Exception { SecurityGroupClient securityGroupClient = EC2Client.class.cast( @@ -101,10 +96,10 @@ public class EC2ComputeServiceLiveTest extends BaseComputeServiceLiveTest { String tag = this.tag + "optionsandlogin"; - Template template = buildTemplate(client.templateBuilder()); + TemplateOptions options = client.templateOptions(); - template.getOptions().as(EC2TemplateOptions.class).securityGroups(tag); - template.getOptions().as(EC2TemplateOptions.class).keyPair(tag); + options.as(EC2TemplateOptions.class).securityGroups(tag); + options.as(EC2TemplateOptions.class).keyPair(tag); String startedId = null; try { @@ -116,9 +111,10 @@ public class EC2ComputeServiceLiveTest extends BaseComputeServiceLiveTest { // create a keypair to pass in as well KeyPair result = keyPairClient.createKeyPairInRegion(null, tag); - Set nodes = client.runNodesWithTag(tag, 1, template); - Credentials good = nodes.iterator().next().getCredentials(); - assert good.account != null; + Set nodes = client.runNodesWithTag(tag, 1, options); + NodeMetadata first = Iterables.get(nodes, 0); + assert first.getCredentials() != null : first; + assert first.getCredentials().account != null : first; startedId = Iterables.getOnlyElement(nodes).getId(); @@ -135,8 +131,8 @@ public class EC2ComputeServiceLiveTest extends BaseComputeServiceLiveTest { 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())); + runScriptWithCreds(tag, first.getImage().getOsFamily(), new Credentials(first + .getCredentials().account, result.getKeyMaterial())); } finally { client.destroyNodesMatching(NodePredicates.withTag(tag)); @@ -162,17 +158,17 @@ public class EC2ComputeServiceLiveTest extends BaseComputeServiceLiveTest { String tag = this.tag + "optionsnokey"; - Template template = buildTemplate(client.templateBuilder()); + TemplateOptions options = client.templateOptions(); - template.getOptions().as(EC2TemplateOptions.class).securityGroups(tag); - template.getOptions().as(EC2TemplateOptions.class).noKeyPair(); + options.as(EC2TemplateOptions.class).securityGroups(tag); + options.as(EC2TemplateOptions.class).noKeyPair(); String startedId = null; try { // create the security group securityGroupClient.createSecurityGroupInRegion(null, tag, tag); - Set nodes = client.runNodesWithTag(tag, 1, template); + Set nodes = client.runNodesWithTag(tag, 1, options); Credentials creds = nodes.iterator().next().getCredentials(); assert creds == null; diff --git a/aws/core/src/test/java/org/jclouds/aws/ec2/compute/EC2ComputeServiceTest.java b/aws/core/src/test/java/org/jclouds/aws/ec2/compute/EC2ComputeServiceTest.java index 4472fa1d48..db009e038e 100644 --- a/aws/core/src/test/java/org/jclouds/aws/ec2/compute/EC2ComputeServiceTest.java +++ b/aws/core/src/test/java/org/jclouds/aws/ec2/compute/EC2ComputeServiceTest.java @@ -25,9 +25,13 @@ package org.jclouds.aws.ec2.compute; import static java.lang.String.format; +import static org.easymock.EasyMock.expect; +import static org.easymock.classextension.EasyMock.createMock; +import static org.easymock.classextension.EasyMock.replay; + +import javax.inject.Provider; 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.ComputeMetadata; import org.jclouds.compute.domain.Image; @@ -35,6 +39,7 @@ import org.jclouds.compute.domain.Template; import org.jclouds.compute.domain.TemplateBuilder; import org.jclouds.compute.domain.internal.ImageImpl; import org.jclouds.compute.internal.TemplateBuilderImpl; +import org.jclouds.compute.options.TemplateOptions; import org.jclouds.domain.Credentials; import org.jclouds.domain.Location; import org.jclouds.domain.LocationScope; @@ -110,16 +115,25 @@ public class EC2ComputeServiceTest { "m2.xlarge", String.valueOf(template.getSize())); } + @SuppressWarnings("unchecked") private TemplateBuilder newTemplateBuilder() { Location location = new LocationImpl(LocationScope.REGION, "us-east-1", "us east", null); + + Provider optionsProvider = createMock(Provider.class); + Provider templateBuilderProvider = createMock(Provider.class); + TemplateOptions defaultOptions = createMock(TemplateOptions.class); + + expect(optionsProvider.get()).andReturn(defaultOptions); + Image image = new ImageImpl("ami-image", "image", location, null, Maps . newHashMap(), "description", "1.0", null, "ubuntu", Architecture.X86_64, new Credentials("root", null)); - + replay(optionsProvider); + replay(templateBuilderProvider); return new TemplateBuilderImpl(ImmutableSet.of(location), ImmutableSet.of(image), ImmutableSet.of(EC2Size.C1_MEDIUM, EC2Size.C1_XLARGE, EC2Size.M1_LARGE, EC2Size.M1_SMALL, EC2Size.M1_XLARGE, EC2Size.M2_XLARGE, EC2Size.M2_2XLARGE, - EC2Size.M2_4XLARGE), location, new EC2TemplateOptions()) { + EC2Size.M2_4XLARGE), location, optionsProvider, templateBuilderProvider) { }; } diff --git a/compute/src/main/java/org/jclouds/compute/ComputeService.java b/compute/src/main/java/org/jclouds/compute/ComputeService.java index 038ee250c6..39616f3f57 100755 --- a/compute/src/main/java/org/jclouds/compute/ComputeService.java +++ b/compute/src/main/java/org/jclouds/compute/ComputeService.java @@ -29,6 +29,7 @@ import org.jclouds.compute.domain.Template; import org.jclouds.compute.domain.TemplateBuilder; import org.jclouds.compute.internal.BaseComputeService; import org.jclouds.compute.options.RunScriptOptions; +import org.jclouds.compute.options.TemplateOptions; import org.jclouds.domain.Location; import org.jclouds.ssh.ExecResponse; @@ -54,6 +55,11 @@ public interface ComputeService { */ TemplateBuilder templateBuilder(); + /** + * Makes a new set of options for running nodes + */ + TemplateOptions templateOptions(); + /** * The list sizes command shows you the options including virtual cpu count, memory, and disks. * cpu count is not a portable quantity across clouds, as they are measured differently. However, @@ -125,6 +131,19 @@ public interface ComputeService { Set runNodesWithTag(String tag, int count, Template template) throws RunNodesException; + /** + * Like {@link ComputeService#runNodesWithTag(String,int,Template)}, except that the template is + * default, equivalent to {@code templateBuilder().any().options(templateOptions)}. + */ + Set runNodesWithTag(String tag, int count, + TemplateOptions templateOptions) throws RunNodesException; + + /** + * Like {@link ComputeService#runNodesWithTag(String,int,TemplateOptions)}, except that the + * options are default, as specified in {@link ComputeService#templateOptions}. + */ + Set runNodesWithTag(String tag, int count) throws RunNodesException; + /** * destroy the node. If it is the only node in a tag set, the dependent resources will also be * destroyed. @@ -151,7 +170,7 @@ public interface ComputeService { */ void rebootNodesMatching(Predicate filter); - /** + /** * Find a node by its id */ NodeMetadata getNodeMetadata(Location location, String id); diff --git a/compute/src/main/java/org/jclouds/compute/domain/TemplateBuilder.java b/compute/src/main/java/org/jclouds/compute/domain/TemplateBuilder.java index 544e9d1f1d..2de5d41528 100644 --- a/compute/src/main/java/org/jclouds/compute/domain/TemplateBuilder.java +++ b/compute/src/main/java/org/jclouds/compute/domain/TemplateBuilder.java @@ -30,6 +30,12 @@ import com.google.inject.ImplementedBy; */ @ImplementedBy(TemplateBuilderImpl.class) public interface TemplateBuilder { + + /** + * prime this builder with parameters known to work on the current compute provider. + */ + TemplateBuilder any(); + /** * Configure this template to require the minimum size of the parameter. */ @@ -97,7 +103,7 @@ public interface TemplateBuilder { * Configure this template to have an image name that matches the regular expression */ TemplateBuilder imageNameMatches(String imageNameRegex); - + /** * Configure this template to have an image version that matches the regular expression */ diff --git a/compute/src/main/java/org/jclouds/compute/internal/BaseComputeService.java b/compute/src/main/java/org/jclouds/compute/internal/BaseComputeService.java index 2e36de874f..91f400ae67 100755 --- a/compute/src/main/java/org/jclouds/compute/internal/BaseComputeService.java +++ b/compute/src/main/java/org/jclouds/compute/internal/BaseComputeService.java @@ -52,6 +52,7 @@ import org.jclouds.compute.domain.Size; import org.jclouds.compute.domain.Template; import org.jclouds.compute.domain.TemplateBuilder; import org.jclouds.compute.options.RunScriptOptions; +import org.jclouds.compute.options.TemplateOptions; import org.jclouds.compute.predicates.NodePredicates; import org.jclouds.compute.reference.ComputeServiceConstants; import org.jclouds.compute.strategy.DestroyNodeStrategy; @@ -95,6 +96,7 @@ public class BaseComputeService implements ComputeService { protected final RebootNodeStrategy rebootNodeStrategy; protected final DestroyNodeStrategy destroyNodeStrategy; protected final Provider templateBuilderProvider; + protected final Provider templateOptionsProvider; protected final ComputeUtils utils; protected final ExecutorService executor; @@ -105,7 +107,8 @@ public class BaseComputeService implements ComputeService { GetNodeMetadataStrategy getNodeMetadataStrategy, RunNodesAndAddToSetStrategy runNodesAndAddToSetStrategy, RebootNodeStrategy rebootNodeStrategy, DestroyNodeStrategy destroyNodeStrategy, - Provider templateBuilderProvider, ComputeUtils utils, + Provider templateBuilderProvider, + Provider templateOptionsProvider, ComputeUtils utils, @Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor) { this.context = checkNotNull(context, "context"); this.images = checkNotNull(images, "images"); @@ -120,18 +123,26 @@ public class BaseComputeService implements ComputeService { this.destroyNodeStrategy = checkNotNull(destroyNodeStrategy, "destroyNodeStrategy"); this.templateBuilderProvider = checkNotNull(templateBuilderProvider, "templateBuilderProvider"); + this.templateOptionsProvider = checkNotNull(templateOptionsProvider, + "templateOptionsProvider"); this.utils = checkNotNull(utils, "utils"); this.executor = checkNotNull(executor, "executor"); } + /** + * {@inheritDoc} + */ @Override public ComputeServiceContext getContext() { return context; } + /** + * {@inheritDoc} + */ @Override - public Set runNodesWithTag(final String tag, int count, - final Template template) throws RunNodesException { + public Set runNodesWithTag(String tag, int count, Template template) + throws RunNodesException { checkArgument(tag.indexOf('-') == -1, "tag cannot contain hyphens"); checkNotNull(template.getLocation(), "location"); logger.debug(">> running %d node%s tag(%s) location(%s) image(%s) size(%s) options(%s)", @@ -149,6 +160,27 @@ public class BaseComputeService implements ComputeService { return nodes; } + /** + * {@inheritDoc} + */ + @Override + public Set runNodesWithTag(String tag, int count, + TemplateOptions templateOptions) throws RunNodesException { + return runNodesWithTag(tag, count, templateBuilder().any().options(templateOptions).build()); + } + + /** + * {@inheritDoc} + */ + @Override + public Set runNodesWithTag(String tag, int count) + throws RunNodesException { + return runNodesWithTag(tag, count, templateOptions()); + } + + /** + * {@inheritDoc} + */ @Override public void destroyNode(Location location, String id) { checkNotNull(location, "location"); @@ -158,6 +190,9 @@ public class BaseComputeService implements ComputeService { logger.debug("<< destroyed node(%s/%s) success(%s)", location.getId(), id, successful); } + /** + * {@inheritDoc} + */ @Override public Set destroyNodesMatching(Predicate filter) { logger.debug(">> destroying nodes matching(%s)", filter); @@ -184,6 +219,9 @@ public class BaseComputeService implements ComputeService { .not(NodePredicates.TERMINATED))); } + /** + * {@inheritDoc} + */ @Override public Set listNodes() { logger.debug(">> listing nodes"); @@ -192,6 +230,9 @@ public class BaseComputeService implements ComputeService { return set; } + /** + * {@inheritDoc} + */ @Override public Set listNodesDetailsMatching(Predicate filter) { checkNotNull(filter, "filter"); @@ -202,26 +243,41 @@ public class BaseComputeService implements ComputeService { return set; } + /** + * {@inheritDoc} + */ @Override public Set listSizes() { return sizes.get(); } + /** + * {@inheritDoc} + */ @Override public Set listImages() { return images.get(); } + /** + * {@inheritDoc} + */ @Override public Set listAssignableLocations() { return locations.get(); } + /** + * {@inheritDoc} + */ @Override public TemplateBuilder templateBuilder() { return templateBuilderProvider.get(); } + /** + * {@inheritDoc} + */ @Override public NodeMetadata getNodeMetadata(Location location, String id) { checkNotNull(location, "location"); @@ -229,6 +285,9 @@ public class BaseComputeService implements ComputeService { return getNodeMetadataStrategy.execute(location, id); } + /** + * {@inheritDoc} + */ @Override public void rebootNode(Location location, String id) { checkNotNull(location, "location"); @@ -238,6 +297,9 @@ public class BaseComputeService implements ComputeService { logger.debug("<< rebooted node(%s/%s) success(%s)", location.getId(), id, successful); } + /** + * {@inheritDoc} + */ @Override public void rebootNodesMatching(Predicate filter) { logger.debug(">> rebooting nodes matching(%s)", filter); @@ -257,10 +319,7 @@ public class BaseComputeService implements ComputeService { } /** - * @throws RunScriptOnNodesException - * @see #runScriptOnNodesMatching(Predicate, byte[], - * org.jclouds.compute.options.RunScriptOptions) - * @see org.jclouds.compute.predicates.NodePredicates#runningWithTag(String) + * {@inheritDoc} */ @Override public Map runScriptOnNodesMatching( @@ -269,20 +328,7 @@ public class BaseComputeService implements ComputeService { } /** - * Run the script on all nodes with the specific tag. - * - * @param filter - * Predicate-based filter to define on which nodes the script is to be executed - * @param runScript - * script to run in byte format. If the script is a string, use - * {@link String#getBytes()} to retrieve the bytes - * @param options - * nullable options to how to run the script, whether to override credentials - * @return map with node identifiers and corresponding responses - * @throws RunScriptOnNodesException - * if anything goes wrong during script execution - * - * @see org.jclouds.compute.predicates.NodePredicates#runningWithTag(String) + * {@inheritDoc} */ @Override public Map runScriptOnNodesMatching(Predicate filter, @@ -373,4 +419,9 @@ public class BaseComputeService implements ComputeService { private Iterable detailsOnAllNodes() { return listNodesStrategy.listDetailsOnNodesMatching(NodePredicates.all()); } + + @Override + public TemplateOptions templateOptions() { + return templateOptionsProvider.get(); + } } \ No newline at end of file diff --git a/compute/src/main/java/org/jclouds/compute/internal/TemplateBuilderImpl.java b/compute/src/main/java/org/jclouds/compute/internal/TemplateBuilderImpl.java index c6af352e8e..f7cae4632a 100644 --- a/compute/src/main/java/org/jclouds/compute/internal/TemplateBuilderImpl.java +++ b/compute/src/main/java/org/jclouds/compute/internal/TemplateBuilderImpl.java @@ -26,6 +26,7 @@ import java.util.Set; import javax.annotation.Resource; import javax.inject.Inject; import javax.inject.Named; +import javax.inject.Provider; import org.jclouds.compute.domain.Architecture; import org.jclouds.compute.domain.ComputeMetadata; @@ -63,6 +64,10 @@ public class TemplateBuilderImpl implements TemplateBuilder { private final Set images; private final Set sizes; private final Set locations; + private final Provider optionsProvider; + private final Provider defaultTemplateProvider; + private final Location defaultLocation; + @VisibleForTesting OsFamily os; @VisibleForTesting @@ -77,32 +82,32 @@ public class TemplateBuilderImpl implements TemplateBuilder { String osDescription; @VisibleForTesting String imageVersion; - @VisibleForTesting String imageName; @VisibleForTesting String imageDescription; - @VisibleForTesting double minCores; @VisibleForTesting int minRam; - @VisibleForTesting boolean biggest; @VisibleForTesting boolean fastest; - - private TemplateOptions options; + @VisibleForTesting + TemplateOptions options; @Inject protected TemplateBuilderImpl(Set locations, Set images, - Set sizes, Location defaultLocation, TemplateOptions options) { + Set sizes, Location defaultLocation, + Provider optionsProvider, + @Named("DEFAULT") Provider defaultTemplateProvider) { this.locations = locations; this.images = images; this.sizes = sizes; - this.locationId = defaultLocation.getId(); - this.options = options; + this.defaultLocation = defaultLocation; + this.optionsProvider = optionsProvider; + this.defaultTemplateProvider = defaultTemplateProvider; } private final Predicate locationPredicate = new Predicate() { @@ -389,6 +394,12 @@ public class TemplateBuilderImpl implements TemplateBuilder { */ @Override public Template build() { + if (nothingChanged()) + return defaultTemplateProvider.get().build(); + if (locationId == null) + locationId = defaultLocation.getId(); + if (options == null) + options = optionsProvider.get(); logger.debug(">> searching params(%s)", this); Image image; try { @@ -511,6 +522,22 @@ public class TemplateBuilderImpl implements TemplateBuilder { return this; } + @VisibleForTesting + boolean nothingChanged() { + return os == null && arch == null && locationId == null && imageId == null && sizeId == null + && osDescription == null && imageVersion == null && imageName == null + && imageDescription == null && minCores == 0 && minRam == 0 && !biggest && !fastest + && options == null; + } + + /** + * {@inheritDoc} + */ + @Override + public TemplateBuilder any() { + return defaultTemplateProvider.get(); + } + @Override public String toString() { return "[arch=" + arch + ", biggest=" + biggest + ", fastest=" + fastest + ", imageName=" diff --git a/compute/src/main/java/org/jclouds/compute/options/TemplateOptions.java b/compute/src/main/java/org/jclouds/compute/options/TemplateOptions.java index 71b37cdb3c..b6486ac1f7 100644 --- a/compute/src/main/java/org/jclouds/compute/options/TemplateOptions.java +++ b/compute/src/main/java/org/jclouds/compute/options/TemplateOptions.java @@ -41,7 +41,7 @@ import java.util.Arrays; */ public class TemplateOptions { - public static final TemplateOptions NONE = new TemplateOptions(); + public static final TemplateOptions NONE = new ImmutableTemplateOptions(new TemplateOptions()); public static class ImmutableTemplateOptions extends TemplateOptions { private final TemplateOptions delegate; diff --git a/compute/src/main/java/org/jclouds/compute/predicates/NodePredicates.java b/compute/src/main/java/org/jclouds/compute/predicates/NodePredicates.java index 0fb5820999..62d40eff76 100644 --- a/compute/src/main/java/org/jclouds/compute/predicates/NodePredicates.java +++ b/compute/src/main/java/org/jclouds/compute/predicates/NodePredicates.java @@ -106,7 +106,7 @@ public class NodePredicates { checkNotEmpty(tag, "Tag must be defined"); return new Predicate() { @Override - public boolean apply(@Nullable NodeMetadata nodeMetadata) { + public boolean apply(NodeMetadata nodeMetadata) { return tag.equals(nodeMetadata.getTag()) && nodeMetadata.getState() == NodeState.RUNNING; } @@ -123,7 +123,7 @@ public class NodePredicates { */ public static final Predicate ACTIVE = new Predicate() { @Override - public boolean apply(@Nullable NodeMetadata nodeMetadata) { + public boolean apply(NodeMetadata nodeMetadata) { return nodeMetadata.getState() == NodeState.RUNNING; } @@ -138,7 +138,7 @@ public class NodePredicates { */ public static final Predicate TERMINATED = new Predicate() { @Override - public boolean apply(@Nullable NodeMetadata nodeMetadata) { + public boolean apply(NodeMetadata nodeMetadata) { return nodeMetadata.getState() == NodeState.TERMINATED; } diff --git a/compute/src/test/java/org/jclouds/compute/BaseComputeServiceLiveTest.java b/compute/src/test/java/org/jclouds/compute/BaseComputeServiceLiveTest.java index dca25cb618..28af8ca104 100755 --- a/compute/src/test/java/org/jclouds/compute/BaseComputeServiceLiveTest.java +++ b/compute/src/test/java/org/jclouds/compute/BaseComputeServiceLiveTest.java @@ -226,23 +226,22 @@ public abstract class BaseComputeServiceLiveTest { @Test public void testScriptExecutionAfterBootWithBasicTemplate() throws Exception { String tag = this.tag + "run"; - Template simpleTemplate = buildTemplate(client.templateBuilder()); - simpleTemplate.getOptions().blockOnPort(22, 120); + TemplateOptions options = client.templateOptions().blockOnPort(22, 120); try { - Set nodes = client.runNodesWithTag(tag, 1, simpleTemplate); + Set nodes = client.runNodesWithTag(tag, 1, options); Credentials good = nodes.iterator().next().getCredentials(); assert good.account != null; + Image image = Iterables.get(nodes, 0).getImage(); try { - Map responses = runScriptWithCreds(tag, - simpleTemplate.getImage().getOsFamily(), - new Credentials(good.account, "romeo")); + Map responses = runScriptWithCreds(tag, image + .getOsFamily(), new Credentials(good.account, "romeo")); assert false : "shouldn't pass with a bad password\n" + responses; } catch (RunScriptOnNodesException e) { assert Throwables.getRootCause(e).getMessage().contains("Auth fail") : e; } - runScriptWithCreds(tag, simpleTemplate.getImage().getOsFamily(), good); + runScriptWithCreds(tag, image.getOsFamily(), good); checkNodes(nodes, tag); @@ -319,9 +318,9 @@ public abstract class BaseComputeServiceLiveTest { @Test(enabled = true, dependsOnMethods = "testCreateAnotherNodeWithANewContextToEnsureSharedMemIsntRequired") public void testGet() throws Exception { - Set nodes = client.listNodesDetailsMatching(NodePredicates.all()); - Set metadataSet = Sets.newHashSet(Iterables.filter(nodes, Predicates - .and(NodePredicates.withTag(tag), Predicates.not(NodePredicates.TERMINATED)))); + Set metadataSet = Sets.newHashSet(Iterables.filter(client + .listNodesDetailsMatching(NodePredicates.all()), Predicates.and(NodePredicates + .withTag(tag), Predicates.not(NodePredicates.TERMINATED)))); for (NodeMetadata node : nodes) { metadataSet.remove(node); NodeMetadata metadata = client.getNodeMetadata(node.getLocation(), node.getId()); diff --git a/compute/src/test/java/org/jclouds/compute/internal/TemplateBuilderImplTest.java b/compute/src/test/java/org/jclouds/compute/internal/TemplateBuilderImplTest.java index efd99484e4..b6ae70b650 100644 --- a/compute/src/test/java/org/jclouds/compute/internal/TemplateBuilderImplTest.java +++ b/compute/src/test/java/org/jclouds/compute/internal/TemplateBuilderImplTest.java @@ -18,16 +18,23 @@ */ package org.jclouds.compute.internal; +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.util.NoSuchElementException; + +import javax.inject.Provider; + import org.jclouds.compute.domain.Architecture; import org.jclouds.compute.domain.Image; import org.jclouds.compute.domain.OsFamily; import org.jclouds.compute.domain.Size; +import org.jclouds.compute.domain.TemplateBuilder; import org.jclouds.compute.options.TemplateOptions; 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.collect.ImmutableSet; @@ -39,11 +46,175 @@ import com.google.common.collect.ImmutableSet; @Test(groups = "unit") public class TemplateBuilderImplTest { + @SuppressWarnings("unchecked") + @Test + public void testNothingUsesDefaultTemplateBuilder() { + + Location defaultLocation = createMock(Location.class); + Provider optionsProvider = createMock(Provider.class); + Provider templateBuilderProvider = createMock(Provider.class); + TemplateBuilder defaultTemplate = createMock(TemplateBuilder.class); + + expect(templateBuilderProvider.get()).andReturn(defaultTemplate); + expect(defaultTemplate.build()).andReturn(null); + + replay(defaultTemplate); + replay(defaultLocation); + replay(optionsProvider); + replay(templateBuilderProvider); + + TemplateBuilderImpl template = new TemplateBuilderImpl(ImmutableSet. of(), + ImmutableSet. of(), ImmutableSet. of(), defaultLocation, + optionsProvider, templateBuilderProvider); + + template.build(); + + verify(defaultTemplate); + verify(defaultLocation); + verify(optionsProvider); + verify(templateBuilderProvider); + } + + @SuppressWarnings("unchecked") + @Test + public void testSuppliedLocationWithNoOptions() { + + Location defaultLocation = createMock(Location.class); + Provider optionsProvider = createMock(Provider.class); + Provider templateBuilderProvider = createMock(Provider.class); + TemplateOptions defaultOptions = createMock(TemplateOptions.class); + + expect(optionsProvider.get()).andReturn(defaultOptions); + + replay(defaultOptions); + replay(defaultLocation); + replay(optionsProvider); + replay(templateBuilderProvider); + + TemplateBuilderImpl template = new TemplateBuilderImpl(ImmutableSet. of(), + ImmutableSet. of(), ImmutableSet. of(), defaultLocation, + optionsProvider, templateBuilderProvider); + + try { + template.imageId("foo").locationId("location").build(); + assert false; + } catch (NoSuchElementException e) { + + } + + verify(defaultOptions); + verify(defaultLocation); + verify(optionsProvider); + verify(templateBuilderProvider); + } + + @SuppressWarnings("unchecked") + @Test + public void testSuppliedLocationAndOptions() { + + Location defaultLocation = createMock(Location.class); + Provider optionsProvider = createMock(Provider.class); + Provider templateBuilderProvider = createMock(Provider.class); + + replay(defaultLocation); + replay(optionsProvider); + replay(templateBuilderProvider); + + TemplateBuilderImpl template = new TemplateBuilderImpl(ImmutableSet. of(), + ImmutableSet. of(), ImmutableSet. of(), defaultLocation, + optionsProvider, templateBuilderProvider); + + try { + template.imageId("foo").options(TemplateOptions.NONE).locationId("location").build(); + assert false; + } catch (NoSuchElementException e) { + + } + + verify(defaultLocation); + verify(optionsProvider); + verify(templateBuilderProvider); + } + + @SuppressWarnings("unchecked") + @Test + public void testDefaultLocationWithNoOptions() { + + Location defaultLocation = createMock(Location.class); + Provider optionsProvider = createMock(Provider.class); + Provider templateBuilderProvider = createMock(Provider.class); + TemplateOptions defaultOptions = createMock(TemplateOptions.class); + + expect(defaultLocation.getId()).andReturn("foo"); + expect(optionsProvider.get()).andReturn(defaultOptions); + + replay(defaultOptions); + replay(defaultLocation); + replay(optionsProvider); + replay(templateBuilderProvider); + + TemplateBuilderImpl template = new TemplateBuilderImpl(ImmutableSet. of(), + ImmutableSet. of(), ImmutableSet. of(), defaultLocation, + optionsProvider, templateBuilderProvider); + + try { + template.imageId("foo").build(); + assert false; + } catch (NoSuchElementException e) { + + } + + verify(defaultOptions); + verify(defaultLocation); + verify(optionsProvider); + verify(templateBuilderProvider); + } + + @SuppressWarnings("unchecked") + @Test + public void testDefaultLocationWithOptions() { + + Location defaultLocation = createMock(Location.class); + Provider optionsProvider = createMock(Provider.class); + Provider templateBuilderProvider = createMock(Provider.class); + + expect(defaultLocation.getId()).andReturn("foo"); + + replay(defaultLocation); + replay(optionsProvider); + replay(templateBuilderProvider); + + TemplateBuilderImpl template = new TemplateBuilderImpl(ImmutableSet. of(), + ImmutableSet. of(), ImmutableSet. of(), defaultLocation, + optionsProvider, templateBuilderProvider); + + try { + template.imageId("foo").options(TemplateOptions.NONE).build(); + assert false; + } catch (NoSuchElementException e) { + + } + + verify(defaultLocation); + verify(optionsProvider); + verify(templateBuilderProvider); + } + + @SuppressWarnings("unchecked") @Test public void testImageIdNullsEverythingElse() { + Location defaultLocation = createMock(Location.class); + Provider optionsProvider = createMock(Provider.class); + Provider templateBuilderProvider = createMock(Provider.class); + + replay(defaultLocation); + replay(optionsProvider); + replay(templateBuilderProvider); + TemplateBuilderImpl template = new TemplateBuilderImpl(ImmutableSet. of(), - ImmutableSet. of(), ImmutableSet. of(), new LocationImpl( - LocationScope.REGION, " id", "description", null), new TemplateOptions()); + ImmutableSet. of(), ImmutableSet. of(), defaultLocation, + optionsProvider, templateBuilderProvider); + template.architecture(Architecture.X86_32); template.imageDescriptionMatches("imageDescriptionMatches"); template.imageNameMatches("imageNameMatches"); @@ -68,6 +239,9 @@ public class TemplateBuilderImplTest { assertEquals(template.os, null); assertEquals(template.imageId, "myid"); + verify(defaultLocation); + verify(optionsProvider); + verify(templateBuilderProvider); } } diff --git a/gogrid/src/main/java/org/jclouds/gogrid/compute/config/GoGridComputeServiceContextModule.java b/gogrid/src/main/java/org/jclouds/gogrid/compute/config/GoGridComputeServiceContextModule.java index 5c0487c5d9..92c724fba9 100755 --- a/gogrid/src/main/java/org/jclouds/gogrid/compute/config/GoGridComputeServiceContextModule.java +++ b/gogrid/src/main/java/org/jclouds/gogrid/compute/config/GoGridComputeServiceContextModule.java @@ -49,7 +49,6 @@ import org.jclouds.compute.domain.TemplateBuilder; import org.jclouds.compute.domain.internal.ImageImpl; import org.jclouds.compute.domain.internal.SizeImpl; import org.jclouds.compute.internal.ComputeServiceContextImpl; -import org.jclouds.compute.internal.TemplateBuilderImpl; import org.jclouds.compute.predicates.NodePredicates; import org.jclouds.compute.predicates.ScriptStatusReturnsZero; import org.jclouds.compute.predicates.ScriptStatusReturnsZero.CommandUsingClient; @@ -111,7 +110,8 @@ public class GoGridComputeServiceContextModule extends GoGridContextModule { } @Provides - TemplateBuilder provideTemplate(TemplateBuilderImpl template) { + @Named("DEFAULT") + protected TemplateBuilder provideTemplate(TemplateBuilder template) { return template.osFamily(CENTOS).imageNameMatches(".*w/ None.*"); } diff --git a/rackspace/src/main/java/org/jclouds/rackspace/cloudservers/compute/config/CloudServersComputeServiceContextModule.java b/rackspace/src/main/java/org/jclouds/rackspace/cloudservers/compute/config/CloudServersComputeServiceContextModule.java index 6bdeb1b8f2..2458775b90 100755 --- a/rackspace/src/main/java/org/jclouds/rackspace/cloudservers/compute/config/CloudServersComputeServiceContextModule.java +++ b/rackspace/src/main/java/org/jclouds/rackspace/cloudservers/compute/config/CloudServersComputeServiceContextModule.java @@ -52,7 +52,6 @@ import org.jclouds.compute.domain.internal.NodeMetadataImpl; import org.jclouds.compute.domain.internal.SizeImpl; import org.jclouds.compute.internal.BaseComputeService; import org.jclouds.compute.internal.ComputeServiceContextImpl; -import org.jclouds.compute.internal.TemplateBuilderImpl; import org.jclouds.compute.predicates.NodePredicates; import org.jclouds.compute.predicates.ScriptStatusReturnsZero; import org.jclouds.compute.predicates.ScriptStatusReturnsZero.CommandUsingClient; @@ -118,7 +117,8 @@ public class CloudServersComputeServiceContextModule extends CloudServersContext } @Provides - TemplateBuilder provideTemplate(TemplateBuilderImpl template) { + @Named("DEFAULT") + protected TemplateBuilder provideTemplate(TemplateBuilder template) { return template.osFamily(UBUNTU); } diff --git a/rimuhosting/src/main/java/org/jclouds/rimuhosting/miro/compute/config/RimuHostingComputeServiceContextModule.java b/rimuhosting/src/main/java/org/jclouds/rimuhosting/miro/compute/config/RimuHostingComputeServiceContextModule.java index 18200261b5..9b3433f397 100755 --- a/rimuhosting/src/main/java/org/jclouds/rimuhosting/miro/compute/config/RimuHostingComputeServiceContextModule.java +++ b/rimuhosting/src/main/java/org/jclouds/rimuhosting/miro/compute/config/RimuHostingComputeServiceContextModule.java @@ -55,7 +55,6 @@ import org.jclouds.compute.domain.internal.ImageImpl; import org.jclouds.compute.domain.internal.NodeMetadataImpl; import org.jclouds.compute.domain.internal.SizeImpl; import org.jclouds.compute.internal.ComputeServiceContextImpl; -import org.jclouds.compute.internal.TemplateBuilderImpl; import org.jclouds.compute.predicates.NodePredicates; import org.jclouds.compute.predicates.ScriptStatusReturnsZero; import org.jclouds.compute.predicates.ScriptStatusReturnsZero.CommandUsingClient; @@ -119,7 +118,8 @@ public class RimuHostingComputeServiceContextModule extends RimuHostingContextMo } @Provides - TemplateBuilder provideTemplate(TemplateBuilderImpl template) { + @Named("DEFAULT") + protected TemplateBuilder provideTemplate(TemplateBuilder template) { return template.sizeId("MIRO1B").osFamily(UBUNTU); } diff --git a/vcloud/core/src/main/java/org/jclouds/vcloud/compute/config/VCloudComputeServiceContextModule.java b/vcloud/core/src/main/java/org/jclouds/vcloud/compute/config/VCloudComputeServiceContextModule.java index 4b5204de3e..6c6bca21b9 100755 --- a/vcloud/core/src/main/java/org/jclouds/vcloud/compute/config/VCloudComputeServiceContextModule.java +++ b/vcloud/core/src/main/java/org/jclouds/vcloud/compute/config/VCloudComputeServiceContextModule.java @@ -54,7 +54,6 @@ import org.jclouds.compute.domain.internal.ImageImpl; import org.jclouds.compute.domain.internal.NodeMetadataImpl; import org.jclouds.compute.domain.internal.SizeImpl; import org.jclouds.compute.internal.ComputeServiceContextImpl; -import org.jclouds.compute.internal.TemplateBuilderImpl; import org.jclouds.compute.predicates.ScriptStatusReturnsZero; import org.jclouds.compute.predicates.ScriptStatusReturnsZero.CommandUsingClient; import org.jclouds.compute.reference.ComputeServiceConstants; @@ -127,7 +126,8 @@ public class VCloudComputeServiceContextModule extends VCloudContextModule { } @Provides - protected TemplateBuilder provideTemplate(TemplateBuilderImpl template) { + @Named("DEFAULT") + protected TemplateBuilder provideTemplate(TemplateBuilder template) { return template.osFamily(UBUNTU); } diff --git a/vcloud/hostingdotcom/src/main/java/org/jclouds/vcloud/hostingdotcom/compute/config/HostingDotComVCloudComputeServiceContextModule.java b/vcloud/hostingdotcom/src/main/java/org/jclouds/vcloud/hostingdotcom/compute/config/HostingDotComVCloudComputeServiceContextModule.java index 0a7513037e..1903a05736 100755 --- a/vcloud/hostingdotcom/src/main/java/org/jclouds/vcloud/hostingdotcom/compute/config/HostingDotComVCloudComputeServiceContextModule.java +++ b/vcloud/hostingdotcom/src/main/java/org/jclouds/vcloud/hostingdotcom/compute/config/HostingDotComVCloudComputeServiceContextModule.java @@ -21,7 +21,6 @@ package org.jclouds.vcloud.hostingdotcom.compute.config; import static org.jclouds.compute.domain.OsFamily.CENTOS; import org.jclouds.compute.domain.TemplateBuilder; -import org.jclouds.compute.internal.TemplateBuilderImpl; import org.jclouds.compute.strategy.PopulateDefaultLoginCredentialsForImageStrategy; import org.jclouds.compute.strategy.impl.ReturnNullCredentials; import org.jclouds.vcloud.compute.VCloudComputeClient; @@ -49,7 +48,7 @@ public class HostingDotComVCloudComputeServiceContextModule extends } @Override - protected TemplateBuilder provideTemplate(TemplateBuilderImpl template) { + protected TemplateBuilder provideTemplate(TemplateBuilder template) { return template.osFamily(CENTOS); } diff --git a/vcloud/terremark/src/main/java/org/jclouds/vcloud/terremark/compute/config/TerremarkVCloudComputeServiceContextModule.java b/vcloud/terremark/src/main/java/org/jclouds/vcloud/terremark/compute/config/TerremarkVCloudComputeServiceContextModule.java index c168c3c884..bdcf0b8ec1 100755 --- a/vcloud/terremark/src/main/java/org/jclouds/vcloud/terremark/compute/config/TerremarkVCloudComputeServiceContextModule.java +++ b/vcloud/terremark/src/main/java/org/jclouds/vcloud/terremark/compute/config/TerremarkVCloudComputeServiceContextModule.java @@ -38,7 +38,6 @@ import org.jclouds.compute.domain.Size; import org.jclouds.compute.domain.TemplateBuilder; import org.jclouds.compute.domain.internal.ImageImpl; import org.jclouds.compute.domain.internal.SizeImpl; -import org.jclouds.compute.internal.TemplateBuilderImpl; import org.jclouds.compute.strategy.PopulateDefaultLoginCredentialsForImageStrategy; import org.jclouds.concurrent.ConcurrentUtils; import org.jclouds.domain.Location; @@ -93,7 +92,7 @@ public class TerremarkVCloudComputeServiceContextModule extends VCloudComputeSer } @Override - protected TemplateBuilder provideTemplate(TemplateBuilderImpl template) { + protected TemplateBuilder provideTemplate(TemplateBuilder template) { return template.osFamily(OsFamily.UBUNTU); }