JCLOUDS-512: Implement the ImageCache

This commit refactors the ImageCacheSupplier to act as a
proper cache. It is used by the ImageExtesion and all operations
on the images are propagated to the cache.

A method has also been added to the TemplateBuilder to let users
force a cache refresh. There have been several requests to provide a way
to disable image caching in the compute abstraction, and this new method
should fix that.
This commit is contained in:
Ignasi Barrera 2015-10-22 09:38:14 +02:00
parent 0f6ab3944f
commit 40f31786c5
26 changed files with 677 additions and 268 deletions

View File

@ -89,7 +89,6 @@ import org.jclouds.rest.suppliers.MemoizedRetryOnTimeOutButNotOnAuthorizationExc
import com.google.common.base.Function;
import com.google.common.base.Objects;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.base.Supplier;
import com.google.common.cache.CacheBuilder;
@ -277,14 +276,4 @@ public class CloudStackComputeServiceContextModule extends
NetworkType.ADVANCED, new AdvancedNetworkOptionsConverter(),
NetworkType.BASIC, new BasicNetworkOptionsConverter());
}
@Override
protected Optional<ImageExtension> provideImageExtension(Injector i) {
return Optional.of(i.getInstance(ImageExtension.class));
}
@Override
protected Optional<SecurityGroupExtension> provideSecurityGroupExtension(Injector i) {
return Optional.of(i.getInstance(SecurityGroupExtension.class));
}
}

View File

@ -17,7 +17,6 @@
package org.jclouds.ec2.compute.config;
import static com.google.common.collect.Iterables.toArray;
import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL;
import static org.jclouds.ec2.reference.EC2Constants.PROPERTY_EC2_AMI_OWNERS;
import java.util.Set;
@ -30,8 +29,6 @@ import javax.inject.Singleton;
import org.jclouds.compute.ComputeServiceContext;
import org.jclouds.compute.config.BaseComputeServiceContextModule;
import org.jclouds.compute.domain.Image;
import org.jclouds.compute.extensions.ImageExtension;
import org.jclouds.compute.extensions.SecurityGroupExtension;
import org.jclouds.ec2.compute.EC2ComputeService;
import org.jclouds.ec2.compute.domain.RegionAndName;
import org.jclouds.ec2.compute.loaders.RegionAndIdToImage;
@ -39,7 +36,6 @@ import org.jclouds.ec2.compute.suppliers.RegionAndNameToImageSupplier;
import org.jclouds.rest.AuthorizationException;
import org.jclouds.rest.suppliers.SetAndThrowAuthorizationExceptionSupplier;
import com.google.common.base.Optional;
import com.google.common.base.Splitter;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
@ -78,9 +74,8 @@ public class EC2ComputeServiceContextModule extends BaseComputeServiceContextMod
}
@Override
protected Supplier<Set<? extends Image>> supplyNonParsingImageCache(
AtomicReference<AuthorizationException> authException, @Named(PROPERTY_SESSION_INTERVAL) long seconds,
final Supplier<Set<? extends Image>> imageSupplier, Injector injector) {
protected Supplier<Set<? extends Image>> supplyNonParsingImages(final Supplier<Set<? extends Image>> imageSupplier,
Injector injector) {
final Supplier<LoadingCache<RegionAndName, ? extends Image>> cache = injector.getInstance(Key.get(new TypeLiteral<Supplier<LoadingCache<RegionAndName, ? extends Image>>>() {}));
return new Supplier<Set<? extends Image>>() {
@Override
@ -135,15 +130,5 @@ public class EC2ComputeServiceContextModule extends BaseComputeServiceContextMod
return new String[] {};
return toArray(Splitter.on(',').split(amiOwners), String.class);
}
@Override
protected Optional<ImageExtension> provideImageExtension(Injector i) {
return Optional.of(i.getInstance(ImageExtension.class));
}
@Override
protected Optional<SecurityGroupExtension> provideSecurityGroupExtension(Injector i) {
return Optional.of(i.getInstance(SecurityGroupExtension.class));
}
}

View File

@ -32,8 +32,6 @@ 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 org.jclouds.ec2.compute.domain.RegionAndName;
import org.jclouds.util.Throwables2;
@ -50,11 +48,11 @@ public class EC2TemplateBuilderImpl extends TemplateBuilderImpl {
@Inject
protected EC2TemplateBuilderImpl(@Memoized Supplier<Set<? extends Location>> locations,
ImageCacheSupplier images, @Memoized Supplier<Set<? extends Hardware>> sizes,
@Memoized Supplier<Set<? extends Image>> images, @Memoized Supplier<Set<? extends Hardware>> sizes,
Supplier<Location> defaultLocation, @Named("DEFAULT") Provider<TemplateOptions> optionsProvider,
@Named("DEFAULT") Provider<TemplateBuilder> defaultTemplateProvider, GetImageStrategy getImageStrategy,
@Named("DEFAULT") Provider<TemplateBuilder> defaultTemplateProvider,
Supplier<LoadingCache<RegionAndName, ? extends Image>> imageMap) {
super(locations, images, sizes, defaultLocation, optionsProvider, defaultTemplateProvider, getImageStrategy);
super(locations, images, sizes, defaultLocation, optionsProvider, defaultTemplateProvider);
this.lazyImageCache = imageMap;
}

View File

@ -58,6 +58,7 @@ import org.jclouds.ec2.compute.domain.RegionAndName;
import org.jclouds.ec2.compute.functions.ImagesToRegionAndIdMap;
import org.jclouds.ec2.compute.internal.EC2TemplateBuilderImpl;
import org.jclouds.ec2.domain.VirtualizationType;
import org.jclouds.rest.AuthorizationException;
import org.testng.annotations.Test;
import com.google.common.base.Function;
@ -69,6 +70,8 @@ import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.util.concurrent.Atomics;
import com.google.inject.util.Providers;
/**
* Tests compute service specifically to EC2.
@ -236,8 +239,9 @@ public class EC2TemplateBuilderTest {
m1_small().build(), m1_xlarge().build(), m2_xlarge().build(), m2_2xlarge().build(),
m2_4xlarge().build(), g2_2xlarge().build(), HARDWARE_SUPPORTING_BOGUS));
return new EC2TemplateBuilderImpl(locations, new ImageCacheSupplier(images, 60), sizes, Suppliers.ofInstance(location), optionsProvider,
templateBuilderProvider, getImageStrategy, imageCache) {
return new EC2TemplateBuilderImpl(locations, new ImageCacheSupplier(images, 60,
Atomics.<AuthorizationException> newReference(), Providers.of(getImageStrategy)), sizes,
Suppliers.ofInstance(location), optionsProvider, templateBuilderProvider, imageCache) {
};
}

View File

@ -17,16 +17,31 @@
package org.jclouds.ec2.compute.extensions;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.Iterables.filter;
import static com.google.common.collect.Iterables.getOnlyElement;
import java.lang.reflect.Field;
import java.util.Properties;
import java.util.Set;
import org.jclouds.collect.Memoized;
import org.jclouds.compute.domain.Image;
import org.jclouds.compute.domain.Template;
import org.jclouds.compute.domain.TemplateBuilderSpec;
import org.jclouds.compute.extensions.ImageExtension;
import org.jclouds.compute.extensions.internal.BaseImageExtensionLiveTest;
import org.jclouds.compute.suppliers.ImageCacheSupplier;
import org.jclouds.sshj.config.SshjSshClientModule;
import org.testng.annotations.Test;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.base.Supplier;
import com.google.common.base.Throwables;
import com.google.common.cache.LoadingCache;
import com.google.inject.Key;
import com.google.inject.Module;
import com.google.inject.TypeLiteral;
/**
* Live test for ec2 {@link ImageExtension} implementation
@ -43,11 +58,42 @@ public class EC2ImageExtensionLiveTest extends BaseImageExtensionLiveTest {
protected Properties setupProperties() {
Properties overrides = super.setupProperties();
String ebsSpec = checkNotNull(setIfTestSystemPropertyPresent(overrides, provider + ".ebs-template"), provider
+ ".ebs-template");
+ ".ebs-template");
ebsTemplate = TemplateBuilderSpec.parse(ebsSpec);
return overrides;
}
// Getting an image from the cache is tricky in EC2, as images are filtered
// by default by owner, and the image being created will be owned by the
// current user. If the cache needs to refresh the list, the just added image
// will be removed as the image list will be refreshed (potentially) without
// taking into account the current user owner id. Instead of using the
// TempalteBuilder, just inspect the ImageCacheSupplier directly
@SuppressWarnings("unchecked")
@Override
protected Optional<Image> findImageWithNameInCache(String name) {
ImageCacheSupplier imageCache = (ImageCacheSupplier) context.utils().injector()
.getInstance(Key.get(new TypeLiteral<Supplier<Set<? extends Image>>>() {
}, Memoized.class));
try {
Field field = imageCache.getClass().getDeclaredField("imageCache");
field.setAccessible(true);
LoadingCache<String, Image> cache = (LoadingCache<String, Image>) field.get(imageCache);
return Optional.fromNullable(getOnlyElement(filter(cache.asMap().values(), new Predicate<Image>() {
@Override
public boolean apply(Image input) {
return imageGroup.equals(input.getName());
}
}), null));
} catch (NoSuchFieldException ex) {
throw Throwables.propagate(ex);
} catch (IllegalAccessException ex) {
throw Throwables.propagate(ex);
}
}
@Override
public Template getNodeTemplate() {
return view.getComputeService().templateBuilder().from(ebsTemplate).build();

View File

@ -44,6 +44,7 @@ import org.jclouds.domain.Location;
import org.jclouds.ec2.compute.domain.RegionAndName;
import org.jclouds.ec2.compute.functions.ImagesToRegionAndIdMap;
import org.jclouds.ec2.compute.options.EC2TemplateOptions;
import org.jclouds.rest.AuthorizationException;
import org.testng.annotations.Test;
import com.google.common.base.Functions;
@ -54,6 +55,8 @@ import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.google.common.util.concurrent.Atomics;
import com.google.inject.util.Providers;
@Test(groups = "unit", singleThreaded = true)
public class EC2TemplateBuilderImplTest extends TemplateBuilderImplTest {
@ -86,8 +89,10 @@ public class EC2TemplateBuilderImplTest extends TemplateBuilderImplTest {
ImagesToRegionAndIdMap.imagesToMap(images.get()))));
}
return new EC2TemplateBuilderImpl(locations, new ImageCacheSupplier(images, 60), sizes, Suppliers.ofInstance(defaultLocation),
optionsProvider, templateBuilderProvider, getImageStrategy, Suppliers.<LoadingCache<RegionAndName, ? extends Image>>ofInstance(imageMap));
return new EC2TemplateBuilderImpl(locations, new ImageCacheSupplier(images, 60,
Atomics.<AuthorizationException> newReference(), Providers.of(getImageStrategy)), sizes,
Suppliers.ofInstance(defaultLocation), optionsProvider, templateBuilderProvider,
Suppliers.<LoadingCache<RegionAndName, ? extends Image>> ofInstance(imageMap));
}
@Override

View File

@ -80,7 +80,6 @@ import org.jclouds.openstack.nova.v2_0.predicates.FindSecurityGroupWithNameAndRe
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
@ -280,14 +279,4 @@ public class NovaComputeServiceContextModule extends
protected final Map<org.jclouds.openstack.nova.v2_0.domain.Image.Status, Image.Status> toPortableImageStatus() {
return toPortableImageStatus;
}
@Override
protected Optional<ImageExtension> provideImageExtension(Injector i) {
return Optional.of(i.getInstance(ImageExtension.class));
}
@Override
protected Optional<SecurityGroupExtension> provideSecurityGroupExtension(Injector i) {
return Optional.of(i.getInstance(SecurityGroupExtension.class));
}
}

View File

@ -52,6 +52,7 @@ import org.jclouds.compute.options.RunScriptOptions;
import org.jclouds.compute.options.TemplateOptions;
import org.jclouds.compute.reference.ComputeServiceConstants;
import org.jclouds.compute.strategy.CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap;
import org.jclouds.compute.strategy.GetImageStrategy;
import org.jclouds.compute.strategy.InitializeRunScriptOnNodeOrPlaceInBadMap;
import org.jclouds.compute.suppliers.ImageCacheSupplier;
import org.jclouds.config.ValueOfConfigurationKeyOrNull;
@ -71,8 +72,10 @@ import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.google.inject.AbstractModule;
import com.google.inject.Binding;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Provides;
import com.google.inject.TypeLiteral;
import com.google.inject.assistedinject.FactoryModuleBuilder;
@ -112,9 +115,6 @@ 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() {
@ -238,35 +238,27 @@ public abstract class BaseComputeServiceContextModule extends AbstractModule {
}, images);
}
@Provides
@Singleton
@Named("imageCache")
protected final 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)) {
return supplyImageCache(authException, seconds, imageSupplier);
} else {
return supplyNonParsingImageCache(authException, seconds, imageSupplier, injector);
}
@Memoized
protected final Supplier<Set<? extends Image>> supplyImageCache(
AtomicReference<AuthorizationException> authException, @Named(PROPERTY_SESSION_INTERVAL) long seconds,
final Supplier<Set<? extends Image>> imageSupplier, com.google.inject.Provider<GetImageStrategy> imageLoader, Injector injector) {
Supplier<Set<? extends Image>> parsingImageSupplier = shouldEagerlyParseImages(injector) ? imageSupplier
: supplyNonParsingImages(imageSupplier, injector);
return new ImageCacheSupplier(parsingImageSupplier, seconds, authException, imageLoader);
}
protected boolean shouldEagerlyParseImages(Injector injector) {
return true;
}
protected Supplier<Set<? extends Image>> supplyImageCache(AtomicReference<AuthorizationException> authException, @Named(PROPERTY_SESSION_INTERVAL) long seconds,
final Supplier<Set<? extends Image>> imageSupplier) {
return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException, imageSupplier, seconds,
TimeUnit.SECONDS);
}
/**
* For overriding; default impl is same as {@link supplyImageCache(seconds, imageSupplier)}
* For overriding; default impl just returns the image supplier.
*/
protected Supplier<Set<? extends Image>> supplyNonParsingImageCache(AtomicReference<AuthorizationException> authException, @Named(PROPERTY_SESSION_INTERVAL) long seconds,
final Supplier<Set<? extends Image>> imageSupplier, Injector injector) {
return supplyImageCache(authException, seconds, imageSupplier);
protected Supplier<Set<? extends Image>> supplyNonParsingImages(final Supplier<Set<? extends Image>> imageSupplier, Injector injector) {
return imageSupplier;
}
@Provides
@ -312,21 +304,15 @@ public abstract class BaseComputeServiceContextModule extends AbstractModule {
@Provides
@Singleton
public final Optional<ImageExtension> guiceProvideImageExtension(Injector i) {
return provideImageExtension(i);
}
protected Optional<ImageExtension> provideImageExtension(Injector i) {
return Optional.absent();
Binding<ImageExtension> binding = i.getExistingBinding(Key.get(ImageExtension.class));
return binding == null ? Optional.<ImageExtension> absent() : Optional.of(binding.getProvider().get());
}
@Provides
@Singleton
protected final Optional<SecurityGroupExtension> guiceProvideSecurityGroupExtension(Injector i) {
return provideSecurityGroupExtension(i);
}
protected Optional<SecurityGroupExtension> provideSecurityGroupExtension(Injector i) {
return Optional.absent();
Binding<SecurityGroupExtension> binding = i.getExistingBinding(Key.get(SecurityGroupExtension.class));
return binding == null ? Optional.<SecurityGroupExtension> absent() : Optional.of(binding.getProvider().get());
}

View File

@ -56,7 +56,6 @@ public interface TemplateBuilder {
*
* @since 1.5
*/
@Beta
TemplateBuilder from(TemplateBuilderSpec spec);
/**
@ -66,7 +65,6 @@ public interface TemplateBuilder {
* @param spec a String in the format specified by {@link TemplateBuilderSpec}
* @since 1.5
*/
@Beta
TemplateBuilder from(String spec);
/**
@ -202,5 +200,19 @@ public interface TemplateBuilder {
* Normal usage is to build up all options and pass them to the builder with a single call to this method.
*/
TemplateBuilder options(TemplateOptions options);
/**
* Forces an image lookup against the provider to reload the image cache.
* <p>
* Use with caution. In some providers getting the list of images is an
* expensive operation, and the use of the image cache is recommended. If
* there is a need to minimize the amount of time the images are cached,
* consider configuring the cache expiration time by setting the
* {@link org.jclouds.Constants#PROPERTY_SESSION_INTERVAL} property.
*
* @since 2.0
*/
@Beta
TemplateBuilder forceCacheReload();
}

View File

@ -145,6 +145,7 @@ public class TemplateBuilderSpec {
.put("loginUser", new LoginUserParser())
.put("authenticateSudo", new AuthenticateSudoParser())
.put("locationId", new LocationIdParser())
.put("forceCacheReload", new ForceCacheReloadParser())
.build();
@VisibleForTesting
@ -177,6 +178,8 @@ public class TemplateBuilderSpec {
Boolean authenticateSudo;
@VisibleForTesting
String locationId;
@VisibleForTesting
Boolean forceCacheReload;
/** Specification; used for toParseableString(). */
// transient in case people using serializers don't want this to show up
@ -279,6 +282,9 @@ public class TemplateBuilderSpec {
if (locationId != null) {
builder.locationId(locationId);
}
if (forceCacheReload != null && forceCacheReload) {
builder.forceCacheReload();
}
return builder;
}
@ -304,7 +310,8 @@ public class TemplateBuilderSpec {
@Override
public int hashCode() {
return Objects.hashCode(hardwareId, minCores, minRam, hypervisorMatches, imageId, imageNameMatches, osFamily,
osVersionMatches, os64Bit, osArchMatches, osDescriptionMatches, loginUser, authenticateSudo, locationId);
osVersionMatches, os64Bit, osArchMatches, osDescriptionMatches, loginUser, authenticateSudo, locationId,
forceCacheReload);
}
@Override
@ -322,7 +329,7 @@ public class TemplateBuilderSpec {
&& equal(osVersionMatches, that.osVersionMatches) && equal(os64Bit, that.os64Bit)
&& equal(osArchMatches, that.osArchMatches) && equal(osDescriptionMatches, that.osDescriptionMatches)
&& equal(loginUser, that.loginUser) && equal(authenticateSudo, that.authenticateSudo)
&& equal(locationId, that.locationId);
&& equal(locationId, that.locationId) && equal(forceCacheReload, that.forceCacheReload);
}
/** Base class for parsing doubles. */
@ -563,6 +570,15 @@ public class TemplateBuilderSpec {
}
}
/** Parse forceCacheReload */
static class ForceCacheReloadParser extends BooleanParser {
@Override
protected void parseBoolean(TemplateBuilderSpec spec, boolean value) {
checkArgument(spec.forceCacheReload == null, "forceCacheReload was already set to ", spec.forceCacheReload);
spec.forceCacheReload = value;
}
}
public String getHardwareId() {
return hardwareId;
}
@ -622,4 +638,9 @@ public class TemplateBuilderSpec {
public String getSpecification() {
return specification;
}
public Boolean getForceCacheReload() {
return forceCacheReload;
}
}

View File

@ -16,6 +16,7 @@
*/
package org.jclouds.compute.domain.internal;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.base.Predicates.and;
@ -52,8 +53,8 @@ import org.jclouds.compute.domain.Template;
import org.jclouds.compute.domain.TemplateBuilder;
import org.jclouds.compute.domain.TemplateBuilderSpec;
import org.jclouds.compute.options.TemplateOptions;
import org.jclouds.compute.predicates.ImagePredicates;
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;
@ -84,7 +85,6 @@ public class TemplateBuilderImpl implements TemplateBuilder {
protected final Supplier<Location> defaultLocation;
protected final Provider<TemplateOptions> optionsProvider;
protected final Provider<TemplateBuilder> defaultTemplateProvider;
protected final GetImageStrategy getImageStrategy;
@VisibleForTesting
protected Location location;
@ -128,19 +128,21 @@ public class TemplateBuilderImpl implements TemplateBuilder {
protected boolean fastest;
@VisibleForTesting
protected TemplateOptions options;
@VisibleForTesting
protected Boolean forceCacheReload;
@Inject
protected TemplateBuilderImpl(@Memoized Supplier<Set<? extends Location>> locations,
ImageCacheSupplier images, @Memoized Supplier<Set<? extends Hardware>> hardwares,
@Memoized Supplier<Set<? extends Image>> images, @Memoized Supplier<Set<? extends Hardware>> hardwares,
Supplier<Location> defaultLocation, @Named("DEFAULT") Provider<TemplateOptions> optionsProvider,
@Named("DEFAULT") Provider<TemplateBuilder> defaultTemplateProvider, GetImageStrategy getImageStrategy) {
@Named("DEFAULT") Provider<TemplateBuilder> defaultTemplateProvider) {
this.locations = checkNotNull(locations, "locations");
this.images = checkNotNull(images, "images");
checkArgument(images instanceof ImageCacheSupplier, "an instance of the ImageCacheSupplier is needed");
this.images = ImageCacheSupplier.class.cast(images);
this.hardwares = checkNotNull(hardwares, "hardwares");
this.defaultLocation = checkNotNull(defaultLocation, "defaultLocation");
this.optionsProvider = checkNotNull(optionsProvider, "optionsProvider");
this.defaultTemplateProvider = checkNotNull(defaultTemplateProvider, "defaultTemplateProvider");
this.getImageStrategy = checkNotNull(getImageStrategy, "getImageStrategy");
}
static Predicate<Hardware> supportsImagesPredicate(final Iterable<? extends Image> images) {
@ -175,26 +177,6 @@ public class TemplateBuilderImpl implements TemplateBuilder {
});
private final Predicate<Image> idPredicate = new Predicate<Image>() {
@Override
public boolean apply(Image input) {
boolean returnVal = true;
if (imageId != null) {
returnVal = imageId.equals(input.getId());
// match our input params so that the later predicates pass.
if (returnVal) {
fromImage(input);
}
}
return returnVal;
}
@Override
public String toString() {
return "imageId(" + imageId + ")";
}
};
private final Predicate<OperatingSystem> osFamilyPredicate = new Predicate<OperatingSystem>() {
@Override
@ -691,7 +673,7 @@ public class TemplateBuilderImpl implements TemplateBuilder {
Image image = null;
if (imageId != null) {
image = findImageWithId(images);
image = loadImageWithId(images);
if (currentLocationWiderThan(image.getLocation()))
this.location = image.getLocation();
}
@ -733,27 +715,16 @@ public class TemplateBuilderImpl implements TemplateBuilder {
return supportedImages;
}
private Image findImageWithId(Set<? extends Image> images) {
// 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()) {
return image.get();
private Image loadImageWithId(Iterable<? extends Image> images) {
Optional<? extends Image> image = tryFind(images, ImagePredicates.idEquals(imageId));
if (!image.isPresent()) {
image = this.images.get(imageId); // Load the image from the cache, and refresh if missing
if (!image.isPresent()) {
throw throwNoSuchElementExceptionAfterLoggingImageIds(format("imageId(%s) not found", imageId), images);
}
}
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 imageFromProvider = getImageStrategy.getImage(imageId);
if (imageFromProvider == null) {
throwNoSuchElementExceptionAfterLoggingImageIds(format("%s not found", idPredicate), images);
}
// 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;
fromImage(image.get());
return image.get();
}
private Hardware findHardwareWithId(Set<? extends Hardware> hardwaresToSearch) {
@ -856,10 +827,8 @@ public class TemplateBuilderImpl implements TemplateBuilder {
logger.trace("<< matched images(%s)", transform(matchingImages, imageToId));
return imageChooser().apply(matchingImages);
} catch (NoSuchElementException exception) {
throwNoSuchElementExceptionAfterLoggingImageIds(format("no image matched params: %s", toString()),
supportedImages);
assert false;
return null;
throw throwNoSuchElementExceptionAfterLoggingImageIds(format("no image matched params: %s", toString()),
supportedImages);
}
}
@ -885,7 +854,7 @@ public class TemplateBuilderImpl implements TemplateBuilder {
return maxes;
}
protected Set<? extends Image> getImages() {
return images.get();
return forceCacheReload != null && forceCacheReload ? images.rebuildCache() : images.get();
}
private Predicate<Image> buildImagePredicate() {
@ -1157,6 +1126,7 @@ public class TemplateBuilderImpl implements TemplateBuilder {
toString.add("os64Bit", os64Bit);
toString.add("hardwareId", hardwareId);
toString.add("hypervisor", hypervisor);
toString.add("forceCacheReload", forceCacheReload);
return toString;
}
@ -1176,4 +1146,11 @@ public class TemplateBuilderImpl implements TemplateBuilder {
return from(TemplateBuilderSpec.parse(spec));
}
@Override
public TemplateBuilder forceCacheReload() {
this.forceCacheReload = true;
return this;
}
}

View File

@ -0,0 +1,74 @@
/*
* 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.extensions.internal;
import static com.google.common.base.Preconditions.checkNotNull;
import org.jclouds.compute.domain.Image;
import org.jclouds.compute.domain.ImageTemplate;
import org.jclouds.compute.extensions.ImageExtension;
import org.jclouds.compute.suppliers.ImageCacheSupplier;
import com.google.common.annotations.Beta;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
/**
* Delegates to the provider specific {@link ImageExtension} and takes care of
* propagating the changes made to the images to the image cache.
*/
@Beta
public class DelegatingImageExtension implements ImageExtension {
private final ImageCacheSupplier imageCache;
private final ImageExtension delegate;
public DelegatingImageExtension(ImageCacheSupplier imageCache, ImageExtension delegate) {
this.imageCache = checkNotNull(imageCache, "imageCache");
this.delegate = checkNotNull(delegate, "delegate");
}
public ImageTemplate buildImageTemplateFromNode(String name, String id) {
return delegate.buildImageTemplateFromNode(name, id);
}
public ListenableFuture<Image> createImage(ImageTemplate template) {
ListenableFuture<Image> future = delegate.createImage(template);
Futures.addCallback(future, new FutureCallback<Image>() {
@Override
public void onSuccess(Image result) {
imageCache.registerImage(result);
}
@Override
public void onFailure(Throwable t) {
}
});
return future;
}
public boolean deleteImage(String id) {
boolean success = delegate.deleteImage(id);
if (success) {
imageCache.removeImage(id);
}
return success;
}
}

View File

@ -66,6 +66,7 @@ import org.jclouds.compute.domain.Template;
import org.jclouds.compute.domain.TemplateBuilder;
import org.jclouds.compute.extensions.ImageExtension;
import org.jclouds.compute.extensions.SecurityGroupExtension;
import org.jclouds.compute.extensions.internal.DelegatingImageExtension;
import org.jclouds.compute.options.RunScriptOptions;
import org.jclouds.compute.options.TemplateOptions;
import org.jclouds.compute.reference.ComputeServiceConstants;
@ -80,6 +81,7 @@ import org.jclouds.compute.strategy.RebootNodeStrategy;
import org.jclouds.compute.strategy.ResumeNodeStrategy;
import org.jclouds.compute.strategy.RunScriptOnNodeAndAddToGoodMapOrPutExceptionIntoBadMap;
import org.jclouds.compute.strategy.SuspendNodeStrategy;
import org.jclouds.compute.suppliers.ImageCacheSupplier;
import org.jclouds.domain.Credentials;
import org.jclouds.domain.Location;
import org.jclouds.domain.LoginCredentials;
@ -180,8 +182,13 @@ public class BaseComputeService implements ComputeService {
this.runScriptOnNodeFactory = checkNotNull(runScriptOnNodeFactory, "runScriptOnNodeFactory");
this.persistNodeCredentials = checkNotNull(persistNodeCredentials, "persistNodeCredentials");
this.userExecutor = checkNotNull(userExecutor, "userExecutor");
this.imageExtension = checkNotNull(imageExtension, "imageExtension");
this.securityGroupExtension = checkNotNull(securityGroupExtension, "securityGroupExtension");
if (imageExtension.isPresent() && images instanceof ImageCacheSupplier) {
this.imageExtension = Optional.<ImageExtension> of(new DelegatingImageExtension(ImageCacheSupplier.class
.cast(images), imageExtension.get()));
} else {
this.imageExtension = checkNotNull(imageExtension, "imageExtension");
}
}
/**

View File

@ -17,12 +17,8 @@
package org.jclouds.compute.stub.config;
import org.jclouds.compute.config.JCloudsNativeComputeServiceAdapterContextModule;
import org.jclouds.compute.extensions.SecurityGroupExtension;
import org.jclouds.concurrent.SingleThreaded;
import com.google.common.base.Optional;
import com.google.inject.Injector;
@SingleThreaded
public class StubComputeServiceContextModule extends JCloudsNativeComputeServiceAdapterContextModule {
@ -36,9 +32,4 @@ public class StubComputeServiceContextModule extends JCloudsNativeComputeService
super.configure();
}
@Override
protected Optional<SecurityGroupExtension> provideSecurityGroupExtension(Injector i) {
return Optional.of(i.getInstance(SecurityGroupExtension.class));
}
}

View File

@ -17,68 +17,181 @@
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 java.util.concurrent.atomic.AtomicReference;
import javax.annotation.Resource;
import javax.inject.Named;
import javax.inject.Singleton;
import org.jclouds.compute.domain.Image;
import org.jclouds.compute.reference.ComputeServiceConstants;
import org.jclouds.compute.strategy.GetImageStrategy;
import org.jclouds.logging.Logger;
import org.jclouds.rest.AuthorizationException;
import org.jclouds.rest.suppliers.MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier;
import org.jclouds.rest.suppliers.ValueLoadedCallback;
import com.google.common.annotations.Beta;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Supplier;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableSet;
import com.google.inject.Inject;
import com.google.common.collect.Maps;
import com.google.inject.Provider;
/**
* Image supplier that allows new images to be registered.
* Memoized image supplier that allows new images to be registered at runtime.
* <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.
* The memoized <code>Supplier<Set<? extends Image>></code> is a static data
* structure that can't be properly modified at runtime. This class is a wrapper
* for the image supplier 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.
*/
@Singleton
public class ImageCacheSupplier implements Supplier<Set<? extends Image>> {
@Beta
public class ImageCacheSupplier implements Supplier<Set<? extends Image>>, ValueLoadedCallback<Set<? extends Image>> {
private final Supplier<Set<? extends Image>> imageCache;
/**
* The image supplier that fetches the images from the provider.
*/
private final Supplier<Set<? extends Image>> liveImageSupplier;
/**
* The image supplier that loads the images and caches them for the duration
* of the session. Delegates to the {@link #liveImageSupplier}.
*/
private final Supplier<Set<? extends Image>> memoizedImageSupplier;
/**
* The actual image cache. It acts as a view over the memoized image supplier
* and allows to add and remove images at runtime.
*/
private final LoadingCache<String, Image> imageCache;
@Resource
@Named(ComputeServiceConstants.COMPUTE_LOGGER)
protected Logger logger = Logger.NULL;
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();
public ImageCacheSupplier(Supplier<Set<? extends Image>> imageSupplier, long sessionIntervalSeconds,
AtomicReference<AuthorizationException> authException, final Provider<GetImageStrategy> imageLoader) {
liveImageSupplier = imageSupplier;
memoizedImageSupplier = MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException,
imageSupplier, sessionIntervalSeconds, TimeUnit.SECONDS, this);
imageCache = CacheBuilder.newBuilder().expireAfterWrite(sessionIntervalSeconds, TimeUnit.SECONDS)
.build(new CacheLoader<String, Image>() {
@Override
public Image load(String key) throws Exception {
return imageLoader.get().getImage(key);
}
});
}
@Override
public Set<? extends Image> get() {
return ImmutableSet.copyOf(concat(imageCache.get(), uncachedImages.asMap().values()));
// Call the memoized supplier. The "imageCache" is subscribed to the
// reloads of the supplier once it expires. For this reason we ignore the
// value returned by the supplier: every time it is reloaded, the cache
// will be notified and re-populated with the fresh values. Any other call
// to the supplier that returns a cached value will be ignored and the
// values in the cache will be returned, as the cache properly handles
// individual image additions and deletions (introduced, for example, by
// the usage of the ImageExtension).
memoizedImageSupplier.get();
return ImmutableSet.copyOf(imageCache.asMap().values());
}
/**
* The cache is subscribed to value loading events generated by the
* {@link MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier}.
* <p>
* Every time the memoized supplier reloads a value, an event will be
* populated and this method will handle it. This makes it possible to
* refresh the cache with the last values everytime they are reloaded.
*/
@Override
public void valueLoaded(Optional<Set<? extends Image>> value) {
if (value.isPresent()) {
reset(value.get());
}
}
/**
* Resets the cache to the given set of images.
* <p>
* This method is called when the memoized image supplier is reloaded, or
* when the cache needs to be refreshed (for example when the TempalteBuilder
* is invoked forcing a fresh image lookup.
*/
public void reset(Set<? extends Image> images) {
imageCache.invalidateAll();
imageCache.putAll(Maps.uniqueIndex(images, new Function<Image, String>() {
@Override
public String apply(Image input) {
return input.getId();
}
}));
}
/**
* Calls the {@link #liveImageSupplier} to get the current images and
* rebuilds the cache with them.
*/
public Set<? extends Image> rebuildCache() {
Set<? extends Image> images = liveImageSupplier.get();
reset(images);
return images;
}
/**
* Loads an image by id.
* <p>
* This methods returns the cached image, or performs a call to retrieve it
* if the image is still not cached.
*/
public Optional<? extends Image> get(String id) {
try {
return Optional.fromNullable(imageCache.getUnchecked(id));
} catch (Exception ex) {
logger.error(ex, "Unexpected error loading image %s", id);
return Optional.absent();
}
}
/**
* 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.
* 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.
* 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);
imageCache.put(image.getId(), image);
}
/**
* Removes an image from the image cache.
* <p>
* This method should be called to invalidate an already cached image, when
* some image known to not exist in the provider is still cached.
*
* @param imageId The id of the image to invalidate.
*/
public void removeImage(String imageId) {
imageCache.invalidate(checkNotNull(imageId, "imageId"));
}
}

View File

@ -61,6 +61,7 @@ public class TemplateBuilderSpecTest {
assertNull(spec.loginUser);
assertNull(spec.authenticateSudo);
assertNull(spec.locationId);
assertNull(spec.forceCacheReload);
assertTemplateBuilderEquivalence(templateBuilders.get(), templateBuilders.get().from(spec));
}
@ -81,6 +82,7 @@ public class TemplateBuilderSpecTest {
assertNull(spec.loginUser);
assertNull(spec.authenticateSudo);
assertNull(spec.locationId);
assertNull(spec.forceCacheReload);
assertTemplateBuilderEquivalence(templateBuilders.get().hardwareId("m1.small"),
templateBuilders.get().from(spec));
}
@ -132,6 +134,7 @@ public class TemplateBuilderSpecTest {
assertNull(spec.loginUser);
assertNull(spec.authenticateSudo);
assertNull(spec.locationId);
assertNull(spec.forceCacheReload);
assertTemplateBuilderEquivalence(templateBuilders.get().minCores(32), templateBuilders.get().from(spec));
}
@ -160,6 +163,7 @@ public class TemplateBuilderSpecTest {
assertNull(spec.loginUser);
assertNull(spec.authenticateSudo);
assertNull(spec.locationId);
assertNull(spec.forceCacheReload);
assertTemplateBuilderEquivalence(templateBuilders.get().minRam(10), templateBuilders.get().from(spec));
}
@ -180,6 +184,7 @@ public class TemplateBuilderSpecTest {
assertNull(spec.loginUser);
assertNull(spec.authenticateSudo);
assertNull(spec.locationId);
assertNull(spec.forceCacheReload);
assertTemplateBuilderEquivalence(templateBuilders.get().minRam(10), templateBuilders.get().from(spec));
}
@ -216,6 +221,7 @@ public class TemplateBuilderSpecTest {
assertNull(spec.loginUser);
assertNull(spec.authenticateSudo);
assertNull(spec.locationId);
assertNull(spec.forceCacheReload);
assertTemplateBuilderEquivalence(templateBuilders.get().hypervisorMatches("OpenVZ"),
templateBuilders.get().from(spec));
}
@ -246,6 +252,7 @@ public class TemplateBuilderSpecTest {
assertNull(spec.loginUser);
assertNull(spec.authenticateSudo);
assertNull(spec.locationId);
assertNull(spec.forceCacheReload);
assertTemplateBuilderEquivalence(templateBuilders.get().imageId("us-east-1/ami-fffffff"),
templateBuilders.get().from(spec));
}
@ -315,6 +322,7 @@ public class TemplateBuilderSpecTest {
assertNull(spec.loginUser);
assertNull(spec.authenticateSudo);
assertNull(spec.locationId);
assertNull(spec.forceCacheReload);
assertTemplateBuilderEquivalence(templateBuilders.get().imageNameMatches(".*w/ None.*"),
templateBuilders.get().from(spec));
}
@ -345,6 +353,7 @@ public class TemplateBuilderSpecTest {
assertNull(spec.loginUser);
assertNull(spec.authenticateSudo);
assertNull(spec.locationId);
assertNull(spec.forceCacheReload);
assertTemplateBuilderEquivalence(templateBuilders.get().osFamily(OsFamily.UBUNTU),
templateBuilders.get().from(spec));
}
@ -374,6 +383,7 @@ public class TemplateBuilderSpecTest {
assertNull(spec.loginUser);
assertNull(spec.authenticateSudo);
assertNull(spec.locationId);
assertNull(spec.forceCacheReload);
assertTemplateBuilderEquivalence(templateBuilders.get().osVersionMatches(".*[Aa]utomated SSH Access.*"),
templateBuilders.get().from(spec));
}
@ -403,6 +413,7 @@ public class TemplateBuilderSpecTest {
assertNull(spec.loginUser);
assertNull(spec.authenticateSudo);
assertNull(spec.locationId);
assertNull(spec.forceCacheReload);
assertTemplateBuilderEquivalence(templateBuilders.get().os64Bit(true),
templateBuilders.get().from(spec));
}
@ -432,6 +443,7 @@ public class TemplateBuilderSpecTest {
assertNull(spec.loginUser);
assertNull(spec.authenticateSudo);
assertNull(spec.locationId);
assertNull(spec.forceCacheReload);
assertTemplateBuilderEquivalence(templateBuilders.get().osArchMatches("x86"),
templateBuilders.get().from(spec));
}
@ -461,6 +473,7 @@ public class TemplateBuilderSpecTest {
assertNull(spec.loginUser);
assertNull(spec.authenticateSudo);
assertNull(spec.locationId);
assertNull(spec.forceCacheReload);
assertTemplateBuilderEquivalence(templateBuilders.get().osDescriptionMatches("^((?!MGC).)*$"),
templateBuilders.get().from(spec));
}
@ -491,6 +504,7 @@ public class TemplateBuilderSpecTest {
assertEquals(spec.loginUser, "ubuntu");
assertNull(spec.authenticateSudo);
assertNull(spec.locationId);
assertNull(spec.forceCacheReload);
assertTemplateBuilderEquivalence(
templateBuilders.get().options(overrideLoginUser("ubuntu")), templateBuilders
.get().from(spec));
@ -522,6 +536,7 @@ public class TemplateBuilderSpecTest {
assertEquals(spec.loginUser, "root:toor");
assertNull(spec.authenticateSudo);
assertNull(spec.locationId);
assertNull(spec.forceCacheReload);
assertTemplateBuilderEquivalence(
templateBuilders.get().options(
overrideLoginCredentials(LoginCredentials.builder().user("root").password("toor").build())),
@ -554,12 +569,13 @@ public class TemplateBuilderSpecTest {
assertEquals(spec.loginUser, "root:toor");
assertEquals(spec.authenticateSudo.booleanValue(), true);
assertNull(spec.locationId);
assertNull(spec.forceCacheReload);
assertTemplateBuilderEquivalence(
templateBuilders.get().options(
overrideLoginCredentials(LoginCredentials.builder().user("root").password("toor")
.authenticateSudo(true).build())), templateBuilders.get().from(spec));
}
public void testParse_authenticateSudoRepeated() {
try {
parse("loginUser=root:toor,authenticateSudo=true,authenticateSudo=false");
@ -568,6 +584,36 @@ public class TemplateBuilderSpecTest {
// expected
}
}
public void testParse_forceCacheReload() {
TemplateBuilderSpec spec = parse("forceCacheReload=true");
assertNull(spec.hardwareId);
assertNull(spec.minCores);
assertNull(spec.minRam);
assertNull(spec.minDisk);
assertNull(spec.imageId);
assertNull(spec.imageNameMatches);
assertNull(spec.hypervisorMatches);
assertNull(spec.osFamily);
assertNull(spec.osVersionMatches);
assertNull(spec.os64Bit);
assertNull(spec.osArchMatches);
assertNull(spec.osDescriptionMatches);
assertNull(spec.loginUser);
assertNull(spec.authenticateSudo);
assertNull(spec.locationId);
assertEquals(spec.forceCacheReload.booleanValue(), true);
assertTemplateBuilderEquivalence(templateBuilders.get().forceCacheReload(), templateBuilders.get().from(spec));
}
public void testParse_forceCacheReloadRepeated() {
try {
parse("forceCacheReload=true,forceCacheReload=false");
fail("Expected exception");
} catch (IllegalArgumentException expected) {
// expected
}
}
public void testParse_locationId() {
TemplateBuilderSpec spec = parse("locationId=stub");
@ -586,6 +632,7 @@ public class TemplateBuilderSpecTest {
assertNull(spec.loginUser);
assertNull(spec.authenticateSudo);
assertEquals(spec.locationId, "stub");
assertNull(spec.forceCacheReload);
assertTemplateBuilderEquivalence(templateBuilders.get().locationId("stub"),
templateBuilders.get().from(spec));
}
@ -616,6 +663,7 @@ public class TemplateBuilderSpecTest {
assertNull(spec.loginUser);
assertNull(spec.authenticateSudo);
assertNull(spec.locationId);
assertNull(spec.forceCacheReload);
TemplateBuilder expected = templateBuilders.get().osVersionMatches("1[012].[01][04]").imageNameMatches(".*w/ None.*").osFamily(OsFamily.UBUNTU);
assertTemplateBuilderEquivalence(expected, templateBuilders.get().from(spec));
}
@ -637,6 +685,7 @@ public class TemplateBuilderSpecTest {
assertNull(spec.loginUser);
assertNull(spec.authenticateSudo);
assertNull(spec.locationId);
assertNull(spec.forceCacheReload);
TemplateBuilder expected = templateBuilders.get().minRam(10).osFamily(OsFamily.UBUNTU);
assertTemplateBuilderEquivalence(expected, templateBuilders.get().from(spec));
}

View File

@ -50,6 +50,7 @@ import org.jclouds.compute.suppliers.ImageCacheSupplier;
import org.jclouds.domain.Location;
import org.jclouds.domain.LocationBuilder;
import org.jclouds.domain.LocationScope;
import org.jclouds.rest.AuthorizationException;
import org.testng.annotations.Test;
import com.google.common.base.Function;
@ -62,6 +63,8 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Ordering;
import com.google.common.util.concurrent.Atomics;
import com.google.inject.util.Providers;
@Test(groups = "unit", singleThreaded = true, testName = "TemplateBuilderImplTest")
public class TemplateBuilderImplTest {
@ -468,8 +471,10 @@ 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, new ImageCacheSupplier(images, 60), hardwares, Suppliers
.ofInstance(defaultLocation), optionsProvider, templateBuilderProvider, getImageStrategy);
TemplateBuilderImpl template = new TemplateBuilderImpl(locations, new ImageCacheSupplier(images, 60,
Atomics.<AuthorizationException> newReference(), Providers.of(getImageStrategy)), hardwares,
Suppliers.ofInstance(defaultLocation),
optionsProvider, templateBuilderProvider);
return template;
}

View File

@ -20,8 +20,10 @@ import static java.util.concurrent.TimeUnit.SECONDS;
import static org.jclouds.compute.predicates.NodePredicates.inGroup;
import static org.jclouds.util.Predicates2.retry;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;
import java.util.NoSuchElementException;
import java.util.concurrent.ExecutionException;
import javax.annotation.Resource;
@ -90,46 +92,40 @@ public abstract class BaseImageExtensionLiveTest extends BaseComputeServiceConte
@Test(groups = { "integration", "live" }, singleThreaded = true)
public void testCreateImage() throws RunNodesException, InterruptedException, ExecutionException {
ComputeService computeService = view.getComputeService();
Optional<ImageExtension> imageExtension = computeService.getImageExtension();
assertTrue(imageExtension.isPresent(), "image extension was not present");
Template template = getNodeTemplate();
NodeMetadata node = Iterables.getOnlyElement(computeService.createNodesInGroup(imageGroup, 1, template));
checkReachable(node);
logger.info("Creating image from node %s, started with template: %s", node, template);
ImageTemplate newImageTemplate = imageExtension.get().buildImageTemplateFromNode(imageGroup,
node.getId());
Image image = imageExtension.get().createImage(newImageTemplate).get();
logger.info("Image created: %s", image);
assertEquals(imageGroup, image.getName());
imageId = image.getId();
computeService.destroyNode(node.getId());
Optional<? extends Image> optImage = getImage();
assertTrue(optImage.isPresent());
}
@Test(groups = { "integration", "live" }, singleThreaded = true, dependsOnMethods = "testCreateImage")
public void testImageIsCachedAfterBeingCreated() {
Optional<Image> imageInCache = findImageWithNameInCache(imageGroup);
assertTrue(imageInCache.isPresent());
assertEquals(imageInCache.get().getId(), imageId);
}
@Test(groups = { "integration", "live" }, singleThreaded = true, dependsOnMethods = "testCreateImage")
public void testSpawnNodeFromImage() throws RunNodesException {
ComputeService computeService = view.getComputeService();
Optional<? extends Image> optImage = getImage();
assertTrue(optImage.isPresent());
NodeMetadata node = Iterables.getOnlyElement(computeService.createNodesInGroup(imageGroup, 1, view
@ -138,28 +134,30 @@ public abstract class BaseImageExtensionLiveTest extends BaseComputeServiceConte
.templateBuilder().imageId(optImage.get().getId()).fromImage(optImage.get()).build()));
checkReachable(node);
view.getComputeService().destroyNode(node.getId());
}
@Test(groups = { "integration", "live" }, singleThreaded = true, dependsOnMethods = { "testCreateImage",
"testSpawnNodeFromImage" })
"testSpawnNodeFromImage", "testImageIsCachedAfterBeingCreated" })
public void testDeleteImage() {
ComputeService computeService = view.getComputeService();
Optional<ImageExtension> imageExtension = computeService.getImageExtension();
assertTrue(imageExtension.isPresent(), "image extension was not present");
Optional<? extends Image> optImage = getImage();
assertTrue(optImage.isPresent());
Image image = optImage.get();
assertTrue(imageExtension.get().deleteImage(image.getId()));
}
@Test(groups = { "integration", "live" }, singleThreaded = true, dependsOnMethods = "testDeleteImage")
public void testImageIsRemovedFromCacheAfterDeletion() {
Optional<Image> imageInCache = findImageWithNameInCache(imageGroup);
assertFalse(imageInCache.isPresent());
assertFalse(getImage().isPresent());
}
private Optional<? extends Image> getImage() {
return Optional.fromNullable(view.getComputeService().getImage(imageId));
@ -179,6 +177,15 @@ public abstract class BaseImageExtensionLiveTest extends BaseComputeServiceConte
}, getSpawnNodeMaxWait(), 1l, SECONDS).apply(client));
}
protected Optional<Image> findImageWithNameInCache(String name) {
try {
Template template = view.getComputeService().templateBuilder().imageNameMatches(name).build();
return Optional.of(template.getImage());
} catch (NoSuchElementException ex) {
return Optional.absent();
}
}
@AfterClass(groups = { "integration", "live" })
@Override
protected void tearDownContext() {

View File

@ -16,20 +16,31 @@
*/
package org.jclouds.compute.suppliers;
import static com.google.common.collect.Iterables.any;
import static org.jclouds.compute.predicates.ImagePredicates.idEquals;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.jclouds.compute.domain.Image;
import org.jclouds.compute.domain.ImageBuilder;
import org.jclouds.compute.domain.OperatingSystem;
import org.jclouds.compute.strategy.GetImageStrategy;
import org.jclouds.domain.Location;
import org.jclouds.domain.LocationBuilder;
import org.jclouds.domain.LocationScope;
import org.jclouds.rest.AuthorizationException;
import org.testng.annotations.Test;
import com.google.common.base.Optional;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableSet;
import com.google.common.util.concurrent.Atomics;
import com.google.common.util.concurrent.Uninterruptibles;
import com.google.inject.util.Providers;
/**
* Unit tests for the {@link ImageCacheSupplier} class.
@ -49,15 +60,25 @@ public class ImageCacheSupplierTest {
private Set<? extends Image> images = ImmutableSet.of(image);
private GetImageStrategy getImageStrategy = new GetImageStrategy() {
@Override
public Image getImage(String id) {
return new ImageBuilder().id(id).providerId(id).name("imageName-" + id).description("imageDescription")
.version("imageVersion").operatingSystem(os).status(Image.Status.AVAILABLE).location(location).build();
}
};
@Test(expectedExceptions = NullPointerException.class)
public void testRegisterNullImageIsNotAllowed() {
ImageCacheSupplier imageCache = new ImageCacheSupplier(Suppliers.<Set<? extends Image>> ofInstance(images), 60);
ImageCacheSupplier imageCache = new ImageCacheSupplier(Suppliers.<Set<? extends Image>> ofInstance(images), 60,
Atomics.<AuthorizationException> newReference(), Providers.of(getImageStrategy));
imageCache.registerImage(null);
}
@Test
public void testRegisterImageIgnoresDuplicates() {
ImageCacheSupplier imageCache = new ImageCacheSupplier(Suppliers.<Set<? extends Image>> ofInstance(images), 60);
ImageCacheSupplier imageCache = new ImageCacheSupplier(Suppliers.<Set<? extends Image>> ofInstance(images), 60,
Atomics.<AuthorizationException> newReference(), Providers.of(getImageStrategy));
assertEquals(imageCache.get().size(), 1);
imageCache.registerImage(image);
@ -67,11 +88,63 @@ public class ImageCacheSupplierTest {
@Test
public void testRegisterNewImage() {
ImageCacheSupplier imageCache = new ImageCacheSupplier(Suppliers.<Set<? extends Image>> ofInstance(images), 60);
ImageCacheSupplier imageCache = new ImageCacheSupplier(Suppliers.<Set<? extends Image>> ofInstance(images), 60,
Atomics.<AuthorizationException> newReference(), Providers.of(getImageStrategy));
assertEquals(imageCache.get().size(), 1);
imageCache.registerImage(ImageBuilder.fromImage(image).id("newimage").build());
assertEquals(imageCache.get().size(), 2);
}
@Test(expectedExceptions = NullPointerException.class)
public void testRemoveNullImageIsNotAllowed() {
ImageCacheSupplier imageCache = new ImageCacheSupplier(Suppliers.<Set<? extends Image>> ofInstance(images), 60,
Atomics.<AuthorizationException> newReference(), Providers.of(getImageStrategy));
imageCache.removeImage(null);
}
@Test
public void testRemoveImage() {
ImageCacheSupplier imageCache = new ImageCacheSupplier(Suppliers.<Set<? extends Image>> ofInstance(images), 60,
Atomics.<AuthorizationException> newReference(), Providers.of(getImageStrategy));
assertEquals(imageCache.get().size(), 1);
imageCache.removeImage(image.getId());
assertEquals(imageCache.get().size(), 0);
}
@Test
public void testLoadImage() {
ImageCacheSupplier imageCache = new ImageCacheSupplier(Suppliers.<Set<? extends Image>> ofInstance(images), 60,
Atomics.<AuthorizationException> newReference(), Providers.of(getImageStrategy));
assertEquals(imageCache.get().size(), 1);
Optional<? extends Image> image = imageCache.get("foo");
assertTrue(image.isPresent());
assertEquals(image.get().getName(), "imageName-foo");
assertEquals(imageCache.get().size(), 2);
}
@Test
public void testSupplierExpirationReloadsTheCache() {
ImageCacheSupplier imageCache = new ImageCacheSupplier(Suppliers.<Set<? extends Image>> ofInstance(images), 3,
Atomics.<AuthorizationException> newReference(), Providers.of(getImageStrategy));
assertEquals(imageCache.get().size(), 1);
Optional<? extends Image> image = imageCache.get("foo");
// Load an image into the cache
assertTrue(image.isPresent());
assertEquals(image.get().getName(), "imageName-foo");
assertEquals(imageCache.get().size(), 2);
// Once the supplier expires, reloading it will laod the initial values
// (it is a hardcoded supplier), so the just loaded image should be gone
Uninterruptibles.sleepUninterruptibly(3, TimeUnit.SECONDS);
assertEquals(imageCache.get().size(), 1);
assertFalse(any(imageCache.get(), idEquals("foo")));
}
}

View File

@ -53,16 +53,18 @@ import com.google.common.util.concurrent.UncheckedExecutionException;
*/
public class MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<T> extends ForwardingObject implements
Supplier<T> {
static class SetAndThrowAuthorizationExceptionSupplierBackedLoader<V> extends CacheLoader<String, Optional<V>> {
private final Supplier<V> delegate;
private final AtomicReference<AuthorizationException> authException;
private final ValueLoadedCallback<V> valueLoadedCallback;
public SetAndThrowAuthorizationExceptionSupplierBackedLoader(Supplier<V> delegate,
AtomicReference<AuthorizationException> authException) {
AtomicReference<AuthorizationException> authException, ValueLoadedCallback<V> valueLoadedCallback) {
this.delegate = checkNotNull(delegate, "delegate");
this.authException = checkNotNull(authException, "authException");
this.valueLoadedCallback = checkNotNull(valueLoadedCallback, "valueLoadedCallback");
}
@Override
@ -70,7 +72,9 @@ public class MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<T> ext
if (authException.get() != null)
throw authException.get();
try {
return Optional.fromNullable(delegate.get());
Optional<V> value = Optional.fromNullable(delegate.get());
valueLoadedCallback.valueLoaded(value);
return value;
} catch (Exception e) {
AuthorizationException aex = getFirstThrowableOfType(e, AuthorizationException.class);
if (aex != null) {
@ -85,7 +89,24 @@ public class MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<T> ext
public String toString() {
return Objects.toStringHelper(this).add("delegate", delegate).toString();
}
}
public static class ValueLoadedEvent<V> {
private final Object eventKey;
private final Optional<V> value;
public ValueLoadedEvent(Object eventKey, Optional<V> value) {
this.eventKey = checkNotNull(eventKey, "eventKey");
this.value = checkNotNull(value, "value");
}
public Object getEventKey() {
return eventKey;
}
public Optional<V> getValue() {
return value;
}
}
private final Supplier<T> delegate;
@ -96,16 +117,26 @@ public class MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<T> ext
public static <T> MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<T> create(
AtomicReference<AuthorizationException> authException, Supplier<T> delegate, long duration, TimeUnit unit) {
return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<T>(authException, delegate, duration,
unit);
unit, new ValueLoadedCallback.NoOpCallback<T>());
}
/**
* Creates a memoized supplier that calls the given callback each time values are loaded.
*/
public static <T> MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<T> create(
AtomicReference<AuthorizationException> authException, Supplier<T> delegate, long duration, TimeUnit unit,
ValueLoadedCallback<T> valueLoadedCallback) {
return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<T>(authException, delegate, duration,
unit, valueLoadedCallback);
}
MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier(AtomicReference<AuthorizationException> authException,
Supplier<T> delegate, long duration, TimeUnit unit) {
Supplier<T> delegate, long duration, TimeUnit unit, ValueLoadedCallback<T> valueLoadedCallback) {
this.delegate = delegate;
this.duration = duration;
this.unit = unit;
this.cache = CacheBuilder.newBuilder().expireAfterWrite(duration, unit)
.build(new SetAndThrowAuthorizationExceptionSupplierBackedLoader<T>(delegate, authException));
.build(new SetAndThrowAuthorizationExceptionSupplierBackedLoader<T>(delegate, authException, valueLoadedCallback));
}
@Override

View File

@ -0,0 +1,37 @@
/*
* 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.rest.suppliers;
import com.google.common.annotations.Beta;
import com.google.common.base.Optional;
/**
* Callback that gets called every time the supplier loads new values.
*/
@Beta
public interface ValueLoadedCallback<V> {
void valueLoaded(Optional<V> value);
/** The default implementation does nothing */
public static class NoOpCallback<V> implements ValueLoadedCallback<V> {
@Override
public void valueLoaded(Optional<V> value) {
// Do nothing
}
}
}

View File

@ -21,22 +21,26 @@ import static com.google.common.util.concurrent.Atomics.newReference;
import static org.testng.Assert.assertEquals;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import org.jclouds.rest.AuthorizationException;
import org.jclouds.rest.suppliers.MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.SetAndThrowAuthorizationExceptionSupplierBackedLoader;
import org.testng.annotations.Test;
import com.google.common.base.Optional;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.util.concurrent.UncheckedExecutionException;
import com.google.common.util.concurrent.Uninterruptibles;
@Test(groups = "unit", testName = "MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplierTest")
public class MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplierTest {
@Test
public void testLoaderNormal() {
AtomicReference<AuthorizationException> authException = newReference();
assertEquals(new SetAndThrowAuthorizationExceptionSupplierBackedLoader<String>(ofInstance("foo"),
authException).load("KEY").get(), "foo");
assertEquals(new SetAndThrowAuthorizationExceptionSupplierBackedLoader<String>(ofInstance("foo"), authException, new ValueLoadedCallback.NoOpCallback<String>()).load("KEY").get(), "foo");
assertEquals(authException.get(), null);
}
@ -48,7 +52,7 @@ public class MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplierTest {
public String get() {
throw new AuthorizationException();
}
}, authException).load("KEY");
}, authException, new ValueLoadedCallback.NoOpCallback<String>()).load("KEY");
} finally {
assertEquals(authException.get().getClass(), AuthorizationException.class);
}
@ -62,7 +66,7 @@ public class MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplierTest {
public String get() {
throw new RuntimeException(new ExecutionException(new AuthorizationException()));
}
}, authException).load("KEY");
}, authException, new ValueLoadedCallback.NoOpCallback<String>()).load("KEY");
} finally {
assertEquals(authException.get().getClass(), AuthorizationException.class);
}
@ -76,7 +80,7 @@ public class MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplierTest {
public String get() {
throw new UncheckedExecutionException(new AuthorizationException());
}
}, authException).load("KEY");
}, authException, new ValueLoadedCallback.NoOpCallback<String>()).load("KEY");
} finally {
assertEquals(authException.get().getClass(), AuthorizationException.class);
}
@ -90,9 +94,43 @@ public class MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplierTest {
public String get() {
throw new RuntimeException(new IllegalArgumentException("foo"));
}
}, authException).load("KEY");
}, authException, new ValueLoadedCallback.NoOpCallback<String>()).load("KEY");
} finally {
assertEquals(authException.get().getClass(), RuntimeException.class);
}
}
@Test
public void testLoaderNotifiesAfterReloading() {
AtomicReference<AuthorizationException> authException = newReference();
ValueLoadedEventHandler handler = new ValueLoadedEventHandler();
Supplier<String> supplier = MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException,
Suppliers.<String> ofInstance("foo"), 3, TimeUnit.SECONDS, handler);
// The supplier loads the value initially and returns the cached values
assertEquals(handler.count.get(), 0);
supplier.get();
assertEquals(handler.count.get(), 1);
supplier.get();
assertEquals(handler.count.get(), 1);
// Once expired, it reloads the values, notified the event, and updated the cache
Uninterruptibles.sleepUninterruptibly(4, TimeUnit.SECONDS);
supplier.get();
assertEquals(handler.count.get(), 2);
supplier.get();
assertEquals(handler.count.get(), 2);
supplier.get();
assertEquals(handler.count.get(), 2);
}
static class ValueLoadedEventHandler implements ValueLoadedCallback<String>{
AtomicInteger count = new AtomicInteger(0);
@Override
public void valueLoaded(Optional<String> value) {
count.incrementAndGet();
}
}
}

View File

@ -27,8 +27,6 @@ import org.jclouds.compute.domain.Hardware;
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;
@ -40,11 +38,11 @@ public class AWSEC2TemplateBuilderImpl extends EC2TemplateBuilderImpl {
@Inject
protected AWSEC2TemplateBuilderImpl(@Memoized Supplier<Set<? extends Location>> locations,
ImageCacheSupplier images, @Memoized Supplier<Set<? extends Hardware>> sizes,
@Memoized Supplier<Set<? extends Image>> images, @Memoized Supplier<Set<? extends Hardware>> sizes,
Supplier<Location> defaultLocation, @Named("DEFAULT") Provider<TemplateOptions> optionsProvider,
@Named("DEFAULT") Provider<TemplateBuilder> defaultTemplateProvider, GetImageStrategy getImageStrategy,
@Named("DEFAULT") Provider<TemplateBuilder> defaultTemplateProvider,
Supplier<LoadingCache<RegionAndName, ? extends Image>> imageMap) {
super(locations, images, sizes, defaultLocation, optionsProvider, defaultTemplateProvider, getImageStrategy, imageMap);
super(locations, images, sizes, defaultLocation, optionsProvider, defaultTemplateProvider, imageMap);
}
}

View File

@ -16,14 +16,11 @@
*/
package org.jclouds.aws.ec2.compute.config;
import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicReference;
import javax.inject.Named;
import javax.inject.Singleton;
import org.jclouds.aws.ec2.compute.AWSEC2TemplateBuilderImpl;
@ -38,8 +35,6 @@ import org.jclouds.aws.ec2.compute.strategy.CreateKeyPairPlacementAndSecurityGro
import org.jclouds.aws.ec2.compute.suppliers.AWSEC2HardwareSupplier;
import org.jclouds.compute.config.BaseComputeServiceContextModule;
import org.jclouds.compute.domain.Image;
import org.jclouds.compute.extensions.ImageExtension;
import org.jclouds.compute.extensions.SecurityGroupExtension;
import org.jclouds.compute.options.TemplateOptions;
import org.jclouds.ec2.compute.config.EC2BindComputeStrategiesByClass;
import org.jclouds.ec2.compute.domain.RegionAndName;
@ -59,7 +54,6 @@ import org.jclouds.ec2.compute.suppliers.RegionAndNameToImageSupplier;
import org.jclouds.rest.AuthorizationException;
import org.jclouds.rest.suppliers.SetAndThrowAuthorizationExceptionSupplier;
import com.google.common.base.Optional;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.base.Throwables;
@ -107,9 +101,8 @@ public class AWSEC2ComputeServiceContextModule extends BaseComputeServiceContext
// duplicates EC2ComputeServiceContextModule; but that's easiest thing to do with guice; could extract to common util
// TODO: have a another look at this (Adrian)
@Override
protected Supplier<Set<? extends Image>> supplyNonParsingImageCache(
AtomicReference<AuthorizationException> authException, @Named(PROPERTY_SESSION_INTERVAL) long seconds,
final Supplier<Set<? extends Image>> imageSupplier, Injector injector) {
protected Supplier<Set<? extends Image>> supplyNonParsingImages(final Supplier<Set<? extends Image>> imageSupplier,
Injector injector) {
final Supplier<LoadingCache<RegionAndName, ? extends Image>> cache = injector.getInstance(Key
.get(new TypeLiteral<Supplier<LoadingCache<RegionAndName, ? extends Image>>>() {
}));
@ -170,14 +163,4 @@ public class AWSEC2ComputeServiceContextModule extends BaseComputeServiceContext
protected TemplateOptions provideTemplateOptions(Injector injector, TemplateOptions options) {
return options.as(EC2TemplateOptions.class).userData("#cloud-config\nrepo_upgrade: none\n".getBytes());
}
@Override
protected Optional<ImageExtension> provideImageExtension(Injector i) {
return Optional.of(i.getInstance(ImageExtension.class));
}
@Override
protected Optional<SecurityGroupExtension> provideSecurityGroupExtension(Injector i) {
return Optional.of(i.getInstance(SecurityGroupExtension.class));
}
}

View File

@ -21,7 +21,8 @@ import static com.google.common.collect.Iterables.transform;
import org.jclouds.aws.ec2.AWSEC2Api;
import org.jclouds.aws.util.AWSUtils;
import org.jclouds.compute.domain.Image;
import org.jclouds.compute.extensions.internal.BaseImageExtensionLiveTest;
import org.jclouds.compute.extensions.ImageExtension;
import org.jclouds.ec2.compute.extensions.EC2ImageExtensionLiveTest;
import org.jclouds.ec2.compute.functions.EC2ImageParser;
import org.jclouds.ec2.options.DescribeImagesOptions;
import org.jclouds.sshj.config.SshjSshClientModule;
@ -33,7 +34,7 @@ import com.google.inject.Module;
* Live test for aws-ec2 {@link ImageExtension} implementation
*/
@Test(groups = "live", singleThreaded = true, testName = "AWSEC2ImageExtensionLiveTest")
public class AWSEC2ImageExtensionLiveTest extends BaseImageExtensionLiveTest {
public class AWSEC2ImageExtensionLiveTest extends EC2ImageExtensionLiveTest {
public AWSEC2ImageExtensionLiveTest() {
provider = "aws-ec2";

View File

@ -25,26 +25,14 @@ import static org.jclouds.googlecomputeengine.config.GoogleComputeEngineProperti
import static org.jclouds.rest.config.BinderUtils.bindHttpApi;
import static org.jclouds.util.Predicates2.retry;
import javax.inject.Named;
import javax.inject.Singleton;
import java.net.URI;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import com.google.common.base.Function;
import com.google.common.base.Functions;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.base.Supplier;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableMap;
import com.google.inject.Injector;
import com.google.inject.Provides;
import com.google.inject.Scopes;
import com.google.inject.TypeLiteral;
import javax.inject.Named;
import javax.inject.Singleton;
import org.jclouds.collect.Memoized;
import org.jclouds.compute.ComputeService;
import org.jclouds.compute.ComputeServiceAdapter;
@ -53,8 +41,6 @@ import org.jclouds.compute.domain.Hardware;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.domain.OperatingSystem;
import org.jclouds.compute.domain.OsFamily;
import org.jclouds.compute.extensions.ImageExtension;
import org.jclouds.compute.extensions.SecurityGroupExtension;
import org.jclouds.compute.options.TemplateOptions;
import org.jclouds.domain.Location;
import org.jclouds.domain.LoginCredentials;
@ -80,6 +66,19 @@ import org.jclouds.googlecomputeengine.domain.Operation;
import org.jclouds.location.suppliers.ImplicitLocationSupplier;
import org.jclouds.location.suppliers.implicit.FirstZone;
import com.google.common.base.Function;
import com.google.common.base.Functions;
import com.google.common.base.Predicate;
import com.google.common.base.Supplier;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableMap;
import com.google.inject.Injector;
import com.google.inject.Provides;
import com.google.inject.Scopes;
import com.google.inject.TypeLiteral;
public final class GoogleComputeEngineServiceContextModule
extends ComputeServiceAdapterContextModule<Instance, MachineType, Image, Location> {
@ -197,15 +196,6 @@ public final class GoogleComputeEngineServiceContextModule
return CacheBuilder.newBuilder().build(in);
}
@Override protected Optional<ImageExtension> provideImageExtension(Injector i) {
return Optional.absent();
}
@Override protected Optional<SecurityGroupExtension> provideSecurityGroupExtension(Injector i) {
return Optional.absent();
}
private static final Map<Instance.Status, NodeMetadata.Status> toPortableNodeStatus =
ImmutableMap.<Instance.Status, NodeMetadata.Status>builder()
.put(Instance.Status.PROVISIONING, NodeMetadata.Status.PENDING)