JCLOUDS-588: Register discovered images in the image cache

Images were cached in memory using a memoized supplier. To allow growing
this cache with the discovered images, the ImageCacheSupplier class has
been created. It provides an in-memory cache with all discovered images
and acts as a view over the image cache that also provides access to
them.

The in-memory cache for the discovered images expires with the session,
just as the image cache does.

The default memoized image supplier has been changed to the
ImageCacheSupplier, to make sure all providers get injected the right
instance, and the old supplier has been qualified with the 'imageCache'
name, in case a provider needs the basic image cache.
This commit is contained in:
Ignasi Barrera 2014-06-10 17:07:29 +02:00
parent ad56db2a4d
commit b0250cfb4f
12 changed files with 294 additions and 30 deletions

View File

@ -24,11 +24,11 @@ import javax.inject.Provider;
import org.jclouds.collect.Memoized;
import org.jclouds.compute.domain.Hardware;
import org.jclouds.compute.domain.Image;
import org.jclouds.compute.domain.TemplateBuilder;
import org.jclouds.compute.domain.internal.TemplateBuilderImpl;
import org.jclouds.compute.options.TemplateOptions;
import org.jclouds.compute.strategy.GetImageStrategy;
import org.jclouds.compute.suppliers.ImageCacheSupplier;
import org.jclouds.domain.Location;
import com.google.common.base.Supplier;
@ -36,7 +36,7 @@ import com.google.common.base.Supplier;
public class CloudSigmaTemplateBuilderImpl extends TemplateBuilderImpl {
@Inject
public CloudSigmaTemplateBuilderImpl(@Memoized Supplier<Set<? extends Location>> locations,
@Memoized Supplier<Set<? extends Image>> images, @Memoized Supplier<Set<? extends Hardware>> hardwares,
ImageCacheSupplier images, @Memoized Supplier<Set<? extends Hardware>> hardwares,
Supplier<Location> defaultLocation2, @Named("DEFAULT") Provider<TemplateOptions> optionsProvider,
@Named("DEFAULT") Provider<TemplateBuilder> defaultTemplateProvider, GetImageStrategy getImageStrategy) {
super(locations, images, hardwares, defaultLocation2, optionsProvider, defaultTemplateProvider, getImageStrategy);

View File

@ -33,6 +33,7 @@ import org.jclouds.compute.domain.TemplateBuilder;
import org.jclouds.compute.domain.internal.TemplateBuilderImpl;
import org.jclouds.compute.options.TemplateOptions;
import org.jclouds.compute.strategy.GetImageStrategy;
import org.jclouds.compute.suppliers.ImageCacheSupplier;
import org.jclouds.domain.Location;
import org.jclouds.ec2.compute.domain.RegionAndName;
import org.jclouds.util.Throwables2;
@ -49,7 +50,7 @@ public class EC2TemplateBuilderImpl extends TemplateBuilderImpl {
@Inject
protected EC2TemplateBuilderImpl(@Memoized Supplier<Set<? extends Location>> locations,
@Memoized Supplier<Set<? extends Image>> images, @Memoized Supplier<Set<? extends Hardware>> sizes,
ImageCacheSupplier images, @Memoized Supplier<Set<? extends Hardware>> sizes,
Supplier<Location> defaultLocation, @Named("DEFAULT") Provider<TemplateOptions> optionsProvider,
@Named("DEFAULT") Provider<TemplateBuilder> defaultTemplateProvider, GetImageStrategy getImageStrategy,
Supplier<LoadingCache<RegionAndName, ? extends Image>> imageMap) {

View File

@ -49,6 +49,7 @@ import org.jclouds.compute.domain.Template;
import org.jclouds.compute.domain.TemplateBuilder;
import org.jclouds.compute.options.TemplateOptions;
import org.jclouds.compute.strategy.GetImageStrategy;
import org.jclouds.compute.suppliers.ImageCacheSupplier;
import org.jclouds.domain.Location;
import org.jclouds.domain.LocationBuilder;
import org.jclouds.domain.LocationScope;
@ -225,7 +226,7 @@ public class EC2TemplateBuilderTest {
m1_small().build(), m1_xlarge().build(), m2_xlarge().build(), m2_2xlarge().build(),
m2_4xlarge().build(), g2_2xlarge().build(), CC1_4XLARGE));
return new EC2TemplateBuilderImpl(locations, images, sizes, Suppliers.ofInstance(location), optionsProvider,
return new EC2TemplateBuilderImpl(locations, new ImageCacheSupplier(images, 60), sizes, Suppliers.ofInstance(location), optionsProvider,
templateBuilderProvider, getImageStrategy, imageCache) {
};
}

View File

@ -38,6 +38,7 @@ import org.jclouds.compute.domain.internal.TemplateBuilderImpl;
import org.jclouds.compute.domain.internal.TemplateBuilderImplTest;
import org.jclouds.compute.options.TemplateOptions;
import org.jclouds.compute.strategy.GetImageStrategy;
import org.jclouds.compute.suppliers.ImageCacheSupplier;
import org.jclouds.domain.Location;
import org.jclouds.ec2.compute.domain.RegionAndName;
import org.jclouds.ec2.compute.functions.ImagesToRegionAndIdMap;
@ -84,7 +85,7 @@ public class EC2TemplateBuilderImplTest extends TemplateBuilderImplTest {
ImagesToRegionAndIdMap.imagesToMap(images.get()))));
}
return new EC2TemplateBuilderImpl(locations, images, sizes, Suppliers.ofInstance(defaultLocation),
return new EC2TemplateBuilderImpl(locations, new ImageCacheSupplier(images, 60), sizes, Suppliers.ofInstance(defaultLocation),
optionsProvider, templateBuilderProvider, getImageStrategy, Suppliers.<LoadingCache<RegionAndName, ? extends Image>>ofInstance(imageMap));
}
@ -220,4 +221,10 @@ public class EC2TemplateBuilderImplTest extends TemplateBuilderImplTest {
}
// The EC2 provider already overrides the getImage method so this test is not useful for EC2
@Override
public void testFindImageWithIdDefaultToGetImageStrategyAndPopulatesTheCache() {
}
}

View File

@ -24,11 +24,11 @@ import javax.inject.Provider;
import org.jclouds.collect.Memoized;
import org.jclouds.compute.domain.Hardware;
import org.jclouds.compute.domain.Image;
import org.jclouds.compute.domain.TemplateBuilder;
import org.jclouds.compute.domain.internal.TemplateBuilderImpl;
import org.jclouds.compute.options.TemplateOptions;
import org.jclouds.compute.strategy.GetImageStrategy;
import org.jclouds.compute.suppliers.ImageCacheSupplier;
import org.jclouds.domain.Location;
import com.google.common.base.Supplier;
@ -37,7 +37,7 @@ public class VCloudTemplateBuilderImpl extends TemplateBuilderImpl {
@Inject
protected VCloudTemplateBuilderImpl(@Memoized Supplier<Set<? extends Location>> locations,
@Memoized Supplier<Set<? extends Image>> images, @Memoized Supplier<Set<? extends Hardware>> sizes,
ImageCacheSupplier images, @Memoized Supplier<Set<? extends Hardware>> sizes,
Supplier<Location> defaultLocation, @Named("DEFAULT") Provider<TemplateOptions> optionsProvider,
@Named("DEFAULT") Provider<TemplateBuilder> defaultTemplateProvider, GetImageStrategy getImageStrategy) {
super(locations, images, sizes, defaultLocation, optionsProvider, defaultTemplateProvider, getImageStrategy);

View File

@ -53,6 +53,7 @@ import org.jclouds.compute.options.TemplateOptions;
import org.jclouds.compute.reference.ComputeServiceConstants;
import org.jclouds.compute.strategy.CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap;
import org.jclouds.compute.strategy.InitializeRunScriptOnNodeOrPlaceInBadMap;
import org.jclouds.compute.suppliers.ImageCacheSupplier;
import org.jclouds.config.ValueOfConfigurationKeyOrNull;
import org.jclouds.domain.LoginCredentials;
import org.jclouds.json.Json;
@ -111,6 +112,9 @@ public abstract class BaseComputeServiceContextModule extends AbstractModule {
}, InitializeRunScriptOnNodeOrPlaceInBadMap.class).build(InitializeRunScriptOnNodeOrPlaceInBadMap.Factory.class));
install(new FactoryModuleBuilder().build(BlockUntilInitScriptStatusIsZeroThenReturnOutput.Factory.class));
bind(new TypeLiteral<Supplier<Set<? extends Image>>>() {
}).annotatedWith(Memoized.class).to(ImageCacheSupplier.class);
}
protected void bindCredentialsOverriderFunction() {
@ -229,7 +233,7 @@ public abstract class BaseComputeServiceContextModule extends AbstractModule {
@Provides
@Singleton
@Memoized
@Named("imageCache")
protected Supplier<Set<? extends Image>> supplyImageCache(AtomicReference<AuthorizationException> authException, @Named(PROPERTY_SESSION_INTERVAL) long seconds,
final Supplier<Set<? extends Image>> imageSupplier, Injector injector) {
if (shouldEagerlyParseImages(injector)) {

View File

@ -54,6 +54,7 @@ import org.jclouds.compute.domain.TemplateBuilderSpec;
import org.jclouds.compute.options.TemplateOptions;
import org.jclouds.compute.reference.ComputeServiceConstants;
import org.jclouds.compute.strategy.GetImageStrategy;
import org.jclouds.compute.suppliers.ImageCacheSupplier;
import org.jclouds.domain.Location;
import org.jclouds.logging.Logger;
@ -77,7 +78,7 @@ public class TemplateBuilderImpl implements TemplateBuilder {
@Named(ComputeServiceConstants.COMPUTE_LOGGER)
protected Logger logger = Logger.NULL;
protected final Supplier<Set<? extends Image>> images;
protected final ImageCacheSupplier images;
protected final Supplier<Set<? extends Hardware>> hardwares;
protected final Supplier<Set<? extends Location>> locations;
protected final Supplier<Location> defaultLocation;
@ -130,13 +131,13 @@ public class TemplateBuilderImpl implements TemplateBuilder {
@Inject
protected TemplateBuilderImpl(@Memoized Supplier<Set<? extends Location>> locations,
@Memoized Supplier<Set<? extends Image>> images, @Memoized Supplier<Set<? extends Hardware>> hardwares,
Supplier<Location> defaultLocation2, @Named("DEFAULT") Provider<TemplateOptions> optionsProvider,
ImageCacheSupplier images, @Memoized Supplier<Set<? extends Hardware>> hardwares,
Supplier<Location> defaultLocation, @Named("DEFAULT") Provider<TemplateOptions> optionsProvider,
@Named("DEFAULT") Provider<TemplateBuilder> defaultTemplateProvider, GetImageStrategy getImageStrategy) {
this.locations = checkNotNull(locations, "locations");
this.images = checkNotNull(images, "locations");
this.images = checkNotNull(images, "images");
this.hardwares = checkNotNull(hardwares, "hardwares");
this.defaultLocation = checkNotNull(defaultLocation2, "defaultLocation2");
this.defaultLocation = checkNotNull(defaultLocation, "defaultLocation");
this.optionsProvider = checkNotNull(optionsProvider, "optionsProvider");
this.defaultTemplateProvider = checkNotNull(defaultTemplateProvider, "defaultTemplateProvider");
this.getImageStrategy = checkNotNull(getImageStrategy, "getImageStrategy");
@ -727,21 +728,26 @@ public class TemplateBuilderImpl implements TemplateBuilder {
}
private Image findImageWithId(Set<? extends Image> images) {
// Try find the image in the cache and fallback to the GetImageStrategy
// Try to find the image in the cache and fallback to the GetImageStrategy
// see https://issues.apache.org/jira/browse/JCLOUDS-570
Optional<? extends Image> image = tryFind(images, idPredicate);
if (!image.isPresent()) {
logger.warn("Image %s not found in the image cache. Trying to get it directly...", imageId);
// Note that this might generate make a call to the provider instead of using a cache, but
if (image.isPresent()) {
return image.get();
}
logger.info("Image %s not found in the image cache. Trying to get it from the provider...", imageId);
// Note that this will generate make a call to the provider instead of using a cache, but
// this will be executed rarely, only when an image is not present in the image list but
// it actually exists in the provider. It shouldn't be an expensive call so using a cache just for
// this corner case is overkill.
image = Optional.fromNullable(getImageStrategy.getImage(imageId));
if (!image.isPresent()) {
Image imageFromProvider = getImageStrategy.getImage(imageId);
if (imageFromProvider == null) {
throwNoSuchElementExceptionAfterLoggingImageIds(format("%s not found", idPredicate), images);
}
}
return image.get();
// Register the just found image in the image cache, so subsequent uses of the TemplateBuilder and
// the ComptueService find it.
this.images.registerImage(imageFromProvider);
return imageFromProvider;
}
private Hardware findHardwareWithId(Set<? extends Hardware> hardwaresToSearch) {

View File

@ -34,6 +34,7 @@ import javax.inject.Singleton;
import org.jclouds.compute.ComputeServiceAdapter;
import org.jclouds.compute.ComputeServiceAdapter.NodeAndInitialCredentials;
import org.jclouds.compute.config.ComputeServiceAdapterContextModule.AddDefaultCredentialsToImage;
import org.jclouds.compute.domain.ComputeMetadata;
import org.jclouds.compute.domain.Image;
import org.jclouds.compute.domain.NodeMetadata;
@ -74,12 +75,13 @@ public class AdaptingComputeServiceStrategies<N, H, I, L> implements CreateNodeW
private final ComputeServiceAdapter<N, H, I, L> client;
private final Function<N, NodeMetadata> nodeMetadataAdapter;
private final Function<I, Image> imageAdapter;
private final AddDefaultCredentialsToImage addDefaultCredentialsToImage;
@Inject
public AdaptingComputeServiceStrategies(Map<String, Credentials> credentialStore,
PrioritizeCredentialsFromTemplate prioritizeCredentialsFromTemplate,
ComputeServiceAdapter<N, H, I, L> client, Function<N, NodeMetadata> nodeMetadataAdapter,
Function<I, Image> imageAdapter) {
Function<I, Image> imageAdapter, AddDefaultCredentialsToImage addDefaultCredentialsToImage) {
this.credentialStore = checkNotNull(credentialStore, "credentialStore");
this.prioritizeCredentialsFromTemplate = checkNotNull(prioritizeCredentialsFromTemplate,
"prioritizeCredentialsFromTemplate");
@ -87,6 +89,7 @@ public class AdaptingComputeServiceStrategies<N, H, I, L> implements CreateNodeW
this.nodeMetadataAdapter = Functions.compose(addLoginCredentials, checkNotNull(nodeMetadataAdapter,
"nodeMetadataAdapter"));
this.imageAdapter = checkNotNull(imageAdapter, "imageAdapter");
this.addDefaultCredentialsToImage = checkNotNull(addDefaultCredentialsToImage, "addDefaultCredentialsToImage");
}
private final Function<NodeMetadata, NodeMetadata> addLoginCredentials = new Function<NodeMetadata, NodeMetadata>() {
@ -124,7 +127,9 @@ public class AdaptingComputeServiceStrategies<N, H, I, L> implements CreateNodeW
I image = client.getImage(checkNotNull(id, "id"));
if (image == null)
return null;
return imageAdapter.apply(image);
// The image supplier configured in the ComputeServiceAdapterContextModule also adds the default credentials to
// each image in the image list. When getting a single image, the behavior must be the same.
return addDefaultCredentialsToImage.apply(imageAdapter.apply(image));
}
@Override

View File

@ -0,0 +1,84 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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.compute.suppliers;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.Iterables.concat;
import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import javax.inject.Named;
import javax.inject.Singleton;
import org.jclouds.compute.domain.Image;
import com.google.common.base.Supplier;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.ImmutableSet;
import com.google.inject.Inject;
/**
* Image supplier that allows new images to be registered.
* <p>
* This is a wrapper for the memoized image supplier (the actual image cache), to provide a way to register new images as
* needed. Once a new image is created by the {@link org.jclouds.compute.extensions.ImageExtension}, or discovered by
* other means (see https://issues.apache.org/jira/browse/JCLOUDS-570) this supplier will allow the image to be appended
* to the cached list, so it can be properly used normally.
*/
@Singleton
public class ImageCacheSupplier implements Supplier<Set<? extends Image>> {
private final Supplier<Set<? extends Image>> imageCache;
private final Cache<String, Image> uncachedImages;
@Inject
public ImageCacheSupplier(@Named("imageCache") Supplier<Set<? extends Image>> imageCache,
@Named(PROPERTY_SESSION_INTERVAL) long sessionIntervalSeconds) {
this.imageCache = checkNotNull(imageCache, "imageCache");
// We use a cache to let the entries in the "uncached" set expire as soon as the image cache expires. We want the
// uncached set to be regenerated when the original cache is also regenerated.
this.uncachedImages = CacheBuilder.newBuilder().expireAfterWrite(sessionIntervalSeconds, TimeUnit.SECONDS)
.build();
}
@Override
public Set<? extends Image> get() {
return ImmutableSet.copyOf(concat(imageCache.get(), uncachedImages.asMap().values()));
}
/**
* Registers a new image in the image cache.
* <p>
* This method should be called to register new images into the image cache, when some image that is known to exist
* in the provider is still not cached. For example, this can happen when an image is created after the image cache
* has been populated for the first time.
* <p>
* Note that this method does not check if the image is already cached, to avoid loading all images if the image
* cache is still not populated.
*
* @param image The image to be registered to the cache.
*/
public void registerImage(Image image) {
checkNotNull(image, "image");
uncachedImages.put(image.getId(), image);
}
}

View File

@ -22,6 +22,7 @@ import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.verify;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;
@ -45,6 +46,7 @@ import org.jclouds.compute.domain.Volume;
import org.jclouds.compute.options.TemplateOptions;
import org.jclouds.compute.predicates.ImagePredicates;
import org.jclouds.compute.strategy.GetImageStrategy;
import org.jclouds.compute.suppliers.ImageCacheSupplier;
import org.jclouds.domain.Location;
import org.jclouds.domain.LocationBuilder;
import org.jclouds.domain.LocationScope;
@ -466,7 +468,7 @@ public class TemplateBuilderImplTest {
Supplier<Set<? extends Image>> images, Supplier<Set<? extends Hardware>> hardwares,
Location defaultLocation, Provider<TemplateOptions> optionsProvider,
Provider<TemplateBuilder> templateBuilderProvider, GetImageStrategy getImageStrategy) {
TemplateBuilderImpl template = new TemplateBuilderImpl(locations, images, hardwares, Suppliers
TemplateBuilderImpl template = new TemplateBuilderImpl(locations, new ImageCacheSupplier(images, 60), hardwares, Suppliers
.ofInstance(defaultLocation), optionsProvider, templateBuilderProvider, getImageStrategy);
return template;
}
@ -824,6 +826,82 @@ public class TemplateBuilderImplTest {
verify(getImageStrategy);
}
@Test
public void testFindImageWithIdDefaultToGetImageStrategyAndPopulatesTheCache() {
final Supplier<Set<? extends Location>> locations = Suppliers.<Set<? extends Location>> ofInstance(ImmutableSet
.<Location> of(region));
final Supplier<Set<? extends Image>> images = Suppliers.<Set<? extends Image>> ofInstance(ImmutableSet
.<Image> of(
new ImageBuilder()
.ids("Ubuntu 11.04 x64")
.name("Ubuntu 11.04 x64")
.description("Ubuntu 11.04 x64")
.location(region)
.status(Status.AVAILABLE)
.operatingSystem(
OperatingSystem.builder().name("Ubuntu 11.04 x64").description("Ubuntu 11.04 x64")
.is64Bit(true).version("11.04").family(OsFamily.UBUNTU).build()).build(),
new ImageBuilder()
.ids("Ubuntu 11.04 64-bit")
.name("Ubuntu 11.04 64-bit")
.description("Ubuntu 11.04 64-bit")
.location(region)
.status(Status.AVAILABLE)
.operatingSystem(
OperatingSystem.builder().name("Ubuntu 11.04 64-bit").description("Ubuntu 11.04 64-bit")
.is64Bit(true).version("11.04").family(OsFamily.UBUNTU).build()).build()));
final Supplier<Set<? extends Hardware>> hardwares = Suppliers.<Set<? extends Hardware>> ofInstance(ImmutableSet
.<Hardware> of(
new HardwareBuilder()
.ids(String.format("datacenter(%s)platform(%s)cpuCores(%d)memorySizeMB(%d)diskSizeGB(%d)",
"Falkenberg", "Xen", 1, 512, 5)).ram(512)
.processors(ImmutableList.of(new Processor(1, 1.0)))
.volumes(ImmutableList.<Volume> of(new VolumeImpl((float) 5, true, true))).hypervisor("Xen")
.location(region)
.supportsImage(ImagePredicates.idEquals(image.getId())).build()));
final Provider<TemplateOptions> optionsProvider = new Provider<TemplateOptions>() {
@Override
public TemplateOptions get() {
return new TemplateOptions();
}
};
final GetImageStrategy getImageStrategy = createMock(GetImageStrategy.class);
expect(getImageStrategy.getImage(image.getId())).andReturn(image);
replay(getImageStrategy);
Provider<TemplateBuilder> templateBuilderProvider = new Provider<TemplateBuilder>() {
@Override
public TemplateBuilder get() {
return createTemplateBuilder(null, locations, images, hardwares, region, optionsProvider, this, getImageStrategy);
}
};
TemplateBuilder templateBuilder = templateBuilderProvider.get();
try {
// First call searching for the image properties will fail, as the image is not in the cache
templateBuilder.osNameMatches(image.getOperatingSystem().getName()).build();
fail("Image should not exist in the cache");
} catch (Exception ex) {
// Expected path
}
// A second call using the imageId will fallback to the GetImageStrategy and populate the image in the cache.
assertNotNull(templateBuilder.imageId(image.getId()).build());
// The third call will succeed, as the previous one should have populated the image in the cache.
templateBuilder.imageId(null); // Clear all criteria
Template template = templateBuilder.osNameMatches(image.getOperatingSystem().getName()).build();
assertEquals(template.getImage().getId(), image.getId());
// Verify this is called only once, as the third call will already find the image in the cache
verify(getImageStrategy);
}
@SuppressWarnings("unchecked")
@Test
public void testHardwareIdNullsHypervisor() {

View File

@ -0,0 +1,77 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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.compute.suppliers;
import static org.testng.Assert.assertEquals;
import java.util.Set;
import org.jclouds.compute.domain.Image;
import org.jclouds.compute.domain.ImageBuilder;
import org.jclouds.compute.domain.OperatingSystem;
import org.jclouds.domain.Location;
import org.jclouds.domain.LocationBuilder;
import org.jclouds.domain.LocationScope;
import org.testng.annotations.Test;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableSet;
/**
* Unit tests for the {@link ImageCacheSupplier} class.
*/
@Test(groups = "unit", testName = "ImageCacheSupplierTest")
public class ImageCacheSupplierTest {
private Location location = new LocationBuilder().scope(LocationScope.PROVIDER).id("location")
.description("location").build();
private OperatingSystem os = OperatingSystem.builder().name("osName").version("osVersion")
.description("osDescription").arch("X86_32").build();
private Image image = new ImageBuilder().id("imageId").providerId("imageId").name("imageName")
.description("imageDescription").version("imageVersion").operatingSystem(os).status(Image.Status.AVAILABLE)
.location(location).build();
private Set<? extends Image> images = ImmutableSet.of(image);
@Test(expectedExceptions = NullPointerException.class)
public void testRegisterNullImageIsNotAllowed() {
ImageCacheSupplier imageCache = new ImageCacheSupplier(Suppliers.<Set<? extends Image>> ofInstance(images), 60);
imageCache.registerImage(null);
}
@Test
public void testRegisterImageIgnoresDuplicates() {
ImageCacheSupplier imageCache = new ImageCacheSupplier(Suppliers.<Set<? extends Image>> ofInstance(images), 60);
assertEquals(imageCache.get().size(), 1);
imageCache.registerImage(image);
assertEquals(imageCache.get().size(), 1);
}
@Test
public void testRegisterNewImage() {
ImageCacheSupplier imageCache = new ImageCacheSupplier(Suppliers.<Set<? extends Image>> ofInstance(images), 60);
assertEquals(imageCache.get().size(), 1);
imageCache.registerImage(ImageBuilder.fromImage(image).id("newimage").build());
assertEquals(imageCache.get().size(), 2);
}
}

View File

@ -28,6 +28,7 @@ import org.jclouds.compute.domain.Image;
import org.jclouds.compute.domain.TemplateBuilder;
import org.jclouds.compute.options.TemplateOptions;
import org.jclouds.compute.strategy.GetImageStrategy;
import org.jclouds.compute.suppliers.ImageCacheSupplier;
import org.jclouds.domain.Location;
import org.jclouds.ec2.compute.domain.RegionAndName;
import org.jclouds.ec2.compute.internal.EC2TemplateBuilderImpl;
@ -39,7 +40,7 @@ public class AWSEC2TemplateBuilderImpl extends EC2TemplateBuilderImpl {
@Inject
protected AWSEC2TemplateBuilderImpl(@Memoized Supplier<Set<? extends Location>> locations,
@Memoized Supplier<Set<? extends Image>> images, @Memoized Supplier<Set<? extends Hardware>> sizes,
ImageCacheSupplier images, @Memoized Supplier<Set<? extends Hardware>> sizes,
Supplier<Location> defaultLocation, @Named("DEFAULT") Provider<TemplateOptions> optionsProvider,
@Named("DEFAULT") Provider<TemplateBuilder> defaultTemplateProvider, GetImageStrategy getImageStrategy,
Supplier<LoadingCache<RegionAndName, ? extends Image>> imageMap) {