From 566aa9f0d092a41246fad40305c7bd67a023d149 Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Tue, 18 May 2010 16:28:44 -0700 Subject: [PATCH] Issue 255, 256: lazy parsing of images so you can specify imageId() that isn't in the default owner list --- .../EC2ComputeServiceContextModule.java | 120 +++++++-- .../ec2/compute/functions/ImageParser.java | 49 +++- .../compute/functions/RegionAndIdToImage.java | 65 +++++ .../RunningInstanceToNodeMetadata.java | 124 ++++++--- .../internal/EC2TemplateBuilderImpl.java | 56 +++++ .../handlers/ParseAWSErrorFromXmlContent.java | 12 +- .../compute/EC2ComputeServiceLiveTest.java | 23 -- .../compute/EC2TemplateBuilderLiveTest.java | 121 +++++++++ .../compute/functions/ImageParserTest.java | 39 +++ .../functions/RegionAndIdToImageTest.java | 98 ++++++++ .../RunningInstanceToNodeMetadataTest.java | 236 ++++++++++++++++-- .../internal/EC2TemplateBuilderImplTest.java | 160 ++++++++++++ .../aws/ec2/services/AMIClientLiveTest.java | 37 ++- .../test/resources/ec2/rightscale_images.xml | 45 ++++ aws/core/src/test/resources/log4j.xml | 9 +- .../compute/internal/TemplateBuilderImpl.java | 95 ++++--- .../compute/options/RunScriptOptions.java | 5 + .../compute/BaseComputeServiceLiveTest.java | 1 + .../internal/TemplateBuilderImplTest.java | 68 +++-- .../concurrent/internal/SyncProxy.java | 14 +- .../concurrent/internal/SyncProxyTest.java | 3 +- 21 files changed, 1192 insertions(+), 188 deletions(-) create mode 100644 aws/core/src/main/java/org/jclouds/aws/ec2/compute/functions/RegionAndIdToImage.java create mode 100644 aws/core/src/main/java/org/jclouds/aws/ec2/compute/internal/EC2TemplateBuilderImpl.java create mode 100644 aws/core/src/test/java/org/jclouds/aws/ec2/compute/EC2TemplateBuilderLiveTest.java create mode 100644 aws/core/src/test/java/org/jclouds/aws/ec2/compute/functions/RegionAndIdToImageTest.java create mode 100644 aws/core/src/test/java/org/jclouds/aws/ec2/compute/internal/EC2TemplateBuilderImplTest.java create mode 100644 aws/core/src/test/resources/ec2/rightscale_images.xml 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 52f4cebb9f..720525179e 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 @@ -22,12 +22,16 @@ import static com.google.common.base.Preconditions.checkNotNull; import static org.jclouds.aws.ec2.options.DescribeImagesOptions.Builder.ownedBy; import static org.jclouds.aws.ec2.reference.EC2Constants.PROPERTY_EC2_AMI_OWNERS; import static org.jclouds.compute.domain.OsFamily.UBUNTU; +import static org.jclouds.concurrent.ConcurrentUtils.awaitCompletion; import java.net.URI; import java.security.SecureRandom; import java.util.Map; import java.util.Set; +import java.util.concurrent.Callable; +import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; @@ -36,7 +40,7 @@ import javax.inject.Inject; import javax.inject.Named; import javax.inject.Singleton; -import org.jclouds.aws.domain.Region; +import org.jclouds.Constants; import org.jclouds.aws.ec2.EC2; import org.jclouds.aws.ec2.EC2AsyncClient; import org.jclouds.aws.ec2.EC2Client; @@ -46,13 +50,16 @@ import org.jclouds.aws.ec2.compute.domain.RegionAndName; 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.RegionAndIdToImage; import org.jclouds.aws.ec2.compute.functions.RunningInstanceToNodeMetadata; +import org.jclouds.aws.ec2.compute.internal.EC2TemplateBuilderImpl; import org.jclouds.aws.ec2.compute.options.EC2TemplateOptions; import org.jclouds.aws.ec2.compute.strategy.EC2DestroyNodeStrategy; import org.jclouds.aws.ec2.compute.strategy.EC2RunNodesAndAddToSetStrategy; import org.jclouds.aws.ec2.config.EC2ContextModule; import org.jclouds.aws.ec2.domain.KeyPair; import org.jclouds.aws.ec2.domain.RunningInstance; +import org.jclouds.aws.ec2.domain.Image.ImageType; import org.jclouds.aws.ec2.functions.RunningInstanceToStorageMappingUnix; import org.jclouds.aws.ec2.services.InstanceClient; import org.jclouds.compute.ComputeService; @@ -74,6 +81,7 @@ import org.jclouds.compute.strategy.GetNodeMetadataStrategy; import org.jclouds.compute.strategy.ListNodesStrategy; import org.jclouds.compute.strategy.RebootNodeStrategy; import org.jclouds.compute.strategy.RunNodesAndAddToSetStrategy; +import org.jclouds.concurrent.ConcurrentUtils; import org.jclouds.domain.Location; import org.jclouds.domain.LocationScope; import org.jclouds.domain.internal.LocationImpl; @@ -89,8 +97,10 @@ import com.google.common.base.Splitter; import com.google.common.base.Supplier; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; +import com.google.common.collect.MapMaker; import com.google.common.collect.Maps; import com.google.common.collect.Sets; +import com.google.common.util.concurrent.ListenableFuture; import com.google.inject.Provides; import com.google.inject.Scopes; import com.google.inject.TypeLiteral; @@ -111,6 +121,7 @@ public class EC2ComputeServiceContextModule extends EC2ContextModule { @Override protected void configure() { super.configure(); + bind(TemplateBuilder.class).to(EC2TemplateBuilderImpl.class); bind(TemplateOptions.class).to(EC2TemplateOptions.class); bind(ComputeService.class).to(EC2ComputeService.class); bind(RunNodesAndAddToSetStrategy.class).to(EC2RunNodesAndAddToSetStrategy.class); @@ -147,14 +158,23 @@ public class EC2ComputeServiceContextModule extends EC2ContextModule { // due to image parsing; consider using MapMaker. computing map @Singleton public static class EC2ListNodesStrategy implements ListNodesStrategy { + @Resource + @Named(ComputeServiceConstants.COMPUTE_LOGGER) + protected Logger logger = Logger.NULL; + private final InstanceClient client; + private final Map regionMap; private final RunningInstanceToNodeMetadata runningInstanceToNodeMetadata; + private final ExecutorService executor; @Inject - protected EC2ListNodesStrategy(InstanceClient client, - RunningInstanceToNodeMetadata runningInstanceToNodeMetadata) { + protected EC2ListNodesStrategy(InstanceClient client, @EC2 Map regionMap, + RunningInstanceToNodeMetadata runningInstanceToNodeMetadata, + @Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor) { this.client = client; + this.regionMap = regionMap; this.runningInstanceToNodeMetadata = runningInstanceToNodeMetadata; + this.executor = executor; } @Override @@ -165,11 +185,28 @@ public class EC2ComputeServiceContextModule extends EC2ContextModule { @Override public Iterable listDetailsOnNodesMatching( Predicate filter) { - Set nodes = Sets.newHashSet(); - for (String region : ImmutableSet.of(Region.US_EAST_1, Region.US_WEST_1, Region.EU_WEST_1)) { - Iterables.addAll(nodes, Iterables.transform(Iterables.concat(client - .describeInstancesInRegion(region)), runningInstanceToNodeMetadata)); + final Set nodes = Sets.newHashSet(); + + Map> parallelResponses = Maps.newHashMap(); + + for (final String region : regionMap.keySet()) { + parallelResponses.put(region, ConcurrentUtils.makeListenable(executor + .submit(new Callable() { + @Override + public Void call() throws Exception { + Iterables.addAll(nodes, Iterables.transform(Iterables.concat(client + .describeInstancesInRegion(region)), + runningInstanceToNodeMetadata)); + return null; + } + }), executor)); } + Map exceptions = awaitCompletion(parallelResponses, executor, null, + logger, "nodes"); + + if (exceptions.size() > 0) + throw new RuntimeException(String.format("error parsing nodes in regions: %s", + exceptions)); return Iterables.filter(nodes, filter); } } @@ -322,30 +359,65 @@ public class EC2ComputeServiceContextModule extends EC2ContextModule { @Singleton @Named(PROPERTY_EC2_AMI_OWNERS) String[] amiOwners(@Named(PROPERTY_EC2_AMI_OWNERS) String amiOwners) { + if (amiOwners.trim().equals("")) + return new String[] {}; return Iterables.toArray(Splitter.on(',').split(amiOwners), String.class); } @Provides - @Singleton - protected Set provideImages(final EC2Client sync, - @EC2 Map regionMap, LogHolder holder, - Function indexer, - @Named(PROPERTY_EC2_AMI_OWNERS) String[] amiOwners, ImageParser parser) - throws InterruptedException, ExecutionException, TimeoutException { - final Set images = Sets.newHashSet(); - holder.logger.debug(">> providing images"); + protected Set provideImages(Map map) { + return ImmutableSet.copyOf(map.values()); + } - for (final String region : regionMap.keySet()) { - for (final org.jclouds.aws.ec2.domain.Image from : sync.getAMIServices() - .describeImagesInRegion(region, ownedBy(amiOwners))) { - Image image = parser.apply(from); - if (image != null) - images.add(image); - else - holder.logger.trace("<< image(%s) didn't parse", from.getId()); + @Provides + @Singleton + protected ConcurrentMap provideImageMap( + RegionAndIdToImage regionAndIdToImage) { + return new MapMaker().makeComputingMap(regionAndIdToImage); + } + + @Provides + @Singleton + protected Map provideImages(final EC2Client sync, + @EC2 Map regionMap, final LogHolder holder, + Function indexer, + @Named(PROPERTY_EC2_AMI_OWNERS) final String[] amiOwners, final ImageParser parser, + final ConcurrentMap images, + @Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor) + throws InterruptedException, ExecutionException, TimeoutException { + if (amiOwners.length == 0) { + holder.logger.debug(">> no owners specified, skipping image parsing"); + } else { + holder.logger.debug(">> providing images"); + + Map> parallelResponses = Maps.newHashMap(); + + for (final String region : regionMap.keySet()) { + parallelResponses.put(region, ConcurrentUtils.makeListenable(executor + .submit(new Callable() { + @Override + public Void call() throws Exception { + for (final org.jclouds.aws.ec2.domain.Image from : sync.getAMIServices() + .describeImagesInRegion(region, ownedBy(amiOwners))) { + Image image = parser.apply(from); + if (image != null) + images.put(new RegionAndName(region, image.getId()), image); + else if (from.getImageType() == ImageType.MACHINE) + holder.logger.trace("<< image(%s) didn't parse", from.getId()); + } + return null; + } + }), executor)); } + Map exceptions = awaitCompletion(parallelResponses, executor, null, + holder.logger, "images"); + + if (exceptions.size() > 0) + throw new RuntimeException(String.format("error parsing images in regions: %s", + exceptions)); + + holder.logger.debug("<< images(%d)", images.size()); } - holder.logger.debug("<< images(%d)", images.size()); return images; } } diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/compute/functions/ImageParser.java b/aws/core/src/main/java/org/jclouds/aws/ec2/compute/functions/ImageParser.java index c57cbc1299..ad9e8468a0 100644 --- a/aws/core/src/main/java/org/jclouds/aws/ec2/compute/functions/ImageParser.java +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/compute/functions/ImageParser.java @@ -61,6 +61,14 @@ public class ImageParser implements Function NAME_VERSION_MAP = ImmutableMap . builder().put("hardy", "8.04").put("intrepid", "8.10").put("jaunty", "9.04").put("karmic", "9.10").put("lucid", "10.04").put("maverick", "10.10") @@ -96,19 +104,21 @@ public class ImageParser implements Function + * + * ==================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ==================================================================== + */ +package org.jclouds.aws.ec2.compute.functions; + +import static org.jclouds.aws.ec2.options.DescribeImagesOptions.Builder.imageIds; + +import javax.annotation.Resource; +import javax.inject.Inject; +import javax.inject.Singleton; + +import org.jclouds.aws.ec2.compute.domain.RegionAndName; +import org.jclouds.aws.ec2.services.AMIClient; +import org.jclouds.compute.domain.Image; +import org.jclouds.logging.Logger; +import org.jclouds.rest.ResourceNotFoundException; + +import com.google.common.base.Function; +import com.google.common.collect.Iterables; + +/** + * + * @author Adrian Cole + */ +@Singleton +public final class RegionAndIdToImage implements Function { + @Resource + protected Logger logger = Logger.NULL; + + private final ImageParser parser; + private final AMIClient sync; + + @Inject + public RegionAndIdToImage(ImageParser parser, AMIClient sync) { + this.parser = parser; + this.sync = sync; + } + + public Image apply(RegionAndName key) { + try { + org.jclouds.aws.ec2.domain.Image image = Iterables.getOnlyElement(sync + .describeImagesInRegion(key.getRegion(), imageIds(key.getName()))); + return parser.apply(image); + } catch (ResourceNotFoundException e) { + logger.warn(e, "no image found for %s/%s: %s", key.getRegion(), key.getName(), e + .getMessage()); + return null; + } + } +} \ No newline at end of file diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/compute/functions/RunningInstanceToNodeMetadata.java b/aws/core/src/main/java/org/jclouds/aws/ec2/compute/functions/RunningInstanceToNodeMetadata.java index 8f14bab711..a0408cc612 100644 --- a/aws/core/src/main/java/org/jclouds/aws/ec2/compute/functions/RunningInstanceToNodeMetadata.java +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/compute/functions/RunningInstanceToNodeMetadata.java @@ -26,10 +26,12 @@ import java.net.URI; import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; +import java.util.concurrent.ConcurrentMap; import javax.annotation.Resource; import javax.inject.Inject; import javax.inject.Named; +import javax.inject.Provider; import javax.inject.Singleton; import org.jclouds.aws.ec2.compute.domain.RegionAndName; @@ -63,13 +65,14 @@ public class RunningInstanceToNodeMetadata implements Function { + @VisibleForTesting + static class FindImageForInstance implements Predicate { private final Location location; private final RunningInstance instance; - private FindImageForInstance(Location location, RunningInstance instance) { - this.location = location; - this.instance = instance; + FindImageForInstance(Location location, RunningInstance instance) { + this.location = checkNotNull(location, "location"); + this.instance = checkNotNull(instance, "instance"); } @Override @@ -78,6 +81,37 @@ public class RunningInstanceToNodeMetadata implements Function instanceToNodeState = ImmutableMap @@ -88,16 +122,18 @@ public class RunningInstanceToNodeMetadata implements Function credentialsMap; private final PopulateDefaultLoginCredentialsForImageStrategy credentialProvider; - private final Set images; + private final Provider> images; private final Set locations; private final Function> instanceToStorageMapping; + private final ConcurrentMap imageMap; @Inject RunningInstanceToNodeMetadata( AMIClient amiClient, Map credentialsMap, PopulateDefaultLoginCredentialsForImageStrategy credentialProvider, - Set images, + Provider> images, // to facilitate on-demand refresh of image list + ConcurrentMap imageMap, Set locations, @Named("volumeMapping") Function> instanceToStorageMapping) { this.amiClient = checkNotNull(amiClient, "amiClient"); @@ -105,15 +141,50 @@ public class RunningInstanceToNodeMetadata implements Function userMetadata = ImmutableMap. of(); + + NodeState state = instanceToNodeState.get(instance.getInstanceState()); + + Set publicAddresses = nullSafeSet(instance.getIpAddress()); + Set privateAddresses = nullSafeSet(instance.getPrivateIpAddress()); + + Map extra = getExtra(instance); + + Location location = getLocationForAvailabilityZone(instance); + + Image image = resolveImageForInstanceInLocation(instance, location); + + return new NodeMetadataImpl(id, name, location, uri, userMetadata, tag, image, state, + publicAddresses, privateAddresses, extra, credentials); + } + + private Credentials getCredentialsForInstanceWithTag(final RunningInstance instance, String tag) { + Credentials credentials = null;// default if no keypair exists + + if (instance.getKeyName() != null) { + credentials = new Credentials(getLoginAccountFor(instance), getPrivateKeyOrNull(instance, + tag)); + } + return credentials; + } + + private String getTagForInstace(final RunningInstance instance) { String tag = String.format("NOTAG-%s", instance.getId());// default try { tag = Iterables.getOnlyElement( @@ -133,26 +204,13 @@ public class RunningInstanceToNodeMetadata implements Function userMetadata = ImmutableMap. of(); - - NodeState state = instanceToNodeState.get(instance.getInstanceState()); - - Set publicAddresses = nullSafeSet(instance.getIpAddress()); - Set privateAddresses = nullSafeSet(instance.getPrivateIpAddress()); - + private Location getLocationForAvailabilityZone(final RunningInstance instance) { final String locationId = instance.getAvailabilityZone(); - Map extra = getExtra(instance); - - final Location location = Iterables.find(locations, new Predicate() { + Location location = Iterables.find(locations, new Predicate() { @Override public boolean apply(Location input) { @@ -160,16 +218,24 @@ public class RunningInstanceToNodeMetadata implements Function imageMap; + + @Inject + protected EC2TemplateBuilderImpl(Set locations, Set images, + Set sizes, Location defaultLocation, + Provider optionsProvider, + @Named("DEFAULT") Provider defaultTemplateProvider, + ConcurrentMap imageMap) { + super(locations, images, sizes, defaultLocation, optionsProvider, defaultTemplateProvider); + this.imageMap = imageMap; + } + + /** + * @throws NoSuchElementException + * if the image is not found + */ + @Override + protected Image resolveImage() { + try { + return super.resolveImage(); + } catch (NoSuchElementException e) { + RegionAndName key = new RegionAndName(this.locationId, this.imageId); + try { + return imageMap.get(key); + } catch (NullPointerException nex) { + throw new NoSuchElementException(String.format("image %s/%s not found", + key.getRegion(), key.getName())); + } + } + } + +} diff --git a/aws/core/src/main/java/org/jclouds/aws/handlers/ParseAWSErrorFromXmlContent.java b/aws/core/src/main/java/org/jclouds/aws/handlers/ParseAWSErrorFromXmlContent.java index 0760925c2b..55f09fbc6e 100755 --- a/aws/core/src/main/java/org/jclouds/aws/handlers/ParseAWSErrorFromXmlContent.java +++ b/aws/core/src/main/java/org/jclouds/aws/handlers/ParseAWSErrorFromXmlContent.java @@ -22,6 +22,7 @@ import java.io.IOException; import javax.annotation.Resource; import javax.inject.Inject; +import javax.inject.Singleton; import org.jclouds.aws.AWSResponseException; import org.jclouds.aws.domain.AWSError; @@ -34,6 +35,7 @@ import org.jclouds.http.HttpResponse; import org.jclouds.http.HttpResponseException; import org.jclouds.logging.Logger; import org.jclouds.rest.AuthorizationException; +import org.jclouds.rest.ResourceNotFoundException; import org.jclouds.util.Utils; import com.google.common.io.Closeables; @@ -45,6 +47,7 @@ import com.google.common.io.Closeables; * @author Adrian Cole * */ +@Singleton public class ParseAWSErrorFromXmlContent implements HttpErrorHandler { @Resource protected Logger logger = Logger.NULL; @@ -60,7 +63,13 @@ public class ParseAWSErrorFromXmlContent implements HttpErrorHandler { Exception exception = new HttpResponseException(command, response); try { AWSError error = parseErrorFromContentOrNull(command, response); + exception = error != null ? new AWSResponseException(command, response, error) : exception; switch (response.getStatusCode()) { + case 400: + if (error.getCode().equals("InvalidAMIID.NotFound") + || error.getCode().equals("InvalidAMIID.Malformed")) + exception = new ResourceNotFoundException(error.getMessage(), exception); + break; case 401: exception = new AuthorizationException(command.getRequest(), error != null ? error .getMessage() : response.getStatusLine()); @@ -77,9 +86,6 @@ public class ParseAWSErrorFromXmlContent implements HttpErrorHandler { exception = new KeyNotFoundException(container, key, message); } break; - default: - exception = error != null ? new AWSResponseException(command, response, error) - : new HttpResponseException(command, response); } } finally { Closeables.closeQuietly(response.getContent()); 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 a6250aff69..18dc3b401e 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 @@ -32,10 +32,7 @@ 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.domain.Architecture; import org.jclouds.compute.domain.NodeMetadata; -import org.jclouds.compute.domain.OsFamily; -import org.jclouds.compute.domain.Template; import org.jclouds.compute.options.TemplateOptions; import org.jclouds.compute.predicates.NodePredicates; import org.jclouds.domain.Credentials; @@ -58,26 +55,6 @@ public class EC2ComputeServiceLiveTest extends BaseComputeServiceLiveTest { service = "ec2"; } - @Test - public void testTemplateBuilderCanUseImageId() { - client.templateBuilder().imageId(Iterables.get(client.listImages(), 0).getId()).build(); - } - - @Test - public void testTemplateBuilder() { - Template defaultTemplate = client.templateBuilder().build(); - assert (defaultTemplate.getImage().getId().startsWith("ami-")) : defaultTemplate; - assertEquals(defaultTemplate.getImage().getName(), "9.10"); - assertEquals(defaultTemplate.getImage().getArchitecture(), Architecture.X86_32); - assertEquals(defaultTemplate.getImage().getOsFamily(), OsFamily.UBUNTU); - assertEquals(defaultTemplate.getLocation().getId(), "us-east-1"); - assertEquals(defaultTemplate.getSize().getCores(), 1.0d); - client.templateBuilder().osFamily(OsFamily.UBUNTU).smallest().architecture( - Architecture.X86_32).imageId("ami-7e28ca17").build(); - client.templateBuilder().osFamily(OsFamily.UBUNTU).smallest().architecture( - Architecture.X86_32).imageId("ami-bb709dd2").build(); - } - @Override protected JschSshClientModule getSshModule() { return new JschSshClientModule(); diff --git a/aws/core/src/test/java/org/jclouds/aws/ec2/compute/EC2TemplateBuilderLiveTest.java b/aws/core/src/test/java/org/jclouds/aws/ec2/compute/EC2TemplateBuilderLiveTest.java new file mode 100644 index 0000000000..93f533c405 --- /dev/null +++ b/aws/core/src/test/java/org/jclouds/aws/ec2/compute/EC2TemplateBuilderLiveTest.java @@ -0,0 +1,121 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ==================================================================== + */ +package org.jclouds.aws.ec2.compute; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.testng.Assert.assertEquals; + +import java.io.IOException; +import java.util.Properties; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; + +import org.jclouds.aws.ec2.reference.EC2Constants; +import org.jclouds.compute.ComputeServiceContext; +import org.jclouds.compute.ComputeServiceContextFactory; +import org.jclouds.compute.domain.Architecture; +import org.jclouds.compute.domain.OsFamily; +import org.jclouds.compute.domain.Template; +import org.jclouds.logging.log4j.config.Log4JLoggingModule; +import org.testng.annotations.BeforeGroups; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; + +/** + * + * @author Adrian Cole + */ +@Test(groups = "live", testName = "ec2.EC2TemplateBuilderLiveTest") +public class EC2TemplateBuilderLiveTest { + private String password; + private String user; + + @BeforeGroups(groups = { "live" }) + public void setupClient() throws InterruptedException, ExecutionException, TimeoutException, + IOException { + user = checkNotNull(System.getProperty("jclouds.test.user"), "jclouds.test.user"); + password = checkNotNull(System.getProperty("jclouds.test.key"), "jclouds.test.key"); + } + + @Test + public void testTemplateBuilderCanUseImageId() { + } + + @Test + public void testTemplateBuilder() throws IOException { + ComputeServiceContext newContext = null; + try { + newContext = new ComputeServiceContextFactory().createContext("ec2", user, password, + ImmutableSet.of(new Log4JLoggingModule())); + + Template defaultTemplate = newContext.getComputeService().templateBuilder().build(); + assert (defaultTemplate.getImage().getId().startsWith("ami-")) : defaultTemplate; + assertEquals(defaultTemplate.getImage().getName(), "9.10"); + assertEquals(defaultTemplate.getImage().getArchitecture(), Architecture.X86_32); + assertEquals(defaultTemplate.getImage().getOsFamily(), OsFamily.UBUNTU); + assertEquals(defaultTemplate.getLocation().getId(), "us-east-1"); + assertEquals(defaultTemplate.getSize().getCores(), 1.0d); + newContext.getComputeService().templateBuilder().imageId( + Iterables.get(newContext.getComputeService().listImages(), 0).getId()).build(); + newContext.getComputeService().templateBuilder().osFamily(OsFamily.UBUNTU).smallest() + .architecture(Architecture.X86_32).imageId("ami-7e28ca17").build(); + newContext.getComputeService().templateBuilder().osFamily(OsFamily.UBUNTU).smallest() + .architecture(Architecture.X86_32).imageId("ami-bb709dd2").build(); + } finally { + if (newContext != null) + newContext.close(); + } + } + + @Test + public void testTemplateBuilderWithNoOwnersParsesImageOnDemand() throws IOException { + ComputeServiceContext newContext = null; + try { + Properties overrides = new Properties(); + // set owners to nothing + overrides.setProperty(EC2Constants.PROPERTY_EC2_AMI_OWNERS, ""); + + newContext = new ComputeServiceContextFactory().createContext("ec2", user, password, + ImmutableSet.of(new Log4JLoggingModule()), overrides); + + assertEquals(newContext.getComputeService().listImages().size(), 0); + + Template template = newContext.getComputeService().templateBuilder().imageId( + "ami-ccb35ea5").build(); + System.out.println(template.getImage()); + assert (template.getImage().getId().startsWith("ami-")) : template; + assertEquals(template.getImage().getName(), "5.4"); + assertEquals(template.getImage().getArchitecture(), Architecture.X86_64); + assertEquals(template.getImage().getOsFamily(), OsFamily.CENTOS); + assertEquals(template.getImage().getVersion(), "4.4.10"); + assertEquals(template.getLocation().getId(), "us-east-1"); + assertEquals(template.getSize().getCores(), 4.0d); // because it is 64bit + + //ensure we cache the new image for next time + assertEquals(newContext.getComputeService().listImages().size(), 1); + + } finally { + if (newContext != null) + newContext.close(); + } + } + +} diff --git a/aws/core/src/test/java/org/jclouds/aws/ec2/compute/functions/ImageParserTest.java b/aws/core/src/test/java/org/jclouds/aws/ec2/compute/functions/ImageParserTest.java index 3ca4208335..cce28894fa 100644 --- a/aws/core/src/test/java/org/jclouds/aws/ec2/compute/functions/ImageParserTest.java +++ b/aws/core/src/test/java/org/jclouds/aws/ec2/compute/functions/ImageParserTest.java @@ -153,6 +153,45 @@ public class ImageParserTest extends BaseEC2HandlerTest { } + public void testParseRightScaleImage() { + InputStream is = getClass().getResourceAsStream("/ec2/rightscale_images.xml"); + + Set result = parseImages(is); + + ImageParser parser = new ImageParser( + new EC2PopulateDefaultLoginCredentialsForImageStrategy(), ImmutableSet + . of(defaultLocation), defaultLocation); + + org.jclouds.compute.domain.Image image = parser.apply(Iterables.get(result, 0)); + + assertEquals(image.getArchitecture(), org.jclouds.compute.domain.Architecture.X86_64); + assertEquals(image.getDescription(), "rightscale-us-east/CentOS_5.4_x64_v4.4.10.manifest.xml"); + assertEquals(image.getId(), "ami-ccb35ea5"); + assertEquals(image.getLocation(), defaultLocation); + assertEquals(image.getName(), "5.4"); + assertEquals(image.getOsDescription(), + "rightscale-us-east/CentOS_5.4_x64_v4.4.10.manifest.xml"); + assertEquals(image.getOsFamily(), OsFamily.CENTOS); + assertEquals(image.getUserMetadata(), ImmutableMap. of("owner", + "411009282317")); + assertEquals(image.getVersion(), "4.4.10"); + + image = parser.apply(Iterables.get(result, 1)); + + assertEquals(image.getArchitecture(), org.jclouds.compute.domain.Architecture.X86_64); + assertEquals(image.getDescription(), "RightImage_Ubuntu_9.10_x64_v4.5.3_EBS_Alpha"); + assertEquals(image.getId(), "ami-c19db6b5"); + assertEquals(image.getLocation(), defaultLocation); + assertEquals(image.getName(), "9.10"); + assertEquals(image.getOsDescription(), + "411009282317/RightImage_Ubuntu_9.10_x64_v4.5.3_EBS_Alpha"); + assertEquals(image.getOsFamily(), OsFamily.UBUNTU); + assertEquals(image.getUserMetadata(), ImmutableMap. of("owner", + "411009282317")); + assertEquals(image.getVersion(), "4.5.3_EBS_Alpha"); + + } + private Set parseImages(InputStream is) { DescribeImagesResponseHandler handler = injector .getInstance(DescribeImagesResponseHandler.class); diff --git a/aws/core/src/test/java/org/jclouds/aws/ec2/compute/functions/RegionAndIdToImageTest.java b/aws/core/src/test/java/org/jclouds/aws/ec2/compute/functions/RegionAndIdToImageTest.java new file mode 100644 index 0000000000..9eeb83a241 --- /dev/null +++ b/aws/core/src/test/java/org/jclouds/aws/ec2/compute/functions/RegionAndIdToImageTest.java @@ -0,0 +1,98 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ==================================================================== + */ +package org.jclouds.aws.ec2.compute.functions; + +import static org.easymock.EasyMock.expect; +import static org.easymock.classextension.EasyMock.createMock; +import static org.easymock.classextension.EasyMock.createNiceMock; +import static org.easymock.classextension.EasyMock.replay; +import static org.easymock.classextension.EasyMock.verify; +import static org.jclouds.aws.ec2.options.DescribeImagesOptions.Builder.imageIds; +import static org.testng.Assert.assertEquals; + +import java.util.Set; + +import org.jclouds.aws.ec2.compute.domain.RegionAndName; +import org.jclouds.aws.ec2.services.AMIClient; +import org.jclouds.compute.domain.Image; +import org.jclouds.rest.ResourceNotFoundException; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableSet; + +/** + * @author Adrian Cole + */ +@Test(groups = "unit", testName = "ec2.RegionAndIdToImageTest") +public class RegionAndIdToImageTest { + + @Test + public void testApply() { + + ImageParser parser = createMock(ImageParser.class); + AMIClient client = createMock(AMIClient.class); + org.jclouds.aws.ec2.domain.Image ec2Image = createMock(org.jclouds.aws.ec2.domain.Image.class); + Image image = createNiceMock(Image.class); + Set images = ImmutableSet + . of(ec2Image); + + expect(client.describeImagesInRegion("region", imageIds("ami"))).andReturn(images); + expect(parser.apply(ec2Image)).andReturn(image); + + replay(image); + replay(parser); + replay(client); + + RegionAndIdToImage function = new RegionAndIdToImage(parser, client); + + assertEquals(function.apply(new RegionAndName("region", "ami")), image); + + verify(image); + verify(parser); + verify(client); + + } + + @Test + public void testApplyNotFound() { + + ImageParser parser = createMock(ImageParser.class); + AMIClient client = createMock(AMIClient.class); + org.jclouds.aws.ec2.domain.Image ec2Image = createMock(org.jclouds.aws.ec2.domain.Image.class); + Image image = createNiceMock(Image.class); + Set images = ImmutableSet + . of(ec2Image); + + expect(client.describeImagesInRegion("region", imageIds("ami"))).andReturn(images); + expect(parser.apply(ec2Image)).andThrow(new ResourceNotFoundException()); + + replay(image); + replay(parser); + replay(client); + + RegionAndIdToImage function = new RegionAndIdToImage(parser, client); + + assertEquals(function.apply(new RegionAndName("region", "ami")), null); + + verify(image); + verify(parser); + verify(client); + + } +} diff --git a/aws/core/src/test/java/org/jclouds/aws/ec2/compute/functions/RunningInstanceToNodeMetadataTest.java b/aws/core/src/test/java/org/jclouds/aws/ec2/compute/functions/RunningInstanceToNodeMetadataTest.java index 4f0735654d..198f9100f7 100644 --- a/aws/core/src/test/java/org/jclouds/aws/ec2/compute/functions/RunningInstanceToNodeMetadataTest.java +++ b/aws/core/src/test/java/org/jclouds/aws/ec2/compute/functions/RunningInstanceToNodeMetadataTest.java @@ -29,6 +29,9 @@ import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Map; import java.util.Set; +import java.util.concurrent.ConcurrentMap; + +import javax.inject.Provider; import org.jclouds.aws.domain.Region; import org.jclouds.aws.ec2.compute.domain.RegionAndName; @@ -55,6 +58,203 @@ import com.google.common.collect.ImmutableSet; */ @Test(groups = "unit", testName = "ec2.RunningInstanceToNodeMetadataTest") public class RunningInstanceToNodeMetadataTest { + private static class ImageProvider implements + Provider> { + private final Set images; + + private ImageProvider(org.jclouds.compute.domain.Image jcImage) { + this.images = ImmutableSet. of(jcImage); + } + + @Override + public Set get() { + return images; + } + } + + @SuppressWarnings("unchecked") + @Test + public void testImageNotFoundAndLazyReturnsNull() throws UnknownHostException { + AMIClient amiClient = createMock(AMIClient.class); + Map credentialsMap = createMock(Map.class); + org.jclouds.compute.domain.Image jcImage = createMock(org.jclouds.compute.domain.Image.class); + + ConcurrentMap imageMap = createMock(ConcurrentMap.class); + + Location location = new LocationImpl(LocationScope.ZONE, "us-east-1a", "description", null); + Set locations = ImmutableSet. of(location); + PopulateDefaultLoginCredentialsForImageStrategy credentialProvider = createMock(PopulateDefaultLoginCredentialsForImageStrategy.class); + RunningInstance instance = createMock(RunningInstance.class); + + expect(instance.getId()).andReturn("id").atLeastOnce(); + expect(instance.getGroupIds()).andReturn(ImmutableSet. of()).atLeastOnce(); + expect(instance.getKeyName()).andReturn(null).atLeastOnce(); + expect(instance.getInstanceState()).andReturn(InstanceState.RUNNING); + + expect(instance.getIpAddress()).andReturn( + InetAddress.getByAddress(new byte[] { 12, 10, 10, 1 })); + expect(instance.getPrivateIpAddress()).andReturn( + InetAddress.getByAddress(new byte[] { 10, 10, 10, 1 })); + + expect(instance.getAvailabilityZone()).andReturn(AvailabilityZone.US_EAST_1A).atLeastOnce(); + + expect(instance.getImageId()).andReturn("imageId").atLeastOnce(); + expect(jcImage.getId()).andReturn("notImageId").atLeastOnce(); + expect(instance.getRegion()).andReturn("us-east-1").atLeastOnce(); + + expect(imageMap.get(new RegionAndName("us-east-1", "imageId"))).andReturn(null); + + expect(instance.getInstanceType()).andReturn(InstanceType.C1_XLARGE).atLeastOnce(); + + replay(imageMap); + replay(jcImage); + replay(amiClient); + replay(credentialsMap); + replay(credentialProvider); + replay(instance); + + RunningInstanceToNodeMetadata parser = new RunningInstanceToNodeMetadata(amiClient, + credentialsMap, credentialProvider, new ImageProvider(jcImage), imageMap, locations, + new RunningInstanceToStorageMappingUnix()); + + NodeMetadata metadata = parser.apply(instance); + assertEquals(metadata.getLocation(), locations.iterator().next()); + assertEquals(metadata.getImage(), null); + assertEquals(metadata.getTag(), "NOTAG-id"); + assertEquals(metadata.getCredentials(), null); + + verify(imageMap); + verify(jcImage); + verify(amiClient); + verify(credentialsMap); + verify(credentialProvider); + verify(instance); + } + + @SuppressWarnings("unchecked") + @Test + public void testImageNotFoundAndLazyFailsWithNPE() throws UnknownHostException { + AMIClient amiClient = createMock(AMIClient.class); + Map credentialsMap = createMock(Map.class); + org.jclouds.compute.domain.Image jcImage = createMock(org.jclouds.compute.domain.Image.class); + + ConcurrentMap imageMap = createMock(ConcurrentMap.class); + + Location location = new LocationImpl(LocationScope.ZONE, "us-east-1a", "description", null); + Set locations = ImmutableSet. of(location); + PopulateDefaultLoginCredentialsForImageStrategy credentialProvider = createMock(PopulateDefaultLoginCredentialsForImageStrategy.class); + RunningInstance instance = createMock(RunningInstance.class); + + expect(instance.getId()).andReturn("id").atLeastOnce(); + expect(instance.getGroupIds()).andReturn(ImmutableSet. of()).atLeastOnce(); + expect(instance.getKeyName()).andReturn(null).atLeastOnce(); + expect(instance.getInstanceState()).andReturn(InstanceState.RUNNING); + + expect(instance.getIpAddress()).andReturn( + InetAddress.getByAddress(new byte[] { 12, 10, 10, 1 })); + expect(instance.getPrivateIpAddress()).andReturn( + InetAddress.getByAddress(new byte[] { 10, 10, 10, 1 })); + + expect(instance.getAvailabilityZone()).andReturn(AvailabilityZone.US_EAST_1A).atLeastOnce(); + + expect(instance.getImageId()).andReturn("imageId").atLeastOnce(); + expect(jcImage.getId()).andReturn("notImageId").atLeastOnce(); + expect(instance.getRegion()).andReturn("us-east-1").atLeastOnce(); + + expect(imageMap.get(new RegionAndName("us-east-1", "imageId"))).andThrow( + new NullPointerException()).atLeastOnce(); + + expect(instance.getInstanceType()).andReturn(InstanceType.C1_XLARGE).atLeastOnce(); + + replay(imageMap); + replay(jcImage); + replay(amiClient); + replay(credentialsMap); + replay(credentialProvider); + replay(instance); + + RunningInstanceToNodeMetadata parser = new RunningInstanceToNodeMetadata(amiClient, + credentialsMap, credentialProvider, new ImageProvider(jcImage), imageMap, locations, + new RunningInstanceToStorageMappingUnix()); + + NodeMetadata metadata = parser.apply(instance); + assertEquals(metadata.getLocation(), locations.iterator().next()); + assertEquals(metadata.getImage(), null); + assertEquals(metadata.getTag(), "NOTAG-id"); + assertEquals(metadata.getCredentials(), null); + + verify(imageMap); + verify(jcImage); + verify(amiClient); + verify(credentialsMap); + verify(credentialProvider); + verify(instance); + } + + @SuppressWarnings("unchecked") + @Test + public void testImageNotFoundAndLazySucceeds() throws UnknownHostException { + AMIClient amiClient = createMock(AMIClient.class); + Map credentialsMap = createMock(Map.class); + org.jclouds.compute.domain.Image jcImage = createMock(org.jclouds.compute.domain.Image.class); + + ConcurrentMap imageMap = createMock(ConcurrentMap.class); + + Location location = new LocationImpl(LocationScope.ZONE, "us-east-1a", "description", null); + Set locations = ImmutableSet. of(location); + PopulateDefaultLoginCredentialsForImageStrategy credentialProvider = createMock(PopulateDefaultLoginCredentialsForImageStrategy.class); + RunningInstance instance = createMock(RunningInstance.class); + + expect(instance.getId()).andReturn("id").atLeastOnce(); + expect(instance.getGroupIds()).andReturn(ImmutableSet. of()).atLeastOnce(); + expect(instance.getKeyName()).andReturn(null).atLeastOnce(); + expect(instance.getInstanceState()).andReturn(InstanceState.RUNNING); + + expect(instance.getIpAddress()).andReturn( + InetAddress.getByAddress(new byte[] { 12, 10, 10, 1 })); + expect(instance.getPrivateIpAddress()).andReturn( + InetAddress.getByAddress(new byte[] { 10, 10, 10, 1 })); + + expect(instance.getAvailabilityZone()).andReturn(AvailabilityZone.US_EAST_1A).atLeastOnce(); + + expect(instance.getImageId()).andReturn("imageId").atLeastOnce(); + expect(jcImage.getId()).andReturn("notImageId").atLeastOnce(); + expect(instance.getRegion()).andReturn("us-east-1").atLeastOnce(); + + org.jclouds.compute.domain.Image lateImage = createMock(org.jclouds.compute.domain.Image.class); + + expect(imageMap.get(new RegionAndName("us-east-1", "imageId"))).andReturn(lateImage) + .atLeastOnce(); + + expect(instance.getInstanceType()).andReturn(InstanceType.C1_XLARGE).atLeastOnce(); + + replay(lateImage); + replay(imageMap); + replay(jcImage); + replay(amiClient); + replay(credentialsMap); + replay(credentialProvider); + replay(instance); + + RunningInstanceToNodeMetadata parser = new RunningInstanceToNodeMetadata(amiClient, + credentialsMap, credentialProvider, new ImageProvider(jcImage), imageMap, locations, + new RunningInstanceToStorageMappingUnix()); + + NodeMetadata metadata = parser.apply(instance); + assertEquals(metadata.getLocation(), locations.iterator().next()); + assertEquals(metadata.getImage(), lateImage); + assertEquals(metadata.getTag(), "NOTAG-id"); + assertEquals(metadata.getCredentials(), null); + + verify(lateImage); + verify(imageMap); + verify(jcImage); + verify(amiClient); + verify(credentialsMap); + verify(credentialProvider); + verify(instance); + } + @SuppressWarnings("unchecked") @Test public void testApplyWithNoSecurityGroupCreatesTagOfIdPrefixedByTagAndNullCredentials() @@ -63,8 +263,7 @@ public class RunningInstanceToNodeMetadataTest { Map credentialsMap = createMock(Map.class); org.jclouds.compute.domain.Image jcImage = createMock(org.jclouds.compute.domain.Image.class); - Set images = ImmutableSet - . of(jcImage); + ConcurrentMap imageMap = createMock(ConcurrentMap.class); Location location = new LocationImpl(LocationScope.ZONE, "us-east-1a", "description", null); Set locations = ImmutableSet. of(location); @@ -89,6 +288,7 @@ public class RunningInstanceToNodeMetadataTest { expect(instance.getInstanceType()).andReturn(InstanceType.C1_XLARGE).atLeastOnce(); + replay(imageMap); replay(jcImage); replay(amiClient); replay(credentialsMap); @@ -96,7 +296,7 @@ public class RunningInstanceToNodeMetadataTest { replay(instance); RunningInstanceToNodeMetadata parser = new RunningInstanceToNodeMetadata(amiClient, - credentialsMap, credentialProvider, images, locations, + credentialsMap, credentialProvider, new ImageProvider(jcImage), imageMap, locations, new RunningInstanceToStorageMappingUnix()); NodeMetadata metadata = parser.apply(instance); @@ -105,6 +305,7 @@ public class RunningInstanceToNodeMetadataTest { assertEquals(metadata.getTag(), "NOTAG-id"); assertEquals(metadata.getCredentials(), null); + verify(imageMap); verify(jcImage); verify(amiClient); verify(credentialsMap); @@ -119,9 +320,7 @@ public class RunningInstanceToNodeMetadataTest { AMIClient amiClient = createMock(AMIClient.class); Map credentialsMap = createMock(Map.class); org.jclouds.compute.domain.Image jcImage = createMock(org.jclouds.compute.domain.Image.class); - - Set images = ImmutableSet - . of(jcImage); + ConcurrentMap imageMap = createMock(ConcurrentMap.class); Location location = new LocationImpl(LocationScope.ZONE, "us-east-1a", "description", null); Set locations = ImmutableSet. of(location); @@ -146,6 +345,7 @@ public class RunningInstanceToNodeMetadataTest { expect(instance.getInstanceType()).andReturn(InstanceType.C1_XLARGE).atLeastOnce(); + replay(imageMap); replay(jcImage); replay(amiClient); replay(credentialsMap); @@ -153,7 +353,7 @@ public class RunningInstanceToNodeMetadataTest { replay(instance); RunningInstanceToNodeMetadata parser = new RunningInstanceToNodeMetadata(amiClient, - credentialsMap, credentialProvider, images, locations, + credentialsMap, credentialProvider, new ImageProvider(jcImage), imageMap, locations, new RunningInstanceToStorageMappingUnix()); NodeMetadata metadata = parser.apply(instance); @@ -162,6 +362,7 @@ public class RunningInstanceToNodeMetadataTest { assertEquals(metadata.getTag(), "tag"); assertEquals(metadata.getCredentials(), null); + verify(imageMap); verify(jcImage); verify(amiClient); verify(credentialsMap); @@ -175,6 +376,7 @@ public class RunningInstanceToNodeMetadataTest { throws UnknownHostException { AMIClient amiClient = createMock(AMIClient.class); Map credentialsMap = createMock(Map.class); + ConcurrentMap imageMap = createMock(ConcurrentMap.class); PopulateDefaultLoginCredentialsForImageStrategy credentialProvider = createMock(PopulateDefaultLoginCredentialsForImageStrategy.class); RunningInstance instance = createMock(RunningInstance.class); @@ -188,8 +390,6 @@ public class RunningInstanceToNodeMetadataTest { Location location = new LocationImpl(LocationScope.ZONE, "us-east-1a", "description", null); Set locations = ImmutableSet. of(location); org.jclouds.compute.domain.Image jcImage = createMock(org.jclouds.compute.domain.Image.class); - Set images = ImmutableSet - . of(jcImage); expect(instance.getIpAddress()).andReturn( InetAddress.getByAddress(new byte[] { 12, 10, 10, 1 })); @@ -214,6 +414,7 @@ public class RunningInstanceToNodeMetadataTest { expect(instance.getInstanceType()).andReturn(InstanceType.C1_XLARGE).atLeastOnce(); + replay(imageMap); replay(amiClient); replay(credentialsMap); replay(credentialProvider); @@ -221,9 +422,8 @@ public class RunningInstanceToNodeMetadataTest { replay(jcImage); RunningInstanceToNodeMetadata parser = new RunningInstanceToNodeMetadata(amiClient, - credentialsMap, credentialProvider, images, locations, + credentialsMap, credentialProvider, new ImageProvider(jcImage), imageMap, locations, new RunningInstanceToStorageMappingUnix()); - NodeMetadata metadata = parser.apply(instance); assertEquals(metadata.getTag(), "tag"); @@ -232,6 +432,7 @@ public class RunningInstanceToNodeMetadataTest { assertEquals(metadata.getCredentials(), new Credentials("user", "pass")); + verify(imageMap); verify(jcImage); verify(amiClient); verify(credentialsMap); @@ -242,25 +443,24 @@ public class RunningInstanceToNodeMetadataTest { @SuppressWarnings("unchecked") @Test - public void testApplyWithTwoSecurityGroups() - throws UnknownHostException { + public void testApplyWithTwoSecurityGroups() throws UnknownHostException { AMIClient amiClient = createMock(AMIClient.class); Map credentialsMap = createMock(Map.class); + ConcurrentMap imageMap = createMock(ConcurrentMap.class); PopulateDefaultLoginCredentialsForImageStrategy credentialProvider = createMock(PopulateDefaultLoginCredentialsForImageStrategy.class); RunningInstance instance = createMock(RunningInstance.class); Image image = createMock(Image.class); expect(instance.getId()).andReturn("id").atLeastOnce(); - expect(instance.getGroupIds()).andReturn(ImmutableSet.of("jclouds#tag","jclouds#tag2")).atLeastOnce(); + expect(instance.getGroupIds()).andReturn(ImmutableSet.of("jclouds#tag", "jclouds#tag2")) + .atLeastOnce(); expect(instance.getKeyName()).andReturn("jclouds#keyName").atLeastOnce(); expect(instance.getInstanceState()).andReturn(InstanceState.RUNNING); Location location = new LocationImpl(LocationScope.ZONE, "us-east-1a", "description", null); Set locations = ImmutableSet. of(location); org.jclouds.compute.domain.Image jcImage = createMock(org.jclouds.compute.domain.Image.class); - Set images = ImmutableSet - . of(jcImage); expect(instance.getIpAddress()).andReturn( InetAddress.getByAddress(new byte[] { 12, 10, 10, 1 })); @@ -285,6 +485,7 @@ public class RunningInstanceToNodeMetadataTest { expect(instance.getInstanceType()).andReturn(InstanceType.C1_XLARGE).atLeastOnce(); + replay(imageMap); replay(amiClient); replay(credentialsMap); replay(credentialProvider); @@ -292,7 +493,7 @@ public class RunningInstanceToNodeMetadataTest { replay(jcImage); RunningInstanceToNodeMetadata parser = new RunningInstanceToNodeMetadata(amiClient, - credentialsMap, credentialProvider, images, locations, + credentialsMap, credentialProvider, new ImageProvider(jcImage), imageMap, locations, new RunningInstanceToStorageMappingUnix()); NodeMetadata metadata = parser.apply(instance); @@ -303,6 +504,7 @@ public class RunningInstanceToNodeMetadataTest { assertEquals(metadata.getCredentials(), new Credentials("user", "pass")); + verify(imageMap); verify(jcImage); verify(amiClient); verify(credentialsMap); diff --git a/aws/core/src/test/java/org/jclouds/aws/ec2/compute/internal/EC2TemplateBuilderImplTest.java b/aws/core/src/test/java/org/jclouds/aws/ec2/compute/internal/EC2TemplateBuilderImplTest.java new file mode 100644 index 0000000000..3ddccd0629 --- /dev/null +++ b/aws/core/src/test/java/org/jclouds/aws/ec2/compute/internal/EC2TemplateBuilderImplTest.java @@ -0,0 +1,160 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ==================================================================== + */ +package org.jclouds.aws.ec2.compute.internal; + +import static org.easymock.EasyMock.expect; +import static org.easymock.classextension.EasyMock.createMock; +import static org.easymock.classextension.EasyMock.createNiceMock; +import static org.easymock.classextension.EasyMock.replay; +import static org.easymock.classextension.EasyMock.verify; +import static org.testng.Assert.assertEquals; + +import java.util.EnumSet; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.concurrent.ConcurrentMap; + +import javax.inject.Provider; + +import org.jclouds.aws.ec2.compute.domain.RegionAndName; +import org.jclouds.compute.domain.Architecture; +import org.jclouds.compute.domain.Image; +import org.jclouds.compute.domain.Size; +import org.jclouds.compute.domain.TemplateBuilder; +import org.jclouds.compute.domain.internal.SizeImpl; +import org.jclouds.compute.internal.TemplateBuilderImpl; +import org.jclouds.compute.internal.TemplateBuilderImplTest; +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.BeforeTest; +import org.testng.annotations.Test; + +import com.google.common.base.Function; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.MapMaker; + +/** + * + * @author Adrian Cole + */ +@Test(groups = "unit", sequential = true) +public class EC2TemplateBuilderImplTest extends TemplateBuilderImplTest { + + RegionAndName knownRegionAndName = new RegionAndName("region", "ami"); + Image knownImage = createNiceMock(Image.class); + + ConcurrentMap imageMap = new MapMaker() + .makeComputingMap(new Function() { + @Override + public Image apply(RegionAndName from) { + return from.equals(knownRegionAndName) ? knownImage : null; + } + + }); + + @BeforeTest + void setup() { + knownImage = createNiceMock(Image.class); + } + + @Override + protected EC2TemplateBuilderImpl createTemplateBuilder(Set locations, + Set images, Set sizes, Location defaultLocation, + Provider optionsProvider, + Provider templateBuilderProvider) { + return new EC2TemplateBuilderImpl(locations, images, sizes, defaultLocation, optionsProvider, + templateBuilderProvider, imageMap); + } + + @SuppressWarnings("unchecked") + @Test + public void testParseOnDemand() { + Location location = new LocationImpl(LocationScope.REGION, "region", "region", null); + Set locations = ImmutableSet. of(location); + Set images = ImmutableSet. of(); + Set sizes = ImmutableSet. of(new SizeImpl(null, null, location, null, + ImmutableMap. of(), 1, 1, 1, EnumSet.allOf(Architecture.class))); + Location defaultLocation = createMock(Location.class); + Provider optionsProvider = createMock(Provider.class); + Provider templateBuilderProvider = createMock(Provider.class); + TemplateOptions defaultOptions = createMock(TemplateOptions.class); + knownImage = createMock(Image.class); + + expect(defaultLocation.getId()).andReturn("region"); + expect(optionsProvider.get()).andReturn(defaultOptions); + expect(knownImage.getArchitecture()).andReturn(Architecture.X86_32); + + replay(knownImage); + replay(defaultOptions); + replay(defaultLocation); + replay(optionsProvider); + replay(templateBuilderProvider); + + TemplateBuilderImpl template = createTemplateBuilder(locations, images, sizes, + defaultLocation, optionsProvider, templateBuilderProvider); + + assertEquals(template.imageId("ami").build().getImage(), knownImage); + + verify(knownImage); + verify(defaultOptions); + verify(defaultLocation); + verify(optionsProvider); + verify(templateBuilderProvider); + } + + @SuppressWarnings("unchecked") + @Test(expectedExceptions = NoSuchElementException.class) + public void testParseOnDemandNotFound() { + Location location = new LocationImpl(LocationScope.REGION, "region", "region", null); + Set locations = ImmutableSet. of(location); + Set images = ImmutableSet. of(); + Set sizes = ImmutableSet. of(new SizeImpl(null, null, location, null, + ImmutableMap. of(), 1, 1, 1, EnumSet.allOf(Architecture.class))); + Location defaultLocation = createMock(Location.class); + Provider optionsProvider = createMock(Provider.class); + Provider templateBuilderProvider = createMock(Provider.class); + TemplateOptions defaultOptions = createMock(TemplateOptions.class); + knownImage = createMock(Image.class); + + expect(defaultLocation.getId()).andReturn("region"); + expect(optionsProvider.get()).andReturn(defaultOptions); + expect(knownImage.getArchitecture()).andReturn(Architecture.X86_32).atLeastOnce(); + + replay(knownImage); + replay(defaultOptions); + replay(defaultLocation); + replay(optionsProvider); + replay(templateBuilderProvider); + + TemplateBuilderImpl template = createTemplateBuilder(locations, images, sizes, + defaultLocation, optionsProvider, templateBuilderProvider); + + assertEquals(template.imageId("bad").build().getImage(), knownImage); + + verify(knownImage); + verify(defaultOptions); + verify(defaultLocation); + verify(optionsProvider); + verify(templateBuilderProvider); + } + +} diff --git a/aws/core/src/test/java/org/jclouds/aws/ec2/services/AMIClientLiveTest.java b/aws/core/src/test/java/org/jclouds/aws/ec2/services/AMIClientLiveTest.java index bc07e9f54c..d45b9a38e8 100644 --- a/aws/core/src/test/java/org/jclouds/aws/ec2/services/AMIClientLiveTest.java +++ b/aws/core/src/test/java/org/jclouds/aws/ec2/services/AMIClientLiveTest.java @@ -37,6 +37,7 @@ import org.jclouds.aws.ec2.domain.Image; import org.jclouds.aws.ec2.domain.RootDeviceType; import org.jclouds.aws.ec2.domain.Image.ImageType; import org.jclouds.logging.log4j.config.Log4JLoggingModule; +import org.jclouds.rest.ResourceNotFoundException; import org.jclouds.rest.RestContext; import org.testng.annotations.AfterTest; import org.testng.annotations.BeforeGroups; @@ -74,9 +75,19 @@ public class AMIClientLiveTest { client = context.getApi().getAMIServices(); } + @Test(expectedExceptions = ResourceNotFoundException.class) + public void testDescribeImageNotExists() { + client.describeImagesInRegion(null, imageIds("ami-cdf819a3")); + } + + @Test(expectedExceptions = ResourceNotFoundException.class) + public void testDescribeImageBadId() { + client.describeImagesInRegion(null, imageIds("asdaasdsa")); + } + + @Test(enabled = false) public void testDescribeImages() { - for (String region : ImmutableSet.of(Region.EU_WEST_1, Region.US_EAST_1, - Region.US_WEST_1)) { + for (String region : ImmutableSet.of(Region.EU_WEST_1, Region.US_EAST_1, Region.US_WEST_1)) { SortedSet allResults = Sets.newTreeSet(client.describeImagesInRegion(region)); assertNotNull(allResults); assert allResults.size() >= 2 : allResults.size(); @@ -95,8 +106,8 @@ public class AMIClientLiveTest { @Test(enabled = false) public void testRegisterImageFromManifest() { - String imageRegisteredId = client.registerImageFromManifestInRegion(null, - "jcloudstest1", DEFAULT_MANIFEST); + String imageRegisteredId = client.registerImageFromManifestInRegion(null, "jcloudstest1", + DEFAULT_MANIFEST); imagesToDeregister.add(imageRegisteredId); Image imageRegisteredFromManifest = Iterables.getOnlyElement(client.describeImagesInRegion( null, imageIds(imageRegisteredId))); @@ -109,8 +120,8 @@ public class AMIClientLiveTest { @Test(enabled = false) public void testRegisterImageFromManifestOptions() { - String imageRegisteredWithOptionsId = client.registerImageFromManifestInRegion( - null, "jcloudstest2", DEFAULT_MANIFEST, withDescription("adrian")); + String imageRegisteredWithOptionsId = client.registerImageFromManifestInRegion(null, + "jcloudstest2", DEFAULT_MANIFEST, withDescription("adrian")); imagesToDeregister.add(imageRegisteredWithOptionsId); Image imageRegisteredFromManifestWithOptions = Iterables.getOnlyElement(client .describeImagesInRegion(null, imageIds(imageRegisteredWithOptionsId))); @@ -126,11 +137,11 @@ public class AMIClientLiveTest { @Test(enabled = false) // awaiting EBS functionality to be added to jclouds public void testRegisterImageBackedByEBS() { - String imageRegisteredId = client.registerUnixImageBackedByEbsInRegion(null, - "jcloudstest1", DEFAULT_MANIFEST); + String imageRegisteredId = client.registerUnixImageBackedByEbsInRegion(null, "jcloudstest1", + DEFAULT_MANIFEST); imagesToDeregister.add(imageRegisteredId); - Image imageRegistered = Iterables.getOnlyElement(client.describeImagesInRegion( - null, imageIds(imageRegisteredId))); + Image imageRegistered = Iterables.getOnlyElement(client.describeImagesInRegion(null, + imageIds(imageRegisteredId))); assertEquals(imageRegistered.getName(), "jcloudstest1"); assertEquals(imageRegistered.getImageType(), ImageType.MACHINE); assertEquals(imageRegistered.getRootDeviceType(), RootDeviceType.EBS); @@ -140,9 +151,9 @@ public class AMIClientLiveTest { @Test(enabled = false) // awaiting EBS functionality to be added to jclouds public void testRegisterImageBackedByEBSOptions() { - String imageRegisteredWithOptionsId = client.registerUnixImageBackedByEbsInRegion( - null, "jcloudstest2", DEFAULT_SNAPSHOT, addNewBlockDevice("/dev/sda2", - "myvirtual", 1).withDescription("adrian")); + String imageRegisteredWithOptionsId = client.registerUnixImageBackedByEbsInRegion(null, + "jcloudstest2", DEFAULT_SNAPSHOT, addNewBlockDevice("/dev/sda2", "myvirtual", 1) + .withDescription("adrian")); imagesToDeregister.add(imageRegisteredWithOptionsId); Image imageRegisteredWithOptions = Iterables.getOnlyElement(client.describeImagesInRegion( null, imageIds(imageRegisteredWithOptionsId))); diff --git a/aws/core/src/test/resources/ec2/rightscale_images.xml b/aws/core/src/test/resources/ec2/rightscale_images.xml new file mode 100644 index 0000000000..042ef5ce1b --- /dev/null +++ b/aws/core/src/test/resources/ec2/rightscale_images.xml @@ -0,0 +1,45 @@ + + + 50c73a72-cf38-462f-aba2-f59116380d36 + + + ami-ccb35ea5 + rightscale-us-east/CentOS_5.4_x64_v4.4.10.manifest.xml + available + 411009282317 + true + x86_64 + machine + aki-b51cf9dc + ari-b31cf9da + instance-store + /dev/sda1 + + + + ami-c19db6b5 + 411009282317/RightImage_Ubuntu_9.10_x64_v4.5.3_EBS_Alpha + available + 411009282317 + true + x86_64 + machine + aki-a22a01d6 + ari-ac2a01d8 + RightImage_Ubuntu_9.10_x64_v4.5.3_EBS_Alpha + RightImage_Ubuntu_9.10_x64_v4.5.3_EBS_Alpha + ebs + /dev/sda1 + + + /dev/sda1 + + snap-a222ddcb + 10 + true + + + + + + diff --git a/aws/core/src/test/resources/log4j.xml b/aws/core/src/test/resources/log4j.xml index ed303459db..1e01882894 100644 --- a/aws/core/src/test/resources/log4j.xml +++ b/aws/core/src/test/resources/log4j.xml @@ -122,7 +122,7 @@ - + @@ -136,15 +136,16 @@ - + - + + 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 f7cae4632a..939dbf2aa5 100644 --- a/compute/src/main/java/org/jclouds/compute/internal/TemplateBuilderImpl.java +++ b/compute/src/main/java/org/jclouds/compute/internal/TemplateBuilderImpl.java @@ -69,33 +69,33 @@ public class TemplateBuilderImpl implements TemplateBuilder { private final Location defaultLocation; @VisibleForTesting - OsFamily os; + protected OsFamily os; @VisibleForTesting - Architecture arch; + protected Architecture arch; @VisibleForTesting - String locationId; + protected String locationId; @VisibleForTesting - String imageId; + protected String imageId; @VisibleForTesting - String sizeId; + protected String sizeId; @VisibleForTesting - String osDescription; + protected String osDescription; @VisibleForTesting - String imageVersion; + protected String imageVersion; @VisibleForTesting - String imageName; + protected String imageName; @VisibleForTesting - String imageDescription; + protected String imageDescription; @VisibleForTesting - double minCores; + protected double minCores; @VisibleForTesting - int minRam; + protected int minRam; @VisibleForTesting - boolean biggest; + protected boolean biggest; @VisibleForTesting - boolean fastest; + protected boolean fastest; @VisibleForTesting - TemplateOptions options; + protected TemplateOptions options; @Inject protected TemplateBuilderImpl(Set locations, Set images, @@ -401,28 +401,19 @@ public class TemplateBuilderImpl implements TemplateBuilder { if (options == null) options = optionsProvider.get(); logger.debug(">> searching params(%s)", this); - Image image; - try { - image = DEFAULT_IMAGE_ORDERING.max(Iterables.filter(images, imagePredicate)); - } catch (NoSuchElementException exception) { - throw new NoSuchElementException("image didn't match: " + toString() + "\n" + images); - } - logger.debug("<< matched image(%s)", image); + Location location = resolveLocation(); + Image image = resolveImage(); + // ensure we have an architecture matching this.arch = image.getArchitecture(); - Ordering sizeOrdering = DEFAULT_SIZE_ORDERING; - if (!biggest) - sizeOrdering = sizeOrdering.reverse(); - if (fastest) - sizeOrdering = Ordering.compound(ImmutableList.of(BY_CORES_ORDERING, sizeOrdering)); - Size size; - try { - size = sizeOrdering.max(Iterables.filter(sizes, sizePredicate)); - } catch (NoSuchElementException exception) { - throw new NoSuchElementException("size didn't match: " + toString() + "\n" + sizes); - } - logger.debug("<< matched size(%s)", size); + Ordering sizeOrdering = sizeSorter(); + Size size = resolveSize(sizeOrdering); + + return new TemplateImpl(image, size, location, options); + } + + protected Location resolveLocation() { Location location = Iterables.find(locations, new Predicate() { @Override @@ -432,7 +423,43 @@ public class TemplateBuilderImpl implements TemplateBuilder { }); logger.debug("<< matched location(%s)", location); - return new TemplateImpl(image, size, location, options); + return location; + } + + protected Size resolveSize(Ordering sizeOrdering) { + Size size; + try { + size = sizeOrdering.max(Iterables.filter(sizes, sizePredicate)); + } catch (NoSuchElementException exception) { + throw new NoSuchElementException("size didn't match: " + toString() + "\n" + sizes); + } + logger.debug("<< matched size(%s)", size); + return size; + } + + protected Ordering sizeSorter() { + Ordering sizeOrdering = DEFAULT_SIZE_ORDERING; + if (!biggest) + sizeOrdering = sizeOrdering.reverse(); + if (fastest) + sizeOrdering = Ordering.compound(ImmutableList.of(BY_CORES_ORDERING, sizeOrdering)); + return sizeOrdering; + } + + /** + * + * @throws NoSuchElementException + * if there's no image that matches the predicate + */ + protected Image resolveImage() { + Image image; + try { + image = DEFAULT_IMAGE_ORDERING.max(Iterables.filter(images, imagePredicate)); + } catch (NoSuchElementException exception) { + throw new NoSuchElementException("image didn't match: " + toString() + "\n" + images); + } + logger.debug("<< matched image(%s)", image); + return image; } /** diff --git a/compute/src/main/java/org/jclouds/compute/options/RunScriptOptions.java b/compute/src/main/java/org/jclouds/compute/options/RunScriptOptions.java index f442abf8aa..0a342d3636 100644 --- a/compute/src/main/java/org/jclouds/compute/options/RunScriptOptions.java +++ b/compute/src/main/java/org/jclouds/compute/options/RunScriptOptions.java @@ -18,6 +18,8 @@ */ package org.jclouds.compute.options; +import static com.google.common.base.Preconditions.checkNotNull; + import org.jclouds.domain.Credentials; /** @@ -77,6 +79,9 @@ public class RunScriptOptions { private boolean runAsRoot = true; public RunScriptOptions withOverridingCredentials(Credentials overridingCredentials) { + checkNotNull(overridingCredentials, "overridingCredentials"); + checkNotNull(overridingCredentials.account, "overridingCredentials.account"); + checkNotNull(overridingCredentials.key, "overridingCredentials.key"); this.overridingCredentials = overridingCredentials; return this; } diff --git a/compute/src/test/java/org/jclouds/compute/BaseComputeServiceLiveTest.java b/compute/src/test/java/org/jclouds/compute/BaseComputeServiceLiveTest.java index 28af8ca104..b4b8d76d62 100755 --- a/compute/src/test/java/org/jclouds/compute/BaseComputeServiceLiveTest.java +++ b/compute/src/test/java/org/jclouds/compute/BaseComputeServiceLiveTest.java @@ -231,6 +231,7 @@ public abstract class BaseComputeServiceLiveTest { Set nodes = client.runNodesWithTag(tag, 1, options); Credentials good = nodes.iterator().next().getCredentials(); assert good.account != null; + assert good.key != null; Image image = Iterables.get(nodes, 0).getImage(); try { 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 b6ae70b650..754c7a5b60 100644 --- a/compute/src/test/java/org/jclouds/compute/internal/TemplateBuilderImplTest.java +++ b/compute/src/test/java/org/jclouds/compute/internal/TemplateBuilderImplTest.java @@ -25,6 +25,7 @@ import static org.easymock.classextension.EasyMock.verify; import static org.testng.Assert.assertEquals; import java.util.NoSuchElementException; +import java.util.Set; import javax.inject.Provider; @@ -49,7 +50,9 @@ public class TemplateBuilderImplTest { @SuppressWarnings("unchecked") @Test public void testNothingUsesDefaultTemplateBuilder() { - + Set locations = ImmutableSet. of(); + Set images = ImmutableSet. of(); + Set sizes = ImmutableSet. of(); Location defaultLocation = createMock(Location.class); Provider optionsProvider = createMock(Provider.class); Provider templateBuilderProvider = createMock(Provider.class); @@ -63,9 +66,8 @@ public class TemplateBuilderImplTest { replay(optionsProvider); replay(templateBuilderProvider); - TemplateBuilderImpl template = new TemplateBuilderImpl(ImmutableSet. of(), - ImmutableSet. of(), ImmutableSet. of(), defaultLocation, - optionsProvider, templateBuilderProvider); + TemplateBuilderImpl template = createTemplateBuilder(locations, images, sizes, + defaultLocation, optionsProvider, templateBuilderProvider); template.build(); @@ -75,10 +77,20 @@ public class TemplateBuilderImplTest { verify(templateBuilderProvider); } + protected TemplateBuilderImpl createTemplateBuilder(Set locations, Set images, + Set sizes, Location defaultLocation, Provider optionsProvider, + Provider templateBuilderProvider) { + TemplateBuilderImpl template = new TemplateBuilderImpl(locations, images, sizes, + defaultLocation, optionsProvider, templateBuilderProvider); + return template; + } + @SuppressWarnings("unchecked") @Test public void testSuppliedLocationWithNoOptions() { - + Set locations = ImmutableSet. of(); + Set images = ImmutableSet. of(); + Set sizes = ImmutableSet. of(); Location defaultLocation = createMock(Location.class); Provider optionsProvider = createMock(Provider.class); Provider templateBuilderProvider = createMock(Provider.class); @@ -91,9 +103,8 @@ public class TemplateBuilderImplTest { replay(optionsProvider); replay(templateBuilderProvider); - TemplateBuilderImpl template = new TemplateBuilderImpl(ImmutableSet. of(), - ImmutableSet. of(), ImmutableSet. of(), defaultLocation, - optionsProvider, templateBuilderProvider); + TemplateBuilderImpl template = createTemplateBuilder(locations, images, sizes, + defaultLocation, optionsProvider, templateBuilderProvider); try { template.imageId("foo").locationId("location").build(); @@ -101,7 +112,7 @@ public class TemplateBuilderImplTest { } catch (NoSuchElementException e) { } - + verify(defaultOptions); verify(defaultLocation); verify(optionsProvider); @@ -111,7 +122,9 @@ public class TemplateBuilderImplTest { @SuppressWarnings("unchecked") @Test public void testSuppliedLocationAndOptions() { - + Set locations = ImmutableSet. of(); + Set images = ImmutableSet. of(); + Set sizes = ImmutableSet. of(); Location defaultLocation = createMock(Location.class); Provider optionsProvider = createMock(Provider.class); Provider templateBuilderProvider = createMock(Provider.class); @@ -120,9 +133,8 @@ public class TemplateBuilderImplTest { replay(optionsProvider); replay(templateBuilderProvider); - TemplateBuilderImpl template = new TemplateBuilderImpl(ImmutableSet. of(), - ImmutableSet. of(), ImmutableSet. of(), defaultLocation, - optionsProvider, templateBuilderProvider); + TemplateBuilderImpl template = createTemplateBuilder(locations, images, sizes, + defaultLocation, optionsProvider, templateBuilderProvider); try { template.imageId("foo").options(TemplateOptions.NONE).locationId("location").build(); @@ -138,8 +150,10 @@ public class TemplateBuilderImplTest { @SuppressWarnings("unchecked") @Test - public void testDefaultLocationWithNoOptions() { - + public void testDefaultLocationWithNoOptionsNoSuchElement() { + Set locations = ImmutableSet. of(); + Set images = ImmutableSet. of(); + Set sizes = ImmutableSet. of(); Location defaultLocation = createMock(Location.class); Provider optionsProvider = createMock(Provider.class); Provider templateBuilderProvider = createMock(Provider.class); @@ -153,9 +167,8 @@ public class TemplateBuilderImplTest { replay(optionsProvider); replay(templateBuilderProvider); - TemplateBuilderImpl template = new TemplateBuilderImpl(ImmutableSet. of(), - ImmutableSet. of(), ImmutableSet. of(), defaultLocation, - optionsProvider, templateBuilderProvider); + TemplateBuilderImpl template = createTemplateBuilder(locations, images, sizes, + defaultLocation, optionsProvider, templateBuilderProvider); try { template.imageId("foo").build(); @@ -163,7 +176,7 @@ public class TemplateBuilderImplTest { } catch (NoSuchElementException e) { } - + verify(defaultOptions); verify(defaultLocation); verify(optionsProvider); @@ -173,7 +186,9 @@ public class TemplateBuilderImplTest { @SuppressWarnings("unchecked") @Test public void testDefaultLocationWithOptions() { - + Set locations = ImmutableSet. of(); + Set images = ImmutableSet. of(); + Set sizes = ImmutableSet. of(); Location defaultLocation = createMock(Location.class); Provider optionsProvider = createMock(Provider.class); Provider templateBuilderProvider = createMock(Provider.class); @@ -184,9 +199,8 @@ public class TemplateBuilderImplTest { replay(optionsProvider); replay(templateBuilderProvider); - TemplateBuilderImpl template = new TemplateBuilderImpl(ImmutableSet. of(), - ImmutableSet. of(), ImmutableSet. of(), defaultLocation, - optionsProvider, templateBuilderProvider); + TemplateBuilderImpl template = createTemplateBuilder(locations, images, sizes, + defaultLocation, optionsProvider, templateBuilderProvider); try { template.imageId("foo").options(TemplateOptions.NONE).build(); @@ -203,6 +217,9 @@ public class TemplateBuilderImplTest { @SuppressWarnings("unchecked") @Test public void testImageIdNullsEverythingElse() { + Set locations = ImmutableSet. of(); + Set images = ImmutableSet. of(); + Set sizes = ImmutableSet. of(); Location defaultLocation = createMock(Location.class); Provider optionsProvider = createMock(Provider.class); Provider templateBuilderProvider = createMock(Provider.class); @@ -211,9 +228,8 @@ public class TemplateBuilderImplTest { replay(optionsProvider); replay(templateBuilderProvider); - TemplateBuilderImpl template = new TemplateBuilderImpl(ImmutableSet. of(), - ImmutableSet. of(), ImmutableSet. of(), defaultLocation, - optionsProvider, templateBuilderProvider); + TemplateBuilderImpl template = createTemplateBuilder(locations, images, sizes, + defaultLocation, optionsProvider, templateBuilderProvider); template.architecture(Architecture.X86_32); template.imageDescriptionMatches("imageDescriptionMatches"); diff --git a/core/src/main/java/org/jclouds/concurrent/internal/SyncProxy.java b/core/src/main/java/org/jclouds/concurrent/internal/SyncProxy.java index 08868d90e0..c7a043859f 100644 --- a/core/src/main/java/org/jclouds/concurrent/internal/SyncProxy.java +++ b/core/src/main/java/org/jclouds/concurrent/internal/SyncProxy.java @@ -24,6 +24,7 @@ import java.lang.reflect.Proxy; import java.util.Arrays; import java.util.Map; import java.util.Set; +import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import javax.annotation.Resource; @@ -120,12 +121,23 @@ public class SyncProxy implements InvocationHandler { try { return ((ListenableFuture) methodMap.get(method).invoke(delegate, args)).get( timeoutMap.get(method), TimeUnit.NANOSECONDS); + } catch (ExecutionException e) { + throw typedExceptionOrPropagate(method.getExceptionTypes(), e.getCause()); } catch (Exception e) { - throw Throwables.getRootCause(e); + throw typedExceptionOrPropagate(method.getExceptionTypes(), e); } } } + public static Throwable typedExceptionOrPropagate(Class[] exceptionTypes, Throwable throwable) { + for (Class type : exceptionTypes) { + if (type.isInstance(throwable)) { + return throwable; + } + } + return Throwables.propagate(throwable); + } + @Override public boolean equals(Object obj) { if (obj == null || !(obj instanceof SyncProxy)) diff --git a/core/src/test/java/org/jclouds/concurrent/internal/SyncProxyTest.java b/core/src/test/java/org/jclouds/concurrent/internal/SyncProxyTest.java index 775142e9d4..14d5af8d9a 100644 --- a/core/src/test/java/org/jclouds/concurrent/internal/SyncProxyTest.java +++ b/core/src/test/java/org/jclouds/concurrent/internal/SyncProxyTest.java @@ -24,7 +24,6 @@ import static org.testng.Assert.assertEquals; import java.io.FileNotFoundException; import java.io.IOException; import java.io.UnsupportedEncodingException; -import java.lang.reflect.UndeclaredThrowableException; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -168,7 +167,7 @@ public class SyncProxyTest { } - @Test(expectedExceptions = UndeclaredThrowableException.class) + @Test(expectedExceptions = RuntimeException.class) public void testTake100MillisecondsAndTimeout() { assertEquals(sync.take100MillisecondsAndTimeout(), "foo");