Issue 763: don't retrieve all images when template.imageId supplied

This commit is contained in:
Aled Sage 2011-12-01 23:58:34 +00:00 committed by Adrian Cole
parent 38feabf015
commit 635c420a92
11 changed files with 353 additions and 159 deletions

View File

@ -39,6 +39,7 @@ import org.jclouds.ec2.compute.domain.RegionAndName;
import com.google.common.base.Supplier; import com.google.common.base.Supplier;
import com.google.common.cache.Cache; import com.google.common.cache.Cache;
import com.google.common.collect.ImmutableSet;
import com.google.common.util.concurrent.UncheckedExecutionException; import com.google.common.util.concurrent.UncheckedExecutionException;
/** /**
@ -47,7 +48,7 @@ import com.google.common.util.concurrent.UncheckedExecutionException;
*/ */
public class EC2TemplateBuilderImpl extends TemplateBuilderImpl { public class EC2TemplateBuilderImpl extends TemplateBuilderImpl {
private final Supplier<Cache<RegionAndName, ? extends Image>> imageMap; private final Supplier<Cache<RegionAndName, ? extends Image>> lazyImageCache;
@Inject @Inject
protected EC2TemplateBuilderImpl(@Memoized Supplier<Set<? extends Location>> locations, protected EC2TemplateBuilderImpl(@Memoized Supplier<Set<? extends Location>> locations,
@ -55,7 +56,7 @@ public class EC2TemplateBuilderImpl extends TemplateBuilderImpl {
Supplier<Location> defaultLocation, @Named("DEFAULT") Provider<TemplateOptions> optionsProvider, Supplier<Location> defaultLocation, @Named("DEFAULT") Provider<TemplateOptions> optionsProvider,
@Named("DEFAULT") Provider<TemplateBuilder> defaultTemplateProvider, Supplier<Cache<RegionAndName, ? extends Image>> imageMap) { @Named("DEFAULT") Provider<TemplateBuilder> defaultTemplateProvider, Supplier<Cache<RegionAndName, ? extends Image>> imageMap) {
super(locations, images, sizes, defaultLocation, optionsProvider, defaultTemplateProvider); super(locations, images, sizes, defaultLocation, optionsProvider, defaultTemplateProvider);
this.imageMap = imageMap; this.lazyImageCache = imageMap;
} }
final Provider<Image> lazyImageProvider = new Provider<Image>() { final Provider<Image> lazyImageProvider = new Provider<Image>() {
@ -68,7 +69,7 @@ public class EC2TemplateBuilderImpl extends TemplateBuilderImpl {
"amazon image ids must include the region ( ex. us-east-1/ami-7ea24a17 ) you specified: " + imageId); "amazon image ids must include the region ( ex. us-east-1/ami-7ea24a17 ) you specified: " + imageId);
RegionAndName key = new RegionAndName(regionName[0], regionName[1]); RegionAndName key = new RegionAndName(regionName[0], regionName[1]);
try { try {
return imageMap.get().get(key); return lazyImageCache.get().get(key);
} catch (ExecutionException e) { } catch (ExecutionException e) {
throw new NoSuchElementException(String.format("could not get imageId(%s/%s)", key.getRegion(), key.getName())); throw new NoSuchElementException(String.format("could not get imageId(%s/%s)", key.getRegion(), key.getName()));
} catch (UncheckedExecutionException e) { } catch (UncheckedExecutionException e) {
@ -101,14 +102,11 @@ public class EC2TemplateBuilderImpl extends TemplateBuilderImpl {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
protected Set<? extends Image> getImages() { protected Set<? extends Image> getImages() {
Set<Image> images = (Set<Image>) this.images.get(); if (imageId != null) {
if (images.size() == 0) { Image image = lazyImageProvider.get();
Image toReturn = lazyImageProvider.get(); return ImmutableSet.of(image);
if (toReturn != null) { } else {
images.add(toReturn); return (Set<Image>) this.images.get();
} }
} }
return images;
}
} }

View File

@ -18,16 +18,38 @@
*/ */
package org.jclouds.ec2.compute.suppliers; package org.jclouds.ec2.compute.suppliers;
import static com.google.common.collect.Iterables.filter;
import static com.google.common.collect.Iterables.transform;
import static com.google.common.collect.Maps.uniqueIndex;
import static org.jclouds.ec2.options.DescribeImagesOptions.Builder.ownedBy;
import static org.jclouds.ec2.reference.EC2Constants.PROPERTY_EC2_AMI_OWNERS;
import java.util.Collections;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
import javax.annotation.Resource;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton; import javax.inject.Singleton;
import org.jclouds.compute.domain.Image; import org.jclouds.compute.domain.Image;
import org.jclouds.compute.reference.ComputeServiceConstants;
import org.jclouds.ec2.compute.domain.RegionAndName; import org.jclouds.ec2.compute.domain.RegionAndName;
import org.jclouds.ec2.compute.functions.EC2ImageParser;
import org.jclouds.ec2.compute.strategy.DescribeImagesParallel;
import org.jclouds.ec2.options.DescribeImagesOptions;
import org.jclouds.location.Region;
import org.jclouds.logging.Logger;
import com.google.common.base.Function;
import com.google.common.base.Predicates;
import com.google.common.base.Supplier; import com.google.common.base.Supplier;
import com.google.common.cache.Cache; import com.google.common.cache.Cache;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMap.Builder;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
/** /**
@ -36,16 +58,75 @@ import com.google.common.collect.Sets;
*/ */
@Singleton @Singleton
public class EC2ImageSupplier implements Supplier<Set<? extends Image>> { public class EC2ImageSupplier implements Supplier<Set<? extends Image>> {
private final Supplier<Cache<RegionAndName, ? extends Image>> map; @Resource
@Named(ComputeServiceConstants.COMPUTE_LOGGER)
protected Logger logger = Logger.NULL;
private final Set<String> regions;
private final DescribeImagesParallel describer;
private final String[] amiOwners;
private final EC2ImageParser parser;
private final Supplier<Cache<RegionAndName, ? extends Image>> cache;
@Inject @Inject
EC2ImageSupplier(Supplier<Cache<RegionAndName, ? extends Image>> map) { protected EC2ImageSupplier(@Region Set<String> regions, DescribeImagesParallel describer,
this.map = map; @Named(PROPERTY_EC2_AMI_OWNERS) String[] amiOwners, Supplier<Cache<RegionAndName, ? extends Image>> cache,
EC2ImageParser parser) {
this.regions = regions;
this.describer = describer;
this.amiOwners = amiOwners;
this.cache = cache;
this.parser = parser;
} }
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override @Override
public Set<? extends Image> get() { public Set<? extends Image> get() {
return Sets.newLinkedHashSet(map.get().asMap().values()); if (amiOwners.length == 0) {
logger.debug(">> no owners specified, skipping image parsing");
return Collections.emptySet();
} else {
logger.debug(">> providing images");
Iterable<Entry<String, DescribeImagesOptions>> queries = getDescribeQueriesForOwnersInRegions(regions,
amiOwners);
Iterable<? extends Image> parsedImages = ImmutableSet.copyOf(filter(transform(describer.apply(queries), parser), Predicates
.notNull()));
ImmutableMap<RegionAndName, ? extends Image> imageMap = uniqueIndex(parsedImages, new Function<Image, RegionAndName>() {
@Override
public RegionAndName apply(Image from) {
return new RegionAndName(from.getLocation().getId(), from.getProviderId());
} }
});
cache.get().invalidateAll();
cache.get().asMap().putAll((Map)imageMap);
logger.debug("<< images(%d)", imageMap.size());
return Sets.newLinkedHashSet(imageMap.values());
}
}
public Iterable<Entry<String, DescribeImagesOptions>> getDescribeQueriesForOwnersInRegions(Set<String> regions,
String[] amiOwners) {
DescribeImagesOptions options = getOptionsForOwners(amiOwners);
Builder<String, DescribeImagesOptions> builder = ImmutableMap.<String, DescribeImagesOptions> builder();
for (String region : regions)
builder.put(region, options);
return builder.build().entrySet();
}
public DescribeImagesOptions getOptionsForOwners(String... amiOwners) {
DescribeImagesOptions options;
if (amiOwners.length == 1 && amiOwners[0].equals("*"))
options = new DescribeImagesOptions();
else
options = ownedBy(amiOwners);
return options;
}
} }

View File

@ -18,38 +18,16 @@
*/ */
package org.jclouds.ec2.compute.suppliers; package org.jclouds.ec2.compute.suppliers;
import static com.google.common.collect.Iterables.filter;
import static com.google.common.collect.Iterables.transform;
import static com.google.common.collect.Maps.uniqueIndex;
import static org.jclouds.ec2.options.DescribeImagesOptions.Builder.ownedBy;
import static org.jclouds.ec2.reference.EC2Constants.PROPERTY_EC2_AMI_OWNERS;
import java.util.Set;
import java.util.Map.Entry;
import javax.annotation.Resource;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton; import javax.inject.Singleton;
import org.jclouds.compute.domain.Image; import org.jclouds.compute.domain.Image;
import org.jclouds.compute.reference.ComputeServiceConstants;
import org.jclouds.ec2.compute.domain.RegionAndName; import org.jclouds.ec2.compute.domain.RegionAndName;
import org.jclouds.ec2.compute.functions.EC2ImageParser;
import org.jclouds.ec2.compute.strategy.DescribeImagesParallel;
import org.jclouds.ec2.options.DescribeImagesOptions;
import org.jclouds.location.Region;
import org.jclouds.logging.Logger;
import com.google.common.base.Function;
import com.google.common.base.Predicates;
import com.google.common.base.Supplier; import com.google.common.base.Supplier;
import com.google.common.cache.Cache; import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader; import com.google.common.cache.CacheLoader;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableMap.Builder;
/** /**
* *
@ -57,69 +35,15 @@ import com.google.common.collect.ImmutableMap.Builder;
*/ */
@Singleton @Singleton
public class RegionAndNameToImageSupplier implements Supplier<Cache<RegionAndName, ? extends Image>> { public class RegionAndNameToImageSupplier implements Supplier<Cache<RegionAndName, ? extends Image>> {
@Resource private final Cache<RegionAndName, Image> cache;
@Named(ComputeServiceConstants.COMPUTE_LOGGER)
protected Logger logger = Logger.NULL;
private final Set<String> regions;
private final DescribeImagesParallel describer;
private final String[] amiOwners;
private final EC2ImageParser parser;
private final CacheLoader<RegionAndName, Image> regionAndIdToImage;
@Inject @Inject
protected RegionAndNameToImageSupplier(@Region Set<String> regions, DescribeImagesParallel describer, protected RegionAndNameToImageSupplier(CacheLoader<RegionAndName, Image> regionAndIdToImage) {
@Named(PROPERTY_EC2_AMI_OWNERS) String[] amiOwners, EC2ImageParser parser, CacheLoader<RegionAndName, Image> regionAndIdToImage) { cache = CacheBuilder.newBuilder().build(regionAndIdToImage);
this.regions = regions;
this.describer = describer;
this.amiOwners = amiOwners;
this.parser = parser;
this.regionAndIdToImage = regionAndIdToImage;
} }
@Override @Override
public Cache<RegionAndName, ? extends Image> get() { public Cache<RegionAndName, ? extends Image> get() {
Cache<RegionAndName, Image> cache = CacheBuilder.newBuilder().build(regionAndIdToImage);
if (amiOwners.length == 0) {
logger.debug(">> no owners specified, skipping image parsing");
} else {
logger.debug(">> providing images");
Iterable<Entry<String, DescribeImagesOptions>> queries = getDescribeQueriesForOwnersInRegions(regions,
amiOwners);
Iterable<? extends Image> parsedImages = ImmutableSet.copyOf(filter(transform(describer.apply(queries), parser), Predicates
.notNull()));
cache.asMap().putAll(uniqueIndex(parsedImages, new Function<Image, RegionAndName>() {
@Override
public RegionAndName apply(Image from) {
return new RegionAndName(from.getLocation().getId(), from.getProviderId());
}
}));
logger.debug("<< images(%d)", cache.asMap().size());
}
return cache; return cache;
} }
public Iterable<Entry<String, DescribeImagesOptions>> getDescribeQueriesForOwnersInRegions(Set<String> regions,
String[] amiOwners) {
DescribeImagesOptions options = getOptionsForOwners(amiOwners);
Builder<String, DescribeImagesOptions> builder = ImmutableMap.<String, DescribeImagesOptions> builder();
for (String region : regions)
builder.put(region, options);
return builder.build().entrySet();
}
public DescribeImagesOptions getOptionsForOwners(String... amiOwners) {
DescribeImagesOptions options;
if (amiOwners.length == 1 && amiOwners[0].equals("*"))
options = new DescribeImagesOptions();
else
options = ownedBy(amiOwners);
return options;
}
} }

View File

@ -22,6 +22,7 @@ import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue; import static org.testng.Assert.assertTrue;
import java.util.Map; import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set; import java.util.Set;
import org.jclouds.compute.BaseComputeServiceLiveTest; import org.jclouds.compute.BaseComputeServiceLiveTest;
@ -52,6 +53,7 @@ import org.jclouds.sshj.config.SshjSshClientModule;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import com.google.common.base.Stopwatch;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSortedSet; import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
@ -237,6 +239,45 @@ public class EC2ComputeServiceLiveTest extends BaseComputeServiceLiveTest {
} }
} }
/**
* e.g. on aws-ec2: timeByImageId=534ms; timeByOsFamily=11587ms.
* Expecting it to be at least 2 times faster seems reasonable, including on other ec2 flavours.
*/
@Test(enabled = true)
public void testTemplateBuildsFasterByImageIdThanBySearchingAllImages() throws Exception {
Stopwatch stopwatch = new Stopwatch();
// Find any image, and get its id
template = buildTemplate(client.templateBuilder());
String imageId = template.getImage().getId();
// Build a template using that specific image-id
context.close();
setupClient();
stopwatch.start();
client.templateBuilder().imageId(imageId).build();
stopwatch.stop();
long timeByImageId = stopwatch.elapsedMillis();
// Build a template using that specific image-id
context.close();
setupClient();
stopwatch.reset();
stopwatch.start();
try {
client.templateBuilder().osFamily(OsFamily.UBUNTU).build();
} catch (NoSuchElementException e) {
// ignore; we are only interested in how long it took to establish this fact!
}
stopwatch.stop();
long timeByOsFamily = stopwatch.elapsedMillis();
System.out.println("testTemplateBuildsFasterByImageIdThanBySearchingAllImages: " +
"timeByImageId="+timeByImageId+"ms; timeByOsFamily="+timeByOsFamily+"ms");
assertTrue((timeByImageId*2) < timeByOsFamily, "timeByImageId="+timeByImageId+"; timeByOsFamily="+timeByOsFamily);
}
protected RunningInstance getInstance(InstanceClient instanceClient, String id) { protected RunningInstance getInstance(InstanceClient instanceClient, String id) {
RunningInstance instance = Iterables.getOnlyElement(Iterables.getOnlyElement(instanceClient RunningInstance instance = Iterables.getOnlyElement(Iterables.getOnlyElement(instanceClient
.describeInstancesInRegion(null, id))); .describeInstancesInRegion(null, id)));

View File

@ -18,6 +18,7 @@
*/ */
package org.jclouds.ec2.compute; package org.jclouds.ec2.compute;
import static com.google.common.collect.Maps.uniqueIndex;
import static java.lang.String.format; import static java.lang.String.format;
import static org.easymock.EasyMock.expect; import static org.easymock.EasyMock.expect;
import static org.easymock.classextension.EasyMock.createMock; import static org.easymock.classextension.EasyMock.createMock;
@ -34,6 +35,8 @@ import static org.jclouds.ec2.compute.domain.EC2HardwareBuilder.m2_xlarge;
import static org.jclouds.ec2.compute.domain.EC2HardwareBuilder.t1_micro; import static org.jclouds.ec2.compute.domain.EC2HardwareBuilder.t1_micro;
import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertEquals;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set; import java.util.Set;
import javax.inject.Provider; import javax.inject.Provider;
@ -52,11 +55,18 @@ import org.jclouds.domain.Location;
import org.jclouds.domain.LocationBuilder; import org.jclouds.domain.LocationBuilder;
import org.jclouds.domain.LocationScope; import org.jclouds.domain.LocationScope;
import org.jclouds.domain.LoginCredentials; import org.jclouds.domain.LoginCredentials;
import org.jclouds.ec2.compute.domain.RegionAndName;
import org.jclouds.ec2.compute.internal.EC2TemplateBuilderImpl;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.base.Functions;
import com.google.common.base.Supplier; import com.google.common.base.Supplier;
import com.google.common.base.Suppliers; import com.google.common.base.Suppliers;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
/** /**
@ -137,9 +147,69 @@ public class EC2TemplateBuilderTest {
.getHardware().getId()); .getHardware().getId());
} }
@SuppressWarnings("unchecked") @Test
private TemplateBuilder newTemplateBuilder() { public void testTemplateChoiceForInstanceByImageId() throws Exception {
Template template = newTemplateBuilder().imageId("us-east-1/cc-image").build();
assert template != null : "The returned template was null, but it should have a value.";
assertEquals(template.getImage().getId(), "us-east-1/cc-image");
}
@Test
public void testTemplateChoiceForInstanceByImageIdDoesNotGetAllImages() throws Exception {
@SuppressWarnings("unchecked")
Supplier<Set<? extends Image>> images = createMock(Supplier.class);
replay(images);
final Image image = new ImageBuilder().providerId("cc-image").name("image").id("us-east-1/cc-image").location(location)
.operatingSystem(new OperatingSystem(OsFamily.UBUNTU, null, "1.0", "hvm", "ubuntu", true))
.description("description").version("1.0").defaultCredentials(new LoginCredentials("root", null, null, false))
.build();
Map<RegionAndName, Image> imageMap = ImmutableMap.of(
new RegionAndName(image.getLocation().getId(), image.getProviderId()), image);
// weird compilation error means have to declare extra generics for call to build() - see https://bugs.eclipse.org/bugs/show_bug.cgi?id=365818
Supplier<Cache<RegionAndName, ? extends Image>> imageCache = Suppliers.<Cache<RegionAndName, ? extends Image>> ofInstance(
CacheBuilder.newBuilder().<RegionAndName,Image>build(CacheLoader.from(Functions.forMap(imageMap))));
Template template = newTemplateBuilder(images, imageCache).imageId("us-east-1/cc-image").build();
assert template != null : "The returned template was null, but it should have a value.";
assertEquals(template.getImage().getId(), "us-east-1/cc-image");
}
@Test(expectedExceptions={NoSuchElementException.class})
public void testNegativeTemplateChoiceForInstanceByImageId() throws Exception {
newTemplateBuilder().imageId("wrongregion/wrongimageid").build();
}
private TemplateBuilder newTemplateBuilder() {
final Supplier<Set<? extends Image>> images = Suppliers.<Set<? extends Image>> ofInstance(ImmutableSet.<Image> of(
new ImageBuilder().providerId("cc-image").name("image").id("us-east-1/cc-image").location(location)
.operatingSystem(new OperatingSystem(OsFamily.UBUNTU, null, "1.0", "hvm", "ubuntu", true))
.description("description").version("1.0").defaultCredentials(new LoginCredentials("root", null, null, false))
.build(),
new ImageBuilder().providerId("normal-image").name("image").id("us-east-1/normal-image").location(location)
.operatingSystem(new OperatingSystem(OsFamily.UBUNTU, null, "1.0", "paravirtual", "ubuntu", true))
.description("description").version("1.0").defaultCredentials(new LoginCredentials("root", null, null, false))
.build()));
// weird compilation error means have to cast this - see https://bugs.eclipse.org/bugs/show_bug.cgi?id=365818
@SuppressWarnings("unchecked")
ImmutableMap<RegionAndName, Image> imageMap = (ImmutableMap<RegionAndName, Image>) uniqueIndex(images.get(), new Function<Image, RegionAndName>() {
@Override
public RegionAndName apply(Image from) {
return new RegionAndName(from.getLocation().getId(), from.getProviderId());
}
});
Supplier<Cache<RegionAndName, ? extends Image>> imageCache = Suppliers.<Cache<RegionAndName, ? extends Image>> ofInstance(
CacheBuilder.newBuilder().<RegionAndName,Image>build(CacheLoader.from(Functions.forMap(imageMap))));
return newTemplateBuilder(images, imageCache);
}
@SuppressWarnings("unchecked")
private TemplateBuilder newTemplateBuilder(Supplier<Set<? extends Image>> images, Supplier<Cache<RegionAndName, ? extends Image>> imageCache) {
Provider<TemplateOptions> optionsProvider = createMock(Provider.class); Provider<TemplateOptions> optionsProvider = createMock(Provider.class);
Provider<TemplateBuilder> templateBuilderProvider = createMock(Provider.class); Provider<TemplateBuilder> templateBuilderProvider = createMock(Provider.class);
TemplateOptions defaultOptions = createMock(TemplateOptions.class); TemplateOptions defaultOptions = createMock(TemplateOptions.class);
@ -150,23 +220,13 @@ public class EC2TemplateBuilderTest {
replay(templateBuilderProvider); replay(templateBuilderProvider);
Supplier<Set<? extends Location>> locations = Suppliers.<Set<? extends Location>> ofInstance(ImmutableSet Supplier<Set<? extends Location>> locations = Suppliers.<Set<? extends Location>> ofInstance(ImmutableSet
.<Location> of(location)); .<Location> of(location));
Supplier<Set<? extends Image>> images = Suppliers.<Set<? extends Image>> ofInstance(ImmutableSet.<Image> of(
new ImageBuilder().providerId("cc-image").name("image").id("us-east-1/cc-image").location(location)
.operatingSystem(new OperatingSystem(OsFamily.UBUNTU, null, "1.0", "hvm", "ubuntu", true))
.description("description").version("1.0").defaultCredentials(new LoginCredentials("root", null, null, false))
.build(), new ImageBuilder().providerId("normal-image").name("image").id("us-east-1/cc-image")
.location(location).operatingSystem(
new OperatingSystem(OsFamily.UBUNTU, null, "1.0", "paravirtual", "ubuntu", true))
.description("description").version("1.0").defaultCredentials(new LoginCredentials("root", null, null, false))
.build()));
Supplier<Set<? extends Hardware>> sizes = Suppliers.<Set<? extends Hardware>> ofInstance(ImmutableSet Supplier<Set<? extends Hardware>> sizes = Suppliers.<Set<? extends Hardware>> ofInstance(ImmutableSet
.<Hardware> of(t1_micro().build(), c1_medium().build(), c1_xlarge().build(), m1_large().build(), .<Hardware> of(t1_micro().build(), c1_medium().build(), c1_xlarge().build(), m1_large().build(),
m1_small32().build(), m1_xlarge().build(), m2_xlarge().build(), m2_2xlarge().build(), m1_small32().build(), m1_xlarge().build(), m2_xlarge().build(), m2_2xlarge().build(),
m2_4xlarge().build(), CC1_4XLARGE)); m2_4xlarge().build(), CC1_4XLARGE));
return new TemplateBuilderImpl(locations, images, sizes, Suppliers.ofInstance(location), optionsProvider, return new EC2TemplateBuilderImpl(locations, images, sizes, Suppliers.ofInstance(location), optionsProvider,
templateBuilderProvider) { templateBuilderProvider, imageCache) {
}; };
} }
@ -178,5 +238,4 @@ public class EC2TemplateBuilderTest {
} }
}; };
} }
} }

View File

@ -18,6 +18,7 @@
*/ */
package org.jclouds.ec2.compute.internal; package org.jclouds.ec2.compute.internal;
import static com.google.common.collect.Maps.uniqueIndex;
import static org.easymock.EasyMock.expect; import static org.easymock.EasyMock.expect;
import static org.easymock.classextension.EasyMock.createMock; import static org.easymock.classextension.EasyMock.createMock;
import static org.easymock.classextension.EasyMock.replay; import static org.easymock.classextension.EasyMock.replay;
@ -45,6 +46,7 @@ import org.jclouds.ec2.compute.domain.RegionAndName;
import org.jclouds.ec2.compute.options.EC2TemplateOptions; import org.jclouds.ec2.compute.options.EC2TemplateOptions;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import com.google.common.base.Function;
import com.google.common.base.Supplier; import com.google.common.base.Supplier;
import com.google.common.base.Suppliers; import com.google.common.base.Suppliers;
import com.google.common.cache.Cache; import com.google.common.cache.Cache;
@ -67,12 +69,15 @@ public class EC2TemplateBuilderImplTest extends TemplateBuilderImplTest {
@Override @Override
protected EC2TemplateBuilderImpl createTemplateBuilder(final Image knownImage, protected EC2TemplateBuilderImpl createTemplateBuilder(final Image knownImage,
@Memoized Supplier<Set<? extends Location>> locations, @Memoized Supplier<Set<? extends Image>> images, @Memoized Supplier<Set<? extends Location>> locations, final @Memoized Supplier<Set<? extends Image>> images,
@Memoized Supplier<Set<? extends Hardware>> sizes, Location defaultLocation, @Memoized Supplier<Set<? extends Hardware>> sizes, Location defaultLocation,
Provider<TemplateOptions> optionsProvider, Provider<TemplateBuilder> templateBuilderProvider) { Provider<TemplateOptions> optionsProvider, Provider<TemplateBuilder> templateBuilderProvider) {
final RegionAndName knownRegionAndName = new RegionAndName("region", "ami");
Cache<RegionAndName, ? extends Image> imageMap = CacheBuilder.newBuilder().build(new CacheLoader<RegionAndName, Image>() { Cache<RegionAndName, ? extends Image> imageMap;
if (knownImage != null) {
final RegionAndName knownRegionAndName = new RegionAndName(knownImage.getLocation().getId(), knownImage.getProviderId());
imageMap = CacheBuilder.newBuilder().build(new CacheLoader<RegionAndName, Image>() {
@Override @Override
public Image load(RegionAndName from) { public Image load(RegionAndName from) {
return from.equals(knownRegionAndName) ? knownImage : null; return from.equals(knownRegionAndName) ? knownImage : null;
@ -80,6 +85,23 @@ public class EC2TemplateBuilderImplTest extends TemplateBuilderImplTest {
}); });
} else {
imageMap = CacheBuilder.newBuilder().build(new CacheLoader<RegionAndName, Image>() {
@Override
public Image load(RegionAndName from) {
return uniqueIndex(images.get(), new Function<Image, RegionAndName>() {
@Override
public RegionAndName apply(Image from) {
return new RegionAndName(from.getLocation().getId(), from.getProviderId());
}
}).get(from);
}
});
}
return new EC2TemplateBuilderImpl(locations, images, sizes, Suppliers.ofInstance(defaultLocation), return new EC2TemplateBuilderImpl(locations, images, sizes, Suppliers.ofInstance(defaultLocation),
optionsProvider, templateBuilderProvider, Suppliers.<Cache<RegionAndName, ? extends Image>>ofInstance(imageMap)); optionsProvider, templateBuilderProvider, Suppliers.<Cache<RegionAndName, ? extends Image>>ofInstance(imageMap));
} }
@ -109,6 +131,7 @@ public class EC2TemplateBuilderImplTest extends TemplateBuilderImplTest {
expect(knownImage.getName()).andReturn(null).atLeastOnce(); expect(knownImage.getName()).andReturn(null).atLeastOnce();
expect(knownImage.getDescription()).andReturn(null).atLeastOnce(); expect(knownImage.getDescription()).andReturn(null).atLeastOnce();
expect(knownImage.getVersion()).andReturn(null).atLeastOnce(); expect(knownImage.getVersion()).andReturn(null).atLeastOnce();
expect(knownImage.getProviderId()).andReturn("ami").atLeastOnce();
expect(knownImage.getOperatingSystem()).andReturn(os).atLeastOnce(); expect(knownImage.getOperatingSystem()).andReturn(os).atLeastOnce();
@ -152,6 +175,9 @@ public class EC2TemplateBuilderImplTest extends TemplateBuilderImplTest {
Provider<TemplateBuilder> templateBuilderProvider = createMock(Provider.class); Provider<TemplateBuilder> templateBuilderProvider = createMock(Provider.class);
TemplateOptions defaultOptions = createMock(TemplateOptions.class); TemplateOptions defaultOptions = createMock(TemplateOptions.class);
Image knownImage = createMock(Image.class); Image knownImage = createMock(Image.class);
expect(knownImage.getId()).andReturn("region/ami").anyTimes();
expect(knownImage.getProviderId()).andReturn("ami").anyTimes();
expect(knownImage.getLocation()).andReturn(location).anyTimes();
expect(optionsProvider.get()).andReturn(defaultOptions); expect(optionsProvider.get()).andReturn(defaultOptions);
@ -190,6 +216,9 @@ public class EC2TemplateBuilderImplTest extends TemplateBuilderImplTest {
Provider<TemplateBuilder> templateBuilderProvider = createMock(Provider.class); Provider<TemplateBuilder> templateBuilderProvider = createMock(Provider.class);
TemplateOptions defaultOptions = createMock(TemplateOptions.class); TemplateOptions defaultOptions = createMock(TemplateOptions.class);
Image knownImage = createMock(Image.class); Image knownImage = createMock(Image.class);
expect(knownImage.getId()).andReturn("region/ami").anyTimes();
expect(knownImage.getProviderId()).andReturn("ami").anyTimes();
expect(knownImage.getLocation()).andReturn(location).anyTimes();
expect(defaultLocation.getId()).andReturn("region"); expect(defaultLocation.getId()).andReturn("region");
expect(optionsProvider.get()).andReturn(defaultOptions); expect(optionsProvider.get()).andReturn(defaultOptions);

View File

@ -139,7 +139,7 @@ public class TemplateBuilderImplTest {
expect(image2.getLocation()).andReturn(defaultLocation).atLeastOnce(); expect(image2.getLocation()).andReturn(defaultLocation).atLeastOnce();
expect(image.getOperatingSystem()).andReturn(os).atLeastOnce(); expect(image.getOperatingSystem()).andReturn(os).atLeastOnce();
expect(image2.getOperatingSystem()).andReturn(os2).atLeastOnce(); expect(image2.getOperatingSystem()).andReturn(os2).atLeastOnce();
expect(image.getId()).andReturn("1"); expect(image.getId()).andReturn("myregion/1");
expect(os.getArch()).andReturn("X86_32").atLeastOnce(); expect(os.getArch()).andReturn("X86_32").atLeastOnce();
expect(os2.getArch()).andReturn("X86_64").atLeastOnce(); expect(os2.getArch()).andReturn("X86_64").atLeastOnce();
@ -175,7 +175,7 @@ public class TemplateBuilderImplTest {
Image image = createMock(Image.class); Image image = createMock(Image.class);
OperatingSystem os = createMock(OperatingSystem.class); OperatingSystem os = createMock(OperatingSystem.class);
Hardware hardware = new HardwareBuilder().id("hardwareId").supportsImage(ImagePredicates.idEquals("imageId")) Hardware hardware = new HardwareBuilder().id("hardwareId").supportsImage(ImagePredicates.idEquals("myregion/imageId"))
.build(); .build();
Supplier<Set<? extends Location>> locations = Suppliers.<Set<? extends Location>> ofInstance(ImmutableSet Supplier<Set<? extends Location>> locations = Suppliers.<Set<? extends Location>> ofInstance(ImmutableSet
@ -189,12 +189,15 @@ public class TemplateBuilderImplTest {
TemplateBuilder defaultTemplate = createMock(TemplateBuilder.class); TemplateBuilder defaultTemplate = createMock(TemplateBuilder.class);
expect(optionsProvider.get()).andReturn(new TemplateOptions()); expect(optionsProvider.get()).andReturn(new TemplateOptions());
expect(image.getId()).andReturn("imageId").atLeastOnce(); expect(image.getId()).andReturn("myregion/imageId").atLeastOnce();
expect(image.getLocation()).andReturn(defaultLocation).atLeastOnce(); expect(image.getLocation()).andReturn(defaultLocation).atLeastOnce();
expect(image.getName()).andReturn(null).atLeastOnce(); expect(image.getName()).andReturn(null).atLeastOnce();
expect(image.getDescription()).andReturn(null).atLeastOnce(); expect(image.getDescription()).andReturn(null).atLeastOnce();
expect(image.getVersion()).andReturn(null).atLeastOnce(); expect(image.getVersion()).andReturn(null).atLeastOnce();
expect(image.getOperatingSystem()).andReturn(os).atLeastOnce(); expect(image.getOperatingSystem()).andReturn(os).atLeastOnce();
expect(image.getProviderId()).andReturn("imageId").anyTimes();
expect(defaultLocation.getId()).andReturn("myregion").anyTimes();
expect(os.getName()).andReturn(null).atLeastOnce(); expect(os.getName()).andReturn(null).atLeastOnce();
expect(os.getVersion()).andReturn(null).atLeastOnce(); expect(os.getVersion()).andReturn(null).atLeastOnce();
@ -215,7 +218,7 @@ public class TemplateBuilderImplTest {
TemplateBuilderImpl template = createTemplateBuilder(null, locations, images, hardwares, defaultLocation, TemplateBuilderImpl template = createTemplateBuilder(null, locations, images, hardwares, defaultLocation,
optionsProvider, templateBuilderProvider); optionsProvider, templateBuilderProvider);
template.imageId("imageId").build(); template.imageId("myregion/imageId").build();
verify(image); verify(image);
verify(os); verify(os);
@ -232,7 +235,7 @@ public class TemplateBuilderImplTest {
Image image = createMock(Image.class); Image image = createMock(Image.class);
OperatingSystem os = createMock(OperatingSystem.class); OperatingSystem os = createMock(OperatingSystem.class);
Hardware hardware = new HardwareBuilder().id("hardwareId").supportsImage(ImagePredicates.idEquals("imageId")) Hardware hardware = new HardwareBuilder().id("hardwareId").supportsImage(ImagePredicates.idEquals("differentImageId"))
.build(); .build();
Supplier<Set<? extends Location>> locations = Suppliers.<Set<? extends Location>> ofInstance(ImmutableSet Supplier<Set<? extends Location>> locations = Suppliers.<Set<? extends Location>> ofInstance(ImmutableSet
@ -246,12 +249,15 @@ public class TemplateBuilderImplTest {
TemplateBuilder defaultTemplate = createMock(TemplateBuilder.class); TemplateBuilder defaultTemplate = createMock(TemplateBuilder.class);
expect(optionsProvider.get()).andReturn(new TemplateOptions()); expect(optionsProvider.get()).andReturn(new TemplateOptions());
expect(image.getId()).andReturn("notImageId").atLeastOnce(); expect(image.getId()).andReturn("myregion/imageId").atLeastOnce();
expect(image.getLocation()).andReturn(defaultLocation).atLeastOnce(); expect(image.getLocation()).andReturn(defaultLocation).atLeastOnce();
expect(image.getOperatingSystem()).andReturn(os).atLeastOnce(); expect(image.getOperatingSystem()).andReturn(os).atLeastOnce();
expect(image.getName()).andReturn(null).atLeastOnce(); expect(image.getName()).andReturn(null).atLeastOnce();
expect(image.getDescription()).andReturn(null).atLeastOnce(); expect(image.getDescription()).andReturn(null).atLeastOnce();
expect(image.getVersion()).andReturn(null).atLeastOnce(); expect(image.getVersion()).andReturn(null).atLeastOnce();
expect(image.getProviderId()).andReturn("imageId").anyTimes();
expect(defaultLocation.getId()).andReturn("myregion").anyTimes();
expect(os.getName()).andReturn(null).atLeastOnce(); expect(os.getName()).andReturn(null).atLeastOnce();
expect(os.getVersion()).andReturn(null).atLeastOnce(); expect(os.getVersion()).andReturn(null).atLeastOnce();
@ -272,13 +278,13 @@ public class TemplateBuilderImplTest {
TemplateBuilderImpl template = createTemplateBuilder(image, locations, images, hardwares, defaultLocation, TemplateBuilderImpl template = createTemplateBuilder(image, locations, images, hardwares, defaultLocation,
optionsProvider, templateBuilderProvider); optionsProvider, templateBuilderProvider);
try { try {
template.imageId("notImageId").build(); template.imageId("myregion/imageId").build();
assert false; assert false;
} catch (NoSuchElementException e) { } catch (NoSuchElementException e) {
// make sure big data is not in the exception message // make sure big data is not in the exception message
assertEquals( assertEquals(
e.getMessage(), e.getMessage(),
"no hardware profiles support images matching params: [biggest=false, fastest=false, imageName=null, imageDescription=null, imageId=notImageId, imagePredicate=null, imageVersion=null, location=EasyMock for interface org.jclouds.domain.Location, minCores=0.0, minRam=0, osFamily=null, osName=null, osDescription=null, osVersion=null, osArch=null, os64Bit=false, hardwareId=null]"); "no hardware profiles support images matching params: [biggest=false, fastest=false, imageName=null, imageDescription=null, imageId=myregion/imageId, imagePredicate=null, imageVersion=null, location=EasyMock for interface org.jclouds.domain.Location, minCores=0.0, minRam=0, osFamily=null, osName=null, osDescription=null, osVersion=null, osArch=null, os64Bit=false, hardwareId=null]");
verify(image); verify(image);
verify(os); verify(os);
verify(defaultTemplate); verify(defaultTemplate);
@ -376,7 +382,7 @@ public class TemplateBuilderImplTest {
Location defaultLocation = createMock(Location.class); Location defaultLocation = createMock(Location.class);
Image image = createMock(Image.class); Image image = createMock(Image.class);
Hardware hardware = new HardwareBuilder().id("hardwareId").supportsImage(ImagePredicates.idEquals("foo")).build(); Hardware hardware = new HardwareBuilder().id("hardwareId").supportsImage(ImagePredicates.idEquals("myregion/foo")).build();
Supplier<Set<? extends Location>> locations = Suppliers.<Set<? extends Location>> ofInstance(ImmutableSet Supplier<Set<? extends Location>> locations = Suppliers.<Set<? extends Location>> ofInstance(ImmutableSet
.<Location> of(defaultLocation)); .<Location> of(defaultLocation));
@ -392,14 +398,15 @@ public class TemplateBuilderImplTest {
TemplateOptions defaultOptions = createMock(TemplateOptions.class); TemplateOptions defaultOptions = createMock(TemplateOptions.class);
expect(optionsProvider.get()).andReturn(from).atLeastOnce(); expect(optionsProvider.get()).andReturn(from).atLeastOnce();
expect(defaultLocation.getId()).andReturn("location").atLeastOnce(); expect(defaultLocation.getId()).andReturn("myregion").atLeastOnce();
expect(image.getId()).andReturn("foo").atLeastOnce(); expect(image.getId()).andReturn("myregion/foo").atLeastOnce();
expect(image.getLocation()).andReturn(defaultLocation).atLeastOnce(); expect(image.getLocation()).andReturn(defaultLocation).atLeastOnce();
expect(image.getOperatingSystem()).andReturn(os).atLeastOnce(); expect(image.getOperatingSystem()).andReturn(os).atLeastOnce();
expect(image.getName()).andReturn(null).atLeastOnce(); expect(image.getName()).andReturn(null).atLeastOnce();
expect(image.getDescription()).andReturn(null).atLeastOnce(); expect(image.getDescription()).andReturn(null).atLeastOnce();
expect(image.getVersion()).andReturn(null).atLeastOnce(); expect(image.getVersion()).andReturn(null).atLeastOnce();
expect(image.getProviderId()).andReturn("foo").anyTimes();
expect(os.getName()).andReturn(null).atLeastOnce(); expect(os.getName()).andReturn(null).atLeastOnce();
expect(os.getVersion()).andReturn(null).atLeastOnce(); expect(os.getVersion()).andReturn(null).atLeastOnce();
@ -421,7 +428,7 @@ public class TemplateBuilderImplTest {
TemplateBuilderImpl template = createTemplateBuilder(null, locations, images, hardwares, defaultLocation, TemplateBuilderImpl template = createTemplateBuilder(null, locations, images, hardwares, defaultLocation,
optionsProvider, templateBuilderProvider); optionsProvider, templateBuilderProvider);
assertEquals(template.imageId("foo").locationId("location").build().getLocation(), defaultLocation); assertEquals(template.imageId("myregion/foo").locationId("myregion").build().getLocation(), defaultLocation);
verify(defaultOptions); verify(defaultOptions);
verify(imageLocation); verify(imageLocation);
@ -525,11 +532,11 @@ public class TemplateBuilderImplTest {
optionsProvider, templateBuilderProvider); optionsProvider, templateBuilderProvider);
try { try {
template.imageId("region/ami").build(); template.imageId("region/imageId").build();
assert false; assert false;
} catch (NoSuchElementException e) { } catch (NoSuchElementException e) {
// make sure big data is not in the exception message // make sure big data is not in the exception message
assertEquals(e.getMessage(), "imageId(region/ami) not found"); assertEquals(e.getMessage(), "imageId(region/imageId) not found");
} }
verify(defaultOptions); verify(defaultOptions);

View File

@ -0,0 +1,36 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you 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.config;
import java.util.Set;
import org.jclouds.aws.ec2.compute.suppliers.AWSEC2ImageSupplier;
import org.jclouds.compute.domain.Image;
import org.jclouds.ec2.compute.config.EC2BindComputeSuppliersByClass;
import com.google.common.base.Supplier;
/**
* @author Aled Sage
*/
public class AWSEC2BindComputeSuppliersByClass extends EC2BindComputeSuppliersByClass {
@Override
protected Class<? extends Supplier<Set<? extends Image>>> defineImageSupplier() {
return AWSEC2ImageSupplier.class;
}
}

View File

@ -24,6 +24,7 @@ import static org.jclouds.compute.domain.OsFamily.AMZN_LINUX;
import javax.inject.Named; import javax.inject.Named;
import javax.inject.Singleton; import javax.inject.Singleton;
import org.jclouds.aws.ec2.AWSEC2PropertiesBuilder;
import org.jclouds.aws.ec2.compute.AWSEC2TemplateBuilderImpl; import org.jclouds.aws.ec2.compute.AWSEC2TemplateBuilderImpl;
import org.jclouds.aws.ec2.compute.functions.AWSRunningInstanceToNodeMetadata; import org.jclouds.aws.ec2.compute.functions.AWSRunningInstanceToNodeMetadata;
import org.jclouds.aws.ec2.compute.predicates.AWSEC2InstancePresent; import org.jclouds.aws.ec2.compute.predicates.AWSEC2InstancePresent;
@ -34,13 +35,11 @@ import org.jclouds.aws.ec2.compute.strategy.AWSEC2ListNodesStrategy;
import org.jclouds.aws.ec2.compute.strategy.AWSEC2ReviseParsedImage; import org.jclouds.aws.ec2.compute.strategy.AWSEC2ReviseParsedImage;
import org.jclouds.aws.ec2.compute.strategy.CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions; import org.jclouds.aws.ec2.compute.strategy.CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions;
import org.jclouds.aws.ec2.compute.suppliers.AWSEC2HardwareSupplier; import org.jclouds.aws.ec2.compute.suppliers.AWSEC2HardwareSupplier;
import org.jclouds.aws.ec2.compute.suppliers.AWSRegionAndNameToImageSupplier;
import org.jclouds.compute.config.BaseComputeServiceContextModule; import org.jclouds.compute.config.BaseComputeServiceContextModule;
import org.jclouds.compute.domain.Image; import org.jclouds.compute.domain.Image;
import org.jclouds.compute.domain.TemplateBuilder; import org.jclouds.compute.domain.TemplateBuilder;
import org.jclouds.compute.options.TemplateOptions; import org.jclouds.compute.options.TemplateOptions;
import org.jclouds.ec2.compute.config.EC2BindComputeStrategiesByClass; import org.jclouds.ec2.compute.config.EC2BindComputeStrategiesByClass;
import org.jclouds.ec2.compute.config.EC2BindComputeSuppliersByClass;
import org.jclouds.ec2.compute.domain.RegionAndName; import org.jclouds.ec2.compute.domain.RegionAndName;
import org.jclouds.ec2.compute.functions.RunningInstanceToNodeMetadata; import org.jclouds.ec2.compute.functions.RunningInstanceToNodeMetadata;
import org.jclouds.ec2.compute.internal.EC2TemplateBuilderImpl; import org.jclouds.ec2.compute.internal.EC2TemplateBuilderImpl;
@ -53,6 +52,7 @@ import org.jclouds.ec2.compute.strategy.EC2GetNodeMetadataStrategy;
import org.jclouds.ec2.compute.strategy.EC2ListNodesStrategy; import org.jclouds.ec2.compute.strategy.EC2ListNodesStrategy;
import org.jclouds.ec2.compute.strategy.ReviseParsedImage; import org.jclouds.ec2.compute.strategy.ReviseParsedImage;
import org.jclouds.ec2.compute.suppliers.EC2HardwareSupplier; import org.jclouds.ec2.compute.suppliers.EC2HardwareSupplier;
import org.jclouds.ec2.compute.suppliers.RegionAndNameToImageSupplier;
import org.jclouds.rest.suppliers.MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier; import org.jclouds.rest.suppliers.MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier;
import com.google.common.base.Supplier; import com.google.common.base.Supplier;
@ -70,7 +70,7 @@ public class AWSEC2ComputeServiceContextModule extends BaseComputeServiceContext
super.configure(); super.configure();
installDependencies(); installDependencies();
install(new EC2BindComputeStrategiesByClass()); install(new EC2BindComputeStrategiesByClass());
install(new EC2BindComputeSuppliersByClass()); install(new AWSEC2BindComputeSuppliersByClass());
bind(ReviseParsedImage.class).to(AWSEC2ReviseParsedImage.class); bind(ReviseParsedImage.class).to(AWSEC2ReviseParsedImage.class);
bind(CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.class).to( bind(CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.class).to(
CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions.class); CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions.class);
@ -91,7 +91,7 @@ public class AWSEC2ComputeServiceContextModule extends BaseComputeServiceContext
@Provides @Provides
@Singleton @Singleton
protected Supplier<Cache<RegionAndName, ? extends Image>> provideRegionAndNameToImageSupplierCache( protected Supplier<Cache<RegionAndName, ? extends Image>> provideRegionAndNameToImageSupplierCache(
@Named(PROPERTY_SESSION_INTERVAL) long seconds, final AWSRegionAndNameToImageSupplier supplier) { @Named(PROPERTY_SESSION_INTERVAL) long seconds, final RegionAndNameToImageSupplier supplier) {
return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<Cache<RegionAndName, ? extends Image>>( return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<Cache<RegionAndName, ? extends Image>>(
authException, seconds, new Supplier<Cache<RegionAndName, ? extends Image>>() { authException, seconds, new Supplier<Cache<RegionAndName, ? extends Image>>() {
@Override @Override

View File

@ -25,6 +25,7 @@ import static org.jclouds.aws.ec2.reference.AWSEC2Constants.PROPERTY_EC2_AMI_QUE
import static org.jclouds.aws.ec2.reference.AWSEC2Constants.PROPERTY_EC2_CC_AMI_QUERY; import static org.jclouds.aws.ec2.reference.AWSEC2Constants.PROPERTY_EC2_CC_AMI_QUERY;
import static org.jclouds.aws.ec2.reference.AWSEC2Constants.PROPERTY_EC2_CC_REGIONS; import static org.jclouds.aws.ec2.reference.AWSEC2Constants.PROPERTY_EC2_CC_REGIONS;
import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future; import java.util.concurrent.Future;
@ -47,8 +48,7 @@ import com.google.common.base.Splitter;
import com.google.common.base.Supplier; import com.google.common.base.Supplier;
import com.google.common.base.Throwables; import com.google.common.base.Throwables;
import com.google.common.cache.Cache; import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder; import com.google.common.collect.ImmutableMap;
import com.google.common.cache.CacheLoader;
import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
@ -60,12 +60,14 @@ import com.google.common.util.concurrent.Futures;
* @author Adrian Cole * @author Adrian Cole
*/ */
@Singleton @Singleton
public class AWSRegionAndNameToImageSupplier implements Supplier<Cache<RegionAndName, ? extends Image>> { public class AWSEC2ImageSupplier implements Supplier<Set<? extends Image>> {
// TODO could/should this sub-class EC2ImageSupplier? Or does that confuse guice?
@Resource @Resource
@Named(ComputeServiceConstants.COMPUTE_LOGGER) @Named(ComputeServiceConstants.COMPUTE_LOGGER)
protected Logger logger = Logger.NULL; protected Logger logger = Logger.NULL;
private final CacheLoader<RegionAndName, Image> regionAndIdToImage;
private final Set<String> clusterComputeIds; private final Set<String> clusterComputeIds;
private final CallForImages.Factory factory; private final CallForImages.Factory factory;
private final ExecutorService executor; private final ExecutorService executor;
@ -74,11 +76,13 @@ public class AWSRegionAndNameToImageSupplier implements Supplier<Cache<RegionAnd
private final String amiQuery; private final String amiQuery;
private final Iterable<String> clusterRegions; private final Iterable<String> clusterRegions;
private final String ccAmiQuery; private final String ccAmiQuery;
private final Supplier<Cache<RegionAndName, ? extends Image>> cache;
@Inject @Inject
protected AWSRegionAndNameToImageSupplier(@Region Set<String> regions, protected AWSEC2ImageSupplier(@Region Set<String> regions,
@Named(PROPERTY_EC2_AMI_QUERY) String amiQuery, @Named(PROPERTY_EC2_CC_REGIONS) String clusterRegions, @Named(PROPERTY_EC2_AMI_QUERY) String amiQuery, @Named(PROPERTY_EC2_CC_REGIONS) String clusterRegions,
@Named(PROPERTY_EC2_CC_AMI_QUERY) String ccAmiQuery, CacheLoader<RegionAndName, Image> regionAndIdToImage, @Named(PROPERTY_EC2_CC_AMI_QUERY) String ccAmiQuery,
Supplier<Cache<RegionAndName, ? extends Image>> cache,
CallForImages.Factory factory, @ClusterCompute Set<String> clusterComputeIds, CallForImages.Factory factory, @ClusterCompute Set<String> clusterComputeIds,
@Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor) { @Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor) {
this.factory = factory; this.factory = factory;
@ -86,21 +90,21 @@ public class AWSRegionAndNameToImageSupplier implements Supplier<Cache<RegionAnd
this.amiQuery = amiQuery; this.amiQuery = amiQuery;
this.clusterRegions = Splitter.on(',').split(clusterRegions); this.clusterRegions = Splitter.on(',').split(clusterRegions);
this.ccAmiQuery = ccAmiQuery; this.ccAmiQuery = ccAmiQuery;
this.regionAndIdToImage = regionAndIdToImage; this.cache = cache;
this.clusterComputeIds = clusterComputeIds; this.clusterComputeIds = clusterComputeIds;
this.executor = executor; this.executor = executor;
} }
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override @Override
public Cache<RegionAndName, ? extends Image> get() { public Set<? extends Image> get() {
Future<Iterable<Image>> normalImages = images(regions, amiQuery, PROPERTY_EC2_AMI_QUERY); Future<Iterable<Image>> normalImages = images(regions, amiQuery, PROPERTY_EC2_AMI_QUERY);
ImmutableSet<Image> clusterImages; ImmutableSet<Image> clusterImages;
try { try {
clusterImages = ImmutableSet.copyOf(images(clusterRegions, ccAmiQuery, PROPERTY_EC2_CC_AMI_QUERY).get()); clusterImages = ImmutableSet.copyOf(images(clusterRegions, ccAmiQuery, PROPERTY_EC2_CC_AMI_QUERY).get());
} catch (Exception e) { } catch (Exception e) {
logger.warn(e, "Error parsing images in query %s", ccAmiQuery); logger.warn(e, "Error parsing images in query %s", ccAmiQuery);
Throwables.propagate(e); throw Throwables.propagate(e);
return null;
} }
Iterables.addAll(clusterComputeIds, transform(clusterImages, new Function<Image, String>() { Iterables.addAll(clusterComputeIds, transform(clusterImages, new Function<Image, String>() {
@ -108,27 +112,32 @@ public class AWSRegionAndNameToImageSupplier implements Supplier<Cache<RegionAnd
public String apply(Image arg0) { public String apply(Image arg0) {
return arg0.getId(); return arg0.getId();
} }
})); }));
Iterable<? extends Image> parsedImages; Iterable<? extends Image> parsedImages;
try { try {
parsedImages = ImmutableSet.copyOf(concat(clusterImages, normalImages.get())); parsedImages = ImmutableSet.copyOf(concat(clusterImages, normalImages.get()));
} catch (Exception e) { } catch (Exception e) {
logger.warn(e, "Error parsing images in query %s", amiQuery); logger.warn(e, "Error parsing images in query %s", amiQuery);
Throwables.propagate(e); throw Throwables.propagate(e);
return null;
} }
Cache<RegionAndName, Image> cache = CacheBuilder.newBuilder().build(regionAndIdToImage);
cache.asMap().putAll(uniqueIndex(parsedImages, new Function<Image, RegionAndName>() { // TODO Need to clear out old entries; previously it was a new cache object every time
// (and enclosed within the cache provider so didn't risk someone getting the cache while it was empty)!
ImmutableMap<RegionAndName, ? extends Image> imageMap = uniqueIndex(parsedImages, new Function<Image, RegionAndName>() {
@Override @Override
public RegionAndName apply(Image from) { public RegionAndName apply(Image from) {
return new RegionAndName(from.getLocation().getId(), from.getProviderId()); return new RegionAndName(from.getLocation().getId(), from.getProviderId());
} }
})); });
logger.debug("<< images(%d)", cache.asMap().size()); cache.get().invalidateAll();
return cache; cache.get().asMap().putAll((Map)imageMap);
logger.debug("<< images(%d)", imageMap.size());
// TODO Used to be mutable; was this assumed anywhere?
return ImmutableSet.copyOf(imageMap.values());
} }
private Future<Iterable<Image>> images(Iterable<String> regions, String query, String tag) { private Future<Iterable<Image>> images(Iterable<String> regions, String query, String tag) {

View File

@ -332,6 +332,16 @@ public class AWSEC2TemplateBuilderLiveTest extends BaseTemplateBuilderLiveTest {
} }
} }
@Test
public void testTemplateBuilderCanUseImageIdFromNonDefaultOwner() {
// This is the id of a public image, not owned by one of the four default owners
String imageId = "us-east-1/ami-44d02f2d";
Template defaultTemplate = context.getComputeService().templateBuilder().imageId(imageId)
.imageMatches(EC2ImagePredicates.rootDeviceType(RootDeviceType.INSTANCE_STORE)).build();
assert (defaultTemplate.getImage().getProviderId().startsWith("ami-")) : defaultTemplate;
assertEquals(defaultTemplate.getImage().getId(), imageId);
}
@Override @Override
protected Set<String> getIso3166Codes() { protected Set<String> getIso3166Codes() {
return ImmutableSet.<String> of("US-VA", "US-CA", "US-OR", "IE", "SG", "JP-13"); return ImmutableSet.<String> of("US-VA", "US-CA", "US-OR", "IE", "SG", "JP-13");