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.Function;
import com.google.common.base.Objects; import com.google.common.base.Objects;
import com.google.common.base.Optional;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import com.google.common.base.Supplier; import com.google.common.base.Supplier;
import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheBuilder;
@ -277,14 +276,4 @@ public class CloudStackComputeServiceContextModule extends
NetworkType.ADVANCED, new AdvancedNetworkOptionsConverter(), NetworkType.ADVANCED, new AdvancedNetworkOptionsConverter(),
NetworkType.BASIC, new BasicNetworkOptionsConverter()); 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; package org.jclouds.ec2.compute.config;
import static com.google.common.collect.Iterables.toArray; 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 static org.jclouds.ec2.reference.EC2Constants.PROPERTY_EC2_AMI_OWNERS;
import java.util.Set; import java.util.Set;
@ -30,8 +29,6 @@ import javax.inject.Singleton;
import org.jclouds.compute.ComputeServiceContext; import org.jclouds.compute.ComputeServiceContext;
import org.jclouds.compute.config.BaseComputeServiceContextModule; import org.jclouds.compute.config.BaseComputeServiceContextModule;
import org.jclouds.compute.domain.Image; import org.jclouds.compute.domain.Image;
import org.jclouds.compute.extensions.ImageExtension;
import org.jclouds.compute.extensions.SecurityGroupExtension;
import org.jclouds.ec2.compute.EC2ComputeService; import org.jclouds.ec2.compute.EC2ComputeService;
import org.jclouds.ec2.compute.domain.RegionAndName; import org.jclouds.ec2.compute.domain.RegionAndName;
import org.jclouds.ec2.compute.loaders.RegionAndIdToImage; 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.AuthorizationException;
import org.jclouds.rest.suppliers.SetAndThrowAuthorizationExceptionSupplier; import org.jclouds.rest.suppliers.SetAndThrowAuthorizationExceptionSupplier;
import com.google.common.base.Optional;
import com.google.common.base.Splitter; import com.google.common.base.Splitter;
import com.google.common.base.Supplier; import com.google.common.base.Supplier;
import com.google.common.base.Suppliers; import com.google.common.base.Suppliers;
@ -78,9 +74,8 @@ public class EC2ComputeServiceContextModule extends BaseComputeServiceContextMod
} }
@Override @Override
protected Supplier<Set<? extends Image>> supplyNonParsingImageCache( protected Supplier<Set<? extends Image>> supplyNonParsingImages(final Supplier<Set<? extends Image>> imageSupplier,
AtomicReference<AuthorizationException> authException, @Named(PROPERTY_SESSION_INTERVAL) long seconds, Injector injector) {
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>>>() {})); final Supplier<LoadingCache<RegionAndName, ? extends Image>> cache = injector.getInstance(Key.get(new TypeLiteral<Supplier<LoadingCache<RegionAndName, ? extends Image>>>() {}));
return new Supplier<Set<? extends Image>>() { return new Supplier<Set<? extends Image>>() {
@Override @Override
@ -135,15 +130,5 @@ public class EC2ComputeServiceContextModule extends BaseComputeServiceContextMod
return new String[] {}; return new String[] {};
return toArray(Splitter.on(',').split(amiOwners), String.class); 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.TemplateBuilder;
import org.jclouds.compute.domain.internal.TemplateBuilderImpl; import org.jclouds.compute.domain.internal.TemplateBuilderImpl;
import org.jclouds.compute.options.TemplateOptions; 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.Location;
import org.jclouds.ec2.compute.domain.RegionAndName; import org.jclouds.ec2.compute.domain.RegionAndName;
import org.jclouds.util.Throwables2; import org.jclouds.util.Throwables2;
@ -50,11 +48,11 @@ public class EC2TemplateBuilderImpl extends TemplateBuilderImpl {
@Inject @Inject
protected EC2TemplateBuilderImpl(@Memoized Supplier<Set<? extends Location>> locations, 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, 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) { Supplier<LoadingCache<RegionAndName, ? extends Image>> imageMap) {
super(locations, images, sizes, defaultLocation, optionsProvider, defaultTemplateProvider, getImageStrategy); super(locations, images, sizes, defaultLocation, optionsProvider, defaultTemplateProvider);
this.lazyImageCache = imageMap; 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.functions.ImagesToRegionAndIdMap;
import org.jclouds.ec2.compute.internal.EC2TemplateBuilderImpl; import org.jclouds.ec2.compute.internal.EC2TemplateBuilderImpl;
import org.jclouds.ec2.domain.VirtualizationType; import org.jclouds.ec2.domain.VirtualizationType;
import org.jclouds.rest.AuthorizationException;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import com.google.common.base.Function; 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.cache.LoadingCache;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet; 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. * 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(), m1_small().build(), m1_xlarge().build(), m2_xlarge().build(), m2_2xlarge().build(),
m2_4xlarge().build(), g2_2xlarge().build(), HARDWARE_SUPPORTING_BOGUS)); m2_4xlarge().build(), g2_2xlarge().build(), HARDWARE_SUPPORTING_BOGUS));
return new EC2TemplateBuilderImpl(locations, new ImageCacheSupplier(images, 60), sizes, Suppliers.ofInstance(location), optionsProvider, return new EC2TemplateBuilderImpl(locations, new ImageCacheSupplier(images, 60,
templateBuilderProvider, getImageStrategy, imageCache) { 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; package org.jclouds.ec2.compute.extensions;
import static com.google.common.base.Preconditions.checkNotNull; 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.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.Template;
import org.jclouds.compute.domain.TemplateBuilderSpec; import org.jclouds.compute.domain.TemplateBuilderSpec;
import org.jclouds.compute.extensions.ImageExtension;
import org.jclouds.compute.extensions.internal.BaseImageExtensionLiveTest; import org.jclouds.compute.extensions.internal.BaseImageExtensionLiveTest;
import org.jclouds.compute.suppliers.ImageCacheSupplier;
import org.jclouds.sshj.config.SshjSshClientModule; import org.jclouds.sshj.config.SshjSshClientModule;
import org.testng.annotations.Test; 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.Module;
import com.google.inject.TypeLiteral;
/** /**
* Live test for ec2 {@link ImageExtension} implementation * Live test for ec2 {@link ImageExtension} implementation
@ -48,6 +63,37 @@ public class EC2ImageExtensionLiveTest extends BaseImageExtensionLiveTest {
return overrides; 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 @Override
public Template getNodeTemplate() { public Template getNodeTemplate() {
return view.getComputeService().templateBuilder().from(ebsTemplate).build(); 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.domain.RegionAndName;
import org.jclouds.ec2.compute.functions.ImagesToRegionAndIdMap; import org.jclouds.ec2.compute.functions.ImagesToRegionAndIdMap;
import org.jclouds.ec2.compute.options.EC2TemplateOptions; import org.jclouds.ec2.compute.options.EC2TemplateOptions;
import org.jclouds.rest.AuthorizationException;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import com.google.common.base.Functions; 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.cache.LoadingCache;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import com.google.common.util.concurrent.Atomics;
import com.google.inject.util.Providers;
@Test(groups = "unit", singleThreaded = true) @Test(groups = "unit", singleThreaded = true)
public class EC2TemplateBuilderImplTest extends TemplateBuilderImplTest { public class EC2TemplateBuilderImplTest extends TemplateBuilderImplTest {
@ -86,8 +89,10 @@ public class EC2TemplateBuilderImplTest extends TemplateBuilderImplTest {
ImagesToRegionAndIdMap.imagesToMap(images.get())))); ImagesToRegionAndIdMap.imagesToMap(images.get()))));
} }
return new EC2TemplateBuilderImpl(locations, new ImageCacheSupplier(images, 60), sizes, Suppliers.ofInstance(defaultLocation), return new EC2TemplateBuilderImpl(locations, new ImageCacheSupplier(images, 60,
optionsProvider, templateBuilderProvider, getImageStrategy, Suppliers.<LoadingCache<RegionAndName, ? extends Image>>ofInstance(imageMap)); Atomics.<AuthorizationException> newReference(), Providers.of(getImageStrategy)), sizes,
Suppliers.ofInstance(defaultLocation), optionsProvider, templateBuilderProvider,
Suppliers.<LoadingCache<RegionAndName, ? extends Image>> ofInstance(imageMap));
} }
@Override @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.annotations.VisibleForTesting;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import com.google.common.base.Supplier; import com.google.common.base.Supplier;
import com.google.common.base.Suppliers; 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() { protected final Map<org.jclouds.openstack.nova.v2_0.domain.Image.Status, Image.Status> toPortableImageStatus() {
return 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.options.TemplateOptions;
import org.jclouds.compute.reference.ComputeServiceConstants; import org.jclouds.compute.reference.ComputeServiceConstants;
import org.jclouds.compute.strategy.CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap; import org.jclouds.compute.strategy.CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap;
import org.jclouds.compute.strategy.GetImageStrategy;
import org.jclouds.compute.strategy.InitializeRunScriptOnNodeOrPlaceInBadMap; import org.jclouds.compute.strategy.InitializeRunScriptOnNodeOrPlaceInBadMap;
import org.jclouds.compute.suppliers.ImageCacheSupplier; import org.jclouds.compute.suppliers.ImageCacheSupplier;
import org.jclouds.config.ValueOfConfigurationKeyOrNull; 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.ImmutableMap;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.google.inject.AbstractModule; import com.google.inject.AbstractModule;
import com.google.inject.Binding;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Injector; import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Provides; import com.google.inject.Provides;
import com.google.inject.TypeLiteral; import com.google.inject.TypeLiteral;
import com.google.inject.assistedinject.FactoryModuleBuilder; import com.google.inject.assistedinject.FactoryModuleBuilder;
@ -112,9 +115,6 @@ public abstract class BaseComputeServiceContextModule extends AbstractModule {
}, InitializeRunScriptOnNodeOrPlaceInBadMap.class).build(InitializeRunScriptOnNodeOrPlaceInBadMap.Factory.class)); }, InitializeRunScriptOnNodeOrPlaceInBadMap.class).build(InitializeRunScriptOnNodeOrPlaceInBadMap.Factory.class));
install(new FactoryModuleBuilder().build(BlockUntilInitScriptStatusIsZeroThenReturnOutput.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() { protected void bindCredentialsOverriderFunction() {
@ -241,32 +241,24 @@ public abstract class BaseComputeServiceContextModule extends AbstractModule {
@Provides @Provides
@Singleton @Singleton
@Named("imageCache") @Memoized
protected final Supplier<Set<? extends Image>> supplyImageCache(AtomicReference<AuthorizationException> authException, @Named(PROPERTY_SESSION_INTERVAL) long seconds, protected final Supplier<Set<? extends Image>> supplyImageCache(
final Supplier<Set<? extends Image>> imageSupplier, Injector injector) { AtomicReference<AuthorizationException> authException, @Named(PROPERTY_SESSION_INTERVAL) long seconds,
if (shouldEagerlyParseImages(injector)) { final Supplier<Set<? extends Image>> imageSupplier, com.google.inject.Provider<GetImageStrategy> imageLoader, Injector injector) {
return supplyImageCache(authException, seconds, imageSupplier); Supplier<Set<? extends Image>> parsingImageSupplier = shouldEagerlyParseImages(injector) ? imageSupplier
} else { : supplyNonParsingImages(imageSupplier, injector);
return supplyNonParsingImageCache(authException, seconds, imageSupplier, injector); return new ImageCacheSupplier(parsingImageSupplier, seconds, authException, imageLoader);
}
} }
protected boolean shouldEagerlyParseImages(Injector injector) { protected boolean shouldEagerlyParseImages(Injector injector) {
return true; 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, protected Supplier<Set<? extends Image>> supplyNonParsingImages(final Supplier<Set<? extends Image>> imageSupplier, Injector injector) {
final Supplier<Set<? extends Image>> imageSupplier, Injector injector) { return imageSupplier;
return supplyImageCache(authException, seconds, imageSupplier);
} }
@Provides @Provides
@ -312,21 +304,15 @@ public abstract class BaseComputeServiceContextModule extends AbstractModule {
@Provides @Provides
@Singleton @Singleton
public final Optional<ImageExtension> guiceProvideImageExtension(Injector i) { public final Optional<ImageExtension> guiceProvideImageExtension(Injector i) {
return provideImageExtension(i); Binding<ImageExtension> binding = i.getExistingBinding(Key.get(ImageExtension.class));
} return binding == null ? Optional.<ImageExtension> absent() : Optional.of(binding.getProvider().get());
protected Optional<ImageExtension> provideImageExtension(Injector i) {
return Optional.absent();
} }
@Provides @Provides
@Singleton @Singleton
protected final Optional<SecurityGroupExtension> guiceProvideSecurityGroupExtension(Injector i) { protected final Optional<SecurityGroupExtension> guiceProvideSecurityGroupExtension(Injector i) {
return provideSecurityGroupExtension(i); Binding<SecurityGroupExtension> binding = i.getExistingBinding(Key.get(SecurityGroupExtension.class));
} return binding == null ? Optional.<SecurityGroupExtension> absent() : Optional.of(binding.getProvider().get());
protected Optional<SecurityGroupExtension> provideSecurityGroupExtension(Injector i) {
return Optional.absent();
} }

View File

@ -56,7 +56,6 @@ public interface TemplateBuilder {
* *
* @since 1.5 * @since 1.5
*/ */
@Beta
TemplateBuilder from(TemplateBuilderSpec spec); TemplateBuilder from(TemplateBuilderSpec spec);
/** /**
@ -66,7 +65,6 @@ public interface TemplateBuilder {
* @param spec a String in the format specified by {@link TemplateBuilderSpec} * @param spec a String in the format specified by {@link TemplateBuilderSpec}
* @since 1.5 * @since 1.5
*/ */
@Beta
TemplateBuilder from(String spec); TemplateBuilder from(String spec);
/** /**
@ -203,4 +201,18 @@ public interface TemplateBuilder {
*/ */
TemplateBuilder options(TemplateOptions options); 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("loginUser", new LoginUserParser())
.put("authenticateSudo", new AuthenticateSudoParser()) .put("authenticateSudo", new AuthenticateSudoParser())
.put("locationId", new LocationIdParser()) .put("locationId", new LocationIdParser())
.put("forceCacheReload", new ForceCacheReloadParser())
.build(); .build();
@VisibleForTesting @VisibleForTesting
@ -177,6 +178,8 @@ public class TemplateBuilderSpec {
Boolean authenticateSudo; Boolean authenticateSudo;
@VisibleForTesting @VisibleForTesting
String locationId; String locationId;
@VisibleForTesting
Boolean forceCacheReload;
/** Specification; used for toParseableString(). */ /** Specification; used for toParseableString(). */
// transient in case people using serializers don't want this to show up // transient in case people using serializers don't want this to show up
@ -279,6 +282,9 @@ public class TemplateBuilderSpec {
if (locationId != null) { if (locationId != null) {
builder.locationId(locationId); builder.locationId(locationId);
} }
if (forceCacheReload != null && forceCacheReload) {
builder.forceCacheReload();
}
return builder; return builder;
} }
@ -304,7 +310,8 @@ public class TemplateBuilderSpec {
@Override @Override
public int hashCode() { public int hashCode() {
return Objects.hashCode(hardwareId, minCores, minRam, hypervisorMatches, imageId, imageNameMatches, osFamily, 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 @Override
@ -322,7 +329,7 @@ public class TemplateBuilderSpec {
&& equal(osVersionMatches, that.osVersionMatches) && equal(os64Bit, that.os64Bit) && equal(osVersionMatches, that.osVersionMatches) && equal(os64Bit, that.os64Bit)
&& equal(osArchMatches, that.osArchMatches) && equal(osDescriptionMatches, that.osDescriptionMatches) && equal(osArchMatches, that.osArchMatches) && equal(osDescriptionMatches, that.osDescriptionMatches)
&& equal(loginUser, that.loginUser) && equal(authenticateSudo, that.authenticateSudo) && 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. */ /** 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() { public String getHardwareId() {
return hardwareId; return hardwareId;
} }
@ -622,4 +638,9 @@ public class TemplateBuilderSpec {
public String getSpecification() { public String getSpecification() {
return specification; return specification;
} }
public Boolean getForceCacheReload() {
return forceCacheReload;
}
} }

View File

@ -16,6 +16,7 @@
*/ */
package org.jclouds.compute.domain.internal; 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.checkNotNull;
import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Preconditions.checkState;
import static com.google.common.base.Predicates.and; 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.TemplateBuilder;
import org.jclouds.compute.domain.TemplateBuilderSpec; import org.jclouds.compute.domain.TemplateBuilderSpec;
import org.jclouds.compute.options.TemplateOptions; import org.jclouds.compute.options.TemplateOptions;
import org.jclouds.compute.predicates.ImagePredicates;
import org.jclouds.compute.reference.ComputeServiceConstants; import org.jclouds.compute.reference.ComputeServiceConstants;
import org.jclouds.compute.strategy.GetImageStrategy;
import org.jclouds.compute.suppliers.ImageCacheSupplier; import org.jclouds.compute.suppliers.ImageCacheSupplier;
import org.jclouds.domain.Location; import org.jclouds.domain.Location;
import org.jclouds.logging.Logger; import org.jclouds.logging.Logger;
@ -84,7 +85,6 @@ public class TemplateBuilderImpl implements TemplateBuilder {
protected final Supplier<Location> defaultLocation; protected final Supplier<Location> defaultLocation;
protected final Provider<TemplateOptions> optionsProvider; protected final Provider<TemplateOptions> optionsProvider;
protected final Provider<TemplateBuilder> defaultTemplateProvider; protected final Provider<TemplateBuilder> defaultTemplateProvider;
protected final GetImageStrategy getImageStrategy;
@VisibleForTesting @VisibleForTesting
protected Location location; protected Location location;
@ -128,19 +128,21 @@ public class TemplateBuilderImpl implements TemplateBuilder {
protected boolean fastest; protected boolean fastest;
@VisibleForTesting @VisibleForTesting
protected TemplateOptions options; protected TemplateOptions options;
@VisibleForTesting
protected Boolean forceCacheReload;
@Inject @Inject
protected TemplateBuilderImpl(@Memoized Supplier<Set<? extends Location>> locations, 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, 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.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.hardwares = checkNotNull(hardwares, "hardwares");
this.defaultLocation = checkNotNull(defaultLocation, "defaultLocation"); this.defaultLocation = checkNotNull(defaultLocation, "defaultLocation");
this.optionsProvider = checkNotNull(optionsProvider, "optionsProvider"); this.optionsProvider = checkNotNull(optionsProvider, "optionsProvider");
this.defaultTemplateProvider = checkNotNull(defaultTemplateProvider, "defaultTemplateProvider"); this.defaultTemplateProvider = checkNotNull(defaultTemplateProvider, "defaultTemplateProvider");
this.getImageStrategy = checkNotNull(getImageStrategy, "getImageStrategy");
} }
static Predicate<Hardware> supportsImagesPredicate(final Iterable<? extends Image> images) { 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>() { private final Predicate<OperatingSystem> osFamilyPredicate = new Predicate<OperatingSystem>() {
@Override @Override
@ -691,7 +673,7 @@ public class TemplateBuilderImpl implements TemplateBuilder {
Image image = null; Image image = null;
if (imageId != null) { if (imageId != null) {
image = findImageWithId(images); image = loadImageWithId(images);
if (currentLocationWiderThan(image.getLocation())) if (currentLocationWiderThan(image.getLocation()))
this.location = image.getLocation(); this.location = image.getLocation();
} }
@ -733,29 +715,18 @@ public class TemplateBuilderImpl implements TemplateBuilder {
return supportedImages; return supportedImages;
} }
private Image findImageWithId(Set<? extends Image> images) { private Image loadImageWithId(Iterable<? extends Image> images) {
// Try to find the image in the cache and fallback to the GetImageStrategy Optional<? extends Image> image = tryFind(images, ImagePredicates.idEquals(imageId));
// see https://issues.apache.org/jira/browse/JCLOUDS-570 if (!image.isPresent()) {
Optional<? extends Image> image = tryFind(images, idPredicate); image = this.images.get(imageId); // Load the image from the cache, and refresh if missing
if (image.isPresent()) { if (!image.isPresent()) {
throw throwNoSuchElementExceptionAfterLoggingImageIds(format("imageId(%s) not found", imageId), images);
}
}
fromImage(image.get());
return image.get(); 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 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;
}
private Hardware findHardwareWithId(Set<? extends Hardware> hardwaresToSearch) { private Hardware findHardwareWithId(Set<? extends Hardware> hardwaresToSearch) {
Hardware hardware; Hardware hardware;
// TODO: switch to GetHardwareStrategy in version 1.5 // TODO: switch to GetHardwareStrategy in version 1.5
@ -856,10 +827,8 @@ public class TemplateBuilderImpl implements TemplateBuilder {
logger.trace("<< matched images(%s)", transform(matchingImages, imageToId)); logger.trace("<< matched images(%s)", transform(matchingImages, imageToId));
return imageChooser().apply(matchingImages); return imageChooser().apply(matchingImages);
} catch (NoSuchElementException exception) { } catch (NoSuchElementException exception) {
throwNoSuchElementExceptionAfterLoggingImageIds(format("no image matched params: %s", toString()), throw throwNoSuchElementExceptionAfterLoggingImageIds(format("no image matched params: %s", toString()),
supportedImages); supportedImages);
assert false;
return null;
} }
} }
@ -885,7 +854,7 @@ public class TemplateBuilderImpl implements TemplateBuilder {
return maxes; return maxes;
} }
protected Set<? extends Image> getImages() { protected Set<? extends Image> getImages() {
return images.get(); return forceCacheReload != null && forceCacheReload ? images.rebuildCache() : images.get();
} }
private Predicate<Image> buildImagePredicate() { private Predicate<Image> buildImagePredicate() {
@ -1157,6 +1126,7 @@ public class TemplateBuilderImpl implements TemplateBuilder {
toString.add("os64Bit", os64Bit); toString.add("os64Bit", os64Bit);
toString.add("hardwareId", hardwareId); toString.add("hardwareId", hardwareId);
toString.add("hypervisor", hypervisor); toString.add("hypervisor", hypervisor);
toString.add("forceCacheReload", forceCacheReload);
return toString; return toString;
} }
@ -1176,4 +1146,11 @@ public class TemplateBuilderImpl implements TemplateBuilder {
return from(TemplateBuilderSpec.parse(spec)); 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.domain.TemplateBuilder;
import org.jclouds.compute.extensions.ImageExtension; import org.jclouds.compute.extensions.ImageExtension;
import org.jclouds.compute.extensions.SecurityGroupExtension; import org.jclouds.compute.extensions.SecurityGroupExtension;
import org.jclouds.compute.extensions.internal.DelegatingImageExtension;
import org.jclouds.compute.options.RunScriptOptions; import org.jclouds.compute.options.RunScriptOptions;
import org.jclouds.compute.options.TemplateOptions; import org.jclouds.compute.options.TemplateOptions;
import org.jclouds.compute.reference.ComputeServiceConstants; 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.ResumeNodeStrategy;
import org.jclouds.compute.strategy.RunScriptOnNodeAndAddToGoodMapOrPutExceptionIntoBadMap; import org.jclouds.compute.strategy.RunScriptOnNodeAndAddToGoodMapOrPutExceptionIntoBadMap;
import org.jclouds.compute.strategy.SuspendNodeStrategy; import org.jclouds.compute.strategy.SuspendNodeStrategy;
import org.jclouds.compute.suppliers.ImageCacheSupplier;
import org.jclouds.domain.Credentials; import org.jclouds.domain.Credentials;
import org.jclouds.domain.Location; import org.jclouds.domain.Location;
import org.jclouds.domain.LoginCredentials; import org.jclouds.domain.LoginCredentials;
@ -180,8 +182,13 @@ public class BaseComputeService implements ComputeService {
this.runScriptOnNodeFactory = checkNotNull(runScriptOnNodeFactory, "runScriptOnNodeFactory"); this.runScriptOnNodeFactory = checkNotNull(runScriptOnNodeFactory, "runScriptOnNodeFactory");
this.persistNodeCredentials = checkNotNull(persistNodeCredentials, "persistNodeCredentials"); this.persistNodeCredentials = checkNotNull(persistNodeCredentials, "persistNodeCredentials");
this.userExecutor = checkNotNull(userExecutor, "userExecutor"); this.userExecutor = checkNotNull(userExecutor, "userExecutor");
this.imageExtension = checkNotNull(imageExtension, "imageExtension");
this.securityGroupExtension = checkNotNull(securityGroupExtension, "securityGroupExtension"); 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; package org.jclouds.compute.stub.config;
import org.jclouds.compute.config.JCloudsNativeComputeServiceAdapterContextModule; import org.jclouds.compute.config.JCloudsNativeComputeServiceAdapterContextModule;
import org.jclouds.compute.extensions.SecurityGroupExtension;
import org.jclouds.concurrent.SingleThreaded; import org.jclouds.concurrent.SingleThreaded;
import com.google.common.base.Optional;
import com.google.inject.Injector;
@SingleThreaded @SingleThreaded
public class StubComputeServiceContextModule extends JCloudsNativeComputeServiceAdapterContextModule { public class StubComputeServiceContextModule extends JCloudsNativeComputeServiceAdapterContextModule {
@ -36,9 +32,4 @@ public class StubComputeServiceContextModule extends JCloudsNativeComputeService
super.configure(); 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; package org.jclouds.compute.suppliers;
import static com.google.common.base.Preconditions.checkNotNull; 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.Set;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import javax.annotation.Resource;
import javax.inject.Named; import javax.inject.Named;
import javax.inject.Singleton;
import org.jclouds.compute.domain.Image; import org.jclouds.compute.domain.Image;
import org.jclouds.compute.reference.ComputeServiceConstants;
import org.jclouds.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.base.Supplier;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableSet; 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> * <p>
* This is a wrapper for the memoized image supplier (the actual image cache), to provide a way to register new images as * The memoized <code>Supplier<Set<? extends Image>></code> is a static data
* needed. Once a new image is created by the {@link org.jclouds.compute.extensions.ImageExtension}, or discovered by * structure that can't be properly modified at runtime. This class is a wrapper
* other means (see https://issues.apache.org/jira/browse/JCLOUDS-570) this supplier will allow the image to be appended * for the image supplier to provide a way to register new images as needed.
* to the cached list, so it can be properly used normally. * 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 @Beta
public class ImageCacheSupplier implements Supplier<Set<? extends Image>> { 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;
private final Cache<String, Image> uncachedImages; /**
* 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;
@Inject /**
public ImageCacheSupplier(@Named("imageCache") Supplier<Set<? extends Image>> imageCache, * The actual image cache. It acts as a view over the memoized image supplier
@Named(PROPERTY_SESSION_INTERVAL) long sessionIntervalSeconds) { * and allows to add and remove images at runtime.
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 private final LoadingCache<String, Image> imageCache;
// uncached set to be regenerated when the original cache is also regenerated.
this.uncachedImages = CacheBuilder.newBuilder().expireAfterWrite(sessionIntervalSeconds, TimeUnit.SECONDS) @Resource
.build(); @Named(ComputeServiceConstants.COMPUTE_LOGGER)
protected Logger logger = Logger.NULL;
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 @Override
public Set<? extends Image> get() { 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. * Registers a new image in the image cache.
* <p> * <p>
* This method should be called to register new images into the image cache, when some image that is known to exist * This method should be called to register new images into the image cache
* in the provider is still not cached. For example, this can happen when an image is created after the image cache * when some image that is known to exist in the provider is still not
* has been populated for the first time. * cached. For example, this can happen when an image is created after the
* image cache has been populated for the first time.
* <p> * <p>
* Note that this method does not check if the image is already cached, to avoid loading all images if the image * Note that this method does not check if the image is already cached, to
* cache is still not populated. * avoid loading all images if the image cache is still not populated.
* *
* @param image The image to be registered to the cache. * @param image The image to be registered to the cache.
*/ */
public void registerImage(Image image) { public void registerImage(Image image) {
checkNotNull(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.loginUser);
assertNull(spec.authenticateSudo); assertNull(spec.authenticateSudo);
assertNull(spec.locationId); assertNull(spec.locationId);
assertNull(spec.forceCacheReload);
assertTemplateBuilderEquivalence(templateBuilders.get(), templateBuilders.get().from(spec)); assertTemplateBuilderEquivalence(templateBuilders.get(), templateBuilders.get().from(spec));
} }
@ -81,6 +82,7 @@ public class TemplateBuilderSpecTest {
assertNull(spec.loginUser); assertNull(spec.loginUser);
assertNull(spec.authenticateSudo); assertNull(spec.authenticateSudo);
assertNull(spec.locationId); assertNull(spec.locationId);
assertNull(spec.forceCacheReload);
assertTemplateBuilderEquivalence(templateBuilders.get().hardwareId("m1.small"), assertTemplateBuilderEquivalence(templateBuilders.get().hardwareId("m1.small"),
templateBuilders.get().from(spec)); templateBuilders.get().from(spec));
} }
@ -132,6 +134,7 @@ public class TemplateBuilderSpecTest {
assertNull(spec.loginUser); assertNull(spec.loginUser);
assertNull(spec.authenticateSudo); assertNull(spec.authenticateSudo);
assertNull(spec.locationId); assertNull(spec.locationId);
assertNull(spec.forceCacheReload);
assertTemplateBuilderEquivalence(templateBuilders.get().minCores(32), templateBuilders.get().from(spec)); assertTemplateBuilderEquivalence(templateBuilders.get().minCores(32), templateBuilders.get().from(spec));
} }
@ -160,6 +163,7 @@ public class TemplateBuilderSpecTest {
assertNull(spec.loginUser); assertNull(spec.loginUser);
assertNull(spec.authenticateSudo); assertNull(spec.authenticateSudo);
assertNull(spec.locationId); assertNull(spec.locationId);
assertNull(spec.forceCacheReload);
assertTemplateBuilderEquivalence(templateBuilders.get().minRam(10), templateBuilders.get().from(spec)); assertTemplateBuilderEquivalence(templateBuilders.get().minRam(10), templateBuilders.get().from(spec));
} }
@ -180,6 +184,7 @@ public class TemplateBuilderSpecTest {
assertNull(spec.loginUser); assertNull(spec.loginUser);
assertNull(spec.authenticateSudo); assertNull(spec.authenticateSudo);
assertNull(spec.locationId); assertNull(spec.locationId);
assertNull(spec.forceCacheReload);
assertTemplateBuilderEquivalence(templateBuilders.get().minRam(10), templateBuilders.get().from(spec)); assertTemplateBuilderEquivalence(templateBuilders.get().minRam(10), templateBuilders.get().from(spec));
} }
@ -216,6 +221,7 @@ public class TemplateBuilderSpecTest {
assertNull(spec.loginUser); assertNull(spec.loginUser);
assertNull(spec.authenticateSudo); assertNull(spec.authenticateSudo);
assertNull(spec.locationId); assertNull(spec.locationId);
assertNull(spec.forceCacheReload);
assertTemplateBuilderEquivalence(templateBuilders.get().hypervisorMatches("OpenVZ"), assertTemplateBuilderEquivalence(templateBuilders.get().hypervisorMatches("OpenVZ"),
templateBuilders.get().from(spec)); templateBuilders.get().from(spec));
} }
@ -246,6 +252,7 @@ public class TemplateBuilderSpecTest {
assertNull(spec.loginUser); assertNull(spec.loginUser);
assertNull(spec.authenticateSudo); assertNull(spec.authenticateSudo);
assertNull(spec.locationId); assertNull(spec.locationId);
assertNull(spec.forceCacheReload);
assertTemplateBuilderEquivalence(templateBuilders.get().imageId("us-east-1/ami-fffffff"), assertTemplateBuilderEquivalence(templateBuilders.get().imageId("us-east-1/ami-fffffff"),
templateBuilders.get().from(spec)); templateBuilders.get().from(spec));
} }
@ -315,6 +322,7 @@ public class TemplateBuilderSpecTest {
assertNull(spec.loginUser); assertNull(spec.loginUser);
assertNull(spec.authenticateSudo); assertNull(spec.authenticateSudo);
assertNull(spec.locationId); assertNull(spec.locationId);
assertNull(spec.forceCacheReload);
assertTemplateBuilderEquivalence(templateBuilders.get().imageNameMatches(".*w/ None.*"), assertTemplateBuilderEquivalence(templateBuilders.get().imageNameMatches(".*w/ None.*"),
templateBuilders.get().from(spec)); templateBuilders.get().from(spec));
} }
@ -345,6 +353,7 @@ public class TemplateBuilderSpecTest {
assertNull(spec.loginUser); assertNull(spec.loginUser);
assertNull(spec.authenticateSudo); assertNull(spec.authenticateSudo);
assertNull(spec.locationId); assertNull(spec.locationId);
assertNull(spec.forceCacheReload);
assertTemplateBuilderEquivalence(templateBuilders.get().osFamily(OsFamily.UBUNTU), assertTemplateBuilderEquivalence(templateBuilders.get().osFamily(OsFamily.UBUNTU),
templateBuilders.get().from(spec)); templateBuilders.get().from(spec));
} }
@ -374,6 +383,7 @@ public class TemplateBuilderSpecTest {
assertNull(spec.loginUser); assertNull(spec.loginUser);
assertNull(spec.authenticateSudo); assertNull(spec.authenticateSudo);
assertNull(spec.locationId); assertNull(spec.locationId);
assertNull(spec.forceCacheReload);
assertTemplateBuilderEquivalence(templateBuilders.get().osVersionMatches(".*[Aa]utomated SSH Access.*"), assertTemplateBuilderEquivalence(templateBuilders.get().osVersionMatches(".*[Aa]utomated SSH Access.*"),
templateBuilders.get().from(spec)); templateBuilders.get().from(spec));
} }
@ -403,6 +413,7 @@ public class TemplateBuilderSpecTest {
assertNull(spec.loginUser); assertNull(spec.loginUser);
assertNull(spec.authenticateSudo); assertNull(spec.authenticateSudo);
assertNull(spec.locationId); assertNull(spec.locationId);
assertNull(spec.forceCacheReload);
assertTemplateBuilderEquivalence(templateBuilders.get().os64Bit(true), assertTemplateBuilderEquivalence(templateBuilders.get().os64Bit(true),
templateBuilders.get().from(spec)); templateBuilders.get().from(spec));
} }
@ -432,6 +443,7 @@ public class TemplateBuilderSpecTest {
assertNull(spec.loginUser); assertNull(spec.loginUser);
assertNull(spec.authenticateSudo); assertNull(spec.authenticateSudo);
assertNull(spec.locationId); assertNull(spec.locationId);
assertNull(spec.forceCacheReload);
assertTemplateBuilderEquivalence(templateBuilders.get().osArchMatches("x86"), assertTemplateBuilderEquivalence(templateBuilders.get().osArchMatches("x86"),
templateBuilders.get().from(spec)); templateBuilders.get().from(spec));
} }
@ -461,6 +473,7 @@ public class TemplateBuilderSpecTest {
assertNull(spec.loginUser); assertNull(spec.loginUser);
assertNull(spec.authenticateSudo); assertNull(spec.authenticateSudo);
assertNull(spec.locationId); assertNull(spec.locationId);
assertNull(spec.forceCacheReload);
assertTemplateBuilderEquivalence(templateBuilders.get().osDescriptionMatches("^((?!MGC).)*$"), assertTemplateBuilderEquivalence(templateBuilders.get().osDescriptionMatches("^((?!MGC).)*$"),
templateBuilders.get().from(spec)); templateBuilders.get().from(spec));
} }
@ -491,6 +504,7 @@ public class TemplateBuilderSpecTest {
assertEquals(spec.loginUser, "ubuntu"); assertEquals(spec.loginUser, "ubuntu");
assertNull(spec.authenticateSudo); assertNull(spec.authenticateSudo);
assertNull(spec.locationId); assertNull(spec.locationId);
assertNull(spec.forceCacheReload);
assertTemplateBuilderEquivalence( assertTemplateBuilderEquivalence(
templateBuilders.get().options(overrideLoginUser("ubuntu")), templateBuilders templateBuilders.get().options(overrideLoginUser("ubuntu")), templateBuilders
.get().from(spec)); .get().from(spec));
@ -522,6 +536,7 @@ public class TemplateBuilderSpecTest {
assertEquals(spec.loginUser, "root:toor"); assertEquals(spec.loginUser, "root:toor");
assertNull(spec.authenticateSudo); assertNull(spec.authenticateSudo);
assertNull(spec.locationId); assertNull(spec.locationId);
assertNull(spec.forceCacheReload);
assertTemplateBuilderEquivalence( assertTemplateBuilderEquivalence(
templateBuilders.get().options( templateBuilders.get().options(
overrideLoginCredentials(LoginCredentials.builder().user("root").password("toor").build())), overrideLoginCredentials(LoginCredentials.builder().user("root").password("toor").build())),
@ -554,6 +569,7 @@ public class TemplateBuilderSpecTest {
assertEquals(spec.loginUser, "root:toor"); assertEquals(spec.loginUser, "root:toor");
assertEquals(spec.authenticateSudo.booleanValue(), true); assertEquals(spec.authenticateSudo.booleanValue(), true);
assertNull(spec.locationId); assertNull(spec.locationId);
assertNull(spec.forceCacheReload);
assertTemplateBuilderEquivalence( assertTemplateBuilderEquivalence(
templateBuilders.get().options( templateBuilders.get().options(
overrideLoginCredentials(LoginCredentials.builder().user("root").password("toor") overrideLoginCredentials(LoginCredentials.builder().user("root").password("toor")
@ -569,6 +585,36 @@ public class TemplateBuilderSpecTest {
} }
} }
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() { public void testParse_locationId() {
TemplateBuilderSpec spec = parse("locationId=stub"); TemplateBuilderSpec spec = parse("locationId=stub");
assertNull(spec.hardwareId); assertNull(spec.hardwareId);
@ -586,6 +632,7 @@ public class TemplateBuilderSpecTest {
assertNull(spec.loginUser); assertNull(spec.loginUser);
assertNull(spec.authenticateSudo); assertNull(spec.authenticateSudo);
assertEquals(spec.locationId, "stub"); assertEquals(spec.locationId, "stub");
assertNull(spec.forceCacheReload);
assertTemplateBuilderEquivalence(templateBuilders.get().locationId("stub"), assertTemplateBuilderEquivalence(templateBuilders.get().locationId("stub"),
templateBuilders.get().from(spec)); templateBuilders.get().from(spec));
} }
@ -616,6 +663,7 @@ public class TemplateBuilderSpecTest {
assertNull(spec.loginUser); assertNull(spec.loginUser);
assertNull(spec.authenticateSudo); assertNull(spec.authenticateSudo);
assertNull(spec.locationId); assertNull(spec.locationId);
assertNull(spec.forceCacheReload);
TemplateBuilder expected = templateBuilders.get().osVersionMatches("1[012].[01][04]").imageNameMatches(".*w/ None.*").osFamily(OsFamily.UBUNTU); TemplateBuilder expected = templateBuilders.get().osVersionMatches("1[012].[01][04]").imageNameMatches(".*w/ None.*").osFamily(OsFamily.UBUNTU);
assertTemplateBuilderEquivalence(expected, templateBuilders.get().from(spec)); assertTemplateBuilderEquivalence(expected, templateBuilders.get().from(spec));
} }
@ -637,6 +685,7 @@ public class TemplateBuilderSpecTest {
assertNull(spec.loginUser); assertNull(spec.loginUser);
assertNull(spec.authenticateSudo); assertNull(spec.authenticateSudo);
assertNull(spec.locationId); assertNull(spec.locationId);
assertNull(spec.forceCacheReload);
TemplateBuilder expected = templateBuilders.get().minRam(10).osFamily(OsFamily.UBUNTU); TemplateBuilder expected = templateBuilders.get().minRam(10).osFamily(OsFamily.UBUNTU);
assertTemplateBuilderEquivalence(expected, templateBuilders.get().from(spec)); 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.Location;
import org.jclouds.domain.LocationBuilder; import org.jclouds.domain.LocationBuilder;
import org.jclouds.domain.LocationScope; import org.jclouds.domain.LocationScope;
import org.jclouds.rest.AuthorizationException;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import com.google.common.base.Function; 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.ImmutableSet;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Ordering; 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") @Test(groups = "unit", singleThreaded = true, testName = "TemplateBuilderImplTest")
public class TemplateBuilderImplTest { public class TemplateBuilderImplTest {
@ -468,8 +471,10 @@ public class TemplateBuilderImplTest {
Supplier<Set<? extends Image>> images, Supplier<Set<? extends Hardware>> hardwares, Supplier<Set<? extends Image>> images, Supplier<Set<? extends Hardware>> hardwares,
Location defaultLocation, Provider<TemplateOptions> optionsProvider, Location defaultLocation, Provider<TemplateOptions> optionsProvider,
Provider<TemplateBuilder> templateBuilderProvider, GetImageStrategy getImageStrategy) { Provider<TemplateBuilder> templateBuilderProvider, GetImageStrategy getImageStrategy) {
TemplateBuilderImpl template = new TemplateBuilderImpl(locations, new ImageCacheSupplier(images, 60), hardwares, Suppliers TemplateBuilderImpl template = new TemplateBuilderImpl(locations, new ImageCacheSupplier(images, 60,
.ofInstance(defaultLocation), optionsProvider, templateBuilderProvider, getImageStrategy); Atomics.<AuthorizationException> newReference(), Providers.of(getImageStrategy)), hardwares,
Suppliers.ofInstance(defaultLocation),
optionsProvider, templateBuilderProvider);
return template; 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.compute.predicates.NodePredicates.inGroup;
import static org.jclouds.util.Predicates2.retry; import static org.jclouds.util.Predicates2.retry;
import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue; import static org.testng.Assert.assertTrue;
import java.util.NoSuchElementException;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import javax.annotation.Resource; import javax.annotation.Resource;
@ -90,46 +92,40 @@ public abstract class BaseImageExtensionLiveTest extends BaseComputeServiceConte
@Test(groups = { "integration", "live" }, singleThreaded = true) @Test(groups = { "integration", "live" }, singleThreaded = true)
public void testCreateImage() throws RunNodesException, InterruptedException, ExecutionException { public void testCreateImage() throws RunNodesException, InterruptedException, ExecutionException {
ComputeService computeService = view.getComputeService(); ComputeService computeService = view.getComputeService();
Optional<ImageExtension> imageExtension = computeService.getImageExtension(); Optional<ImageExtension> imageExtension = computeService.getImageExtension();
assertTrue(imageExtension.isPresent(), "image extension was not present"); assertTrue(imageExtension.isPresent(), "image extension was not present");
Template template = getNodeTemplate(); Template template = getNodeTemplate();
NodeMetadata node = Iterables.getOnlyElement(computeService.createNodesInGroup(imageGroup, 1, template)); NodeMetadata node = Iterables.getOnlyElement(computeService.createNodesInGroup(imageGroup, 1, template));
checkReachable(node); checkReachable(node);
logger.info("Creating image from node %s, started with template: %s", node, template); logger.info("Creating image from node %s, started with template: %s", node, template);
ImageTemplate newImageTemplate = imageExtension.get().buildImageTemplateFromNode(imageGroup, ImageTemplate newImageTemplate = imageExtension.get().buildImageTemplateFromNode(imageGroup,
node.getId()); node.getId());
Image image = imageExtension.get().createImage(newImageTemplate).get(); Image image = imageExtension.get().createImage(newImageTemplate).get();
logger.info("Image created: %s", image); logger.info("Image created: %s", image);
assertEquals(imageGroup, image.getName()); assertEquals(imageGroup, image.getName());
imageId = image.getId(); imageId = image.getId();
computeService.destroyNode(node.getId()); computeService.destroyNode(node.getId());
Optional<? extends Image> optImage = getImage(); Optional<? extends Image> optImage = getImage();
assertTrue(optImage.isPresent()); 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") @Test(groups = { "integration", "live" }, singleThreaded = true, dependsOnMethods = "testCreateImage")
public void testSpawnNodeFromImage() throws RunNodesException { public void testSpawnNodeFromImage() throws RunNodesException {
ComputeService computeService = view.getComputeService(); ComputeService computeService = view.getComputeService();
Optional<? extends Image> optImage = getImage(); Optional<? extends Image> optImage = getImage();
assertTrue(optImage.isPresent()); assertTrue(optImage.isPresent());
NodeMetadata node = Iterables.getOnlyElement(computeService.createNodesInGroup(imageGroup, 1, view NodeMetadata node = Iterables.getOnlyElement(computeService.createNodesInGroup(imageGroup, 1, view
@ -138,29 +134,31 @@ public abstract class BaseImageExtensionLiveTest extends BaseComputeServiceConte
.templateBuilder().imageId(optImage.get().getId()).fromImage(optImage.get()).build())); .templateBuilder().imageId(optImage.get().getId()).fromImage(optImage.get()).build()));
checkReachable(node); checkReachable(node);
view.getComputeService().destroyNode(node.getId()); view.getComputeService().destroyNode(node.getId());
} }
@Test(groups = { "integration", "live" }, singleThreaded = true, dependsOnMethods = { "testCreateImage", @Test(groups = { "integration", "live" }, singleThreaded = true, dependsOnMethods = { "testCreateImage",
"testSpawnNodeFromImage" }) "testSpawnNodeFromImage", "testImageIsCachedAfterBeingCreated" })
public void testDeleteImage() { public void testDeleteImage() {
ComputeService computeService = view.getComputeService(); ComputeService computeService = view.getComputeService();
Optional<ImageExtension> imageExtension = computeService.getImageExtension(); Optional<ImageExtension> imageExtension = computeService.getImageExtension();
assertTrue(imageExtension.isPresent(), "image extension was not present"); assertTrue(imageExtension.isPresent(), "image extension was not present");
Optional<? extends Image> optImage = getImage(); Optional<? extends Image> optImage = getImage();
assertTrue(optImage.isPresent()); assertTrue(optImage.isPresent());
Image image = optImage.get(); Image image = optImage.get();
assertTrue(imageExtension.get().deleteImage(image.getId())); 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() { private Optional<? extends Image> getImage() {
return Optional.fromNullable(view.getComputeService().getImage(imageId)); return Optional.fromNullable(view.getComputeService().getImage(imageId));
} }
@ -179,6 +177,15 @@ public abstract class BaseImageExtensionLiveTest extends BaseComputeServiceConte
}, getSpawnNodeMaxWait(), 1l, SECONDS).apply(client)); }, 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" }) @AfterClass(groups = { "integration", "live" })
@Override @Override
protected void tearDownContext() { protected void tearDownContext() {

View File

@ -16,20 +16,31 @@
*/ */
package org.jclouds.compute.suppliers; 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.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;
import java.util.Set; import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.jclouds.compute.domain.Image; import org.jclouds.compute.domain.Image;
import org.jclouds.compute.domain.ImageBuilder; import org.jclouds.compute.domain.ImageBuilder;
import org.jclouds.compute.domain.OperatingSystem; import org.jclouds.compute.domain.OperatingSystem;
import org.jclouds.compute.strategy.GetImageStrategy;
import org.jclouds.domain.Location; import org.jclouds.domain.Location;
import org.jclouds.domain.LocationBuilder; import org.jclouds.domain.LocationBuilder;
import org.jclouds.domain.LocationScope; import org.jclouds.domain.LocationScope;
import org.jclouds.rest.AuthorizationException;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import com.google.common.base.Optional;
import com.google.common.base.Suppliers; import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableSet; 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. * Unit tests for the {@link ImageCacheSupplier} class.
@ -49,15 +60,25 @@ public class ImageCacheSupplierTest {
private Set<? extends Image> images = ImmutableSet.of(image); 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) @Test(expectedExceptions = NullPointerException.class)
public void testRegisterNullImageIsNotAllowed() { 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); imageCache.registerImage(null);
} }
@Test @Test
public void testRegisterImageIgnoresDuplicates() { 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); assertEquals(imageCache.get().size(), 1);
imageCache.registerImage(image); imageCache.registerImage(image);
@ -67,11 +88,63 @@ public class ImageCacheSupplierTest {
@Test @Test
public void testRegisterNewImage() { 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); assertEquals(imageCache.get().size(), 1);
imageCache.registerImage(ImageBuilder.fromImage(image).id("newimage").build()); imageCache.registerImage(ImageBuilder.fromImage(image).id("newimage").build());
assertEquals(imageCache.get().size(), 2); 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

@ -58,11 +58,13 @@ public class MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<T> ext
private final Supplier<V> delegate; private final Supplier<V> delegate;
private final AtomicReference<AuthorizationException> authException; private final AtomicReference<AuthorizationException> authException;
private final ValueLoadedCallback<V> valueLoadedCallback;
public SetAndThrowAuthorizationExceptionSupplierBackedLoader(Supplier<V> delegate, public SetAndThrowAuthorizationExceptionSupplierBackedLoader(Supplier<V> delegate,
AtomicReference<AuthorizationException> authException) { AtomicReference<AuthorizationException> authException, ValueLoadedCallback<V> valueLoadedCallback) {
this.delegate = checkNotNull(delegate, "delegate"); this.delegate = checkNotNull(delegate, "delegate");
this.authException = checkNotNull(authException, "authException"); this.authException = checkNotNull(authException, "authException");
this.valueLoadedCallback = checkNotNull(valueLoadedCallback, "valueLoadedCallback");
} }
@Override @Override
@ -70,7 +72,9 @@ public class MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<T> ext
if (authException.get() != null) if (authException.get() != null)
throw authException.get(); throw authException.get();
try { try {
return Optional.fromNullable(delegate.get()); Optional<V> value = Optional.fromNullable(delegate.get());
valueLoadedCallback.valueLoaded(value);
return value;
} catch (Exception e) { } catch (Exception e) {
AuthorizationException aex = getFirstThrowableOfType(e, AuthorizationException.class); AuthorizationException aex = getFirstThrowableOfType(e, AuthorizationException.class);
if (aex != null) { if (aex != null) {
@ -85,7 +89,24 @@ public class MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<T> ext
public String toString() { public String toString() {
return Objects.toStringHelper(this).add("delegate", delegate).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; private final Supplier<T> delegate;
@ -96,16 +117,26 @@ public class MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<T> ext
public static <T> MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<T> create( public static <T> MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<T> create(
AtomicReference<AuthorizationException> authException, Supplier<T> delegate, long duration, TimeUnit unit) { AtomicReference<AuthorizationException> authException, Supplier<T> delegate, long duration, TimeUnit unit) {
return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<T>(authException, delegate, duration, 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, 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.delegate = delegate;
this.duration = duration; this.duration = duration;
this.unit = unit; this.unit = unit;
this.cache = CacheBuilder.newBuilder().expireAfterWrite(duration, unit) this.cache = CacheBuilder.newBuilder().expireAfterWrite(duration, unit)
.build(new SetAndThrowAuthorizationExceptionSupplierBackedLoader<T>(delegate, authException)); .build(new SetAndThrowAuthorizationExceptionSupplierBackedLoader<T>(delegate, authException, valueLoadedCallback));
} }
@Override @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 static org.testng.Assert.assertEquals;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import org.jclouds.rest.AuthorizationException; import org.jclouds.rest.AuthorizationException;
import org.jclouds.rest.suppliers.MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.SetAndThrowAuthorizationExceptionSupplierBackedLoader; import org.jclouds.rest.suppliers.MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.SetAndThrowAuthorizationExceptionSupplierBackedLoader;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import com.google.common.base.Optional;
import com.google.common.base.Supplier; 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.UncheckedExecutionException;
import com.google.common.util.concurrent.Uninterruptibles;
@Test(groups = "unit", testName = "MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplierTest") @Test(groups = "unit", testName = "MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplierTest")
public class MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplierTest { public class MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplierTest {
@Test @Test
public void testLoaderNormal() { public void testLoaderNormal() {
AtomicReference<AuthorizationException> authException = newReference(); AtomicReference<AuthorizationException> authException = newReference();
assertEquals(new SetAndThrowAuthorizationExceptionSupplierBackedLoader<String>(ofInstance("foo"), assertEquals(new SetAndThrowAuthorizationExceptionSupplierBackedLoader<String>(ofInstance("foo"), authException, new ValueLoadedCallback.NoOpCallback<String>()).load("KEY").get(), "foo");
authException).load("KEY").get(), "foo");
assertEquals(authException.get(), null); assertEquals(authException.get(), null);
} }
@ -48,7 +52,7 @@ public class MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplierTest {
public String get() { public String get() {
throw new AuthorizationException(); throw new AuthorizationException();
} }
}, authException).load("KEY"); }, authException, new ValueLoadedCallback.NoOpCallback<String>()).load("KEY");
} finally { } finally {
assertEquals(authException.get().getClass(), AuthorizationException.class); assertEquals(authException.get().getClass(), AuthorizationException.class);
} }
@ -62,7 +66,7 @@ public class MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplierTest {
public String get() { public String get() {
throw new RuntimeException(new ExecutionException(new AuthorizationException())); throw new RuntimeException(new ExecutionException(new AuthorizationException()));
} }
}, authException).load("KEY"); }, authException, new ValueLoadedCallback.NoOpCallback<String>()).load("KEY");
} finally { } finally {
assertEquals(authException.get().getClass(), AuthorizationException.class); assertEquals(authException.get().getClass(), AuthorizationException.class);
} }
@ -76,7 +80,7 @@ public class MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplierTest {
public String get() { public String get() {
throw new UncheckedExecutionException(new AuthorizationException()); throw new UncheckedExecutionException(new AuthorizationException());
} }
}, authException).load("KEY"); }, authException, new ValueLoadedCallback.NoOpCallback<String>()).load("KEY");
} finally { } finally {
assertEquals(authException.get().getClass(), AuthorizationException.class); assertEquals(authException.get().getClass(), AuthorizationException.class);
} }
@ -90,9 +94,43 @@ public class MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplierTest {
public String get() { public String get() {
throw new RuntimeException(new IllegalArgumentException("foo")); throw new RuntimeException(new IllegalArgumentException("foo"));
} }
}, authException).load("KEY"); }, authException, new ValueLoadedCallback.NoOpCallback<String>()).load("KEY");
} finally { } finally {
assertEquals(authException.get().getClass(), RuntimeException.class); 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.Image;
import org.jclouds.compute.domain.TemplateBuilder; import org.jclouds.compute.domain.TemplateBuilder;
import org.jclouds.compute.options.TemplateOptions; import org.jclouds.compute.options.TemplateOptions;
import org.jclouds.compute.strategy.GetImageStrategy;
import org.jclouds.compute.suppliers.ImageCacheSupplier;
import org.jclouds.domain.Location; import org.jclouds.domain.Location;
import org.jclouds.ec2.compute.domain.RegionAndName; import org.jclouds.ec2.compute.domain.RegionAndName;
import org.jclouds.ec2.compute.internal.EC2TemplateBuilderImpl; import org.jclouds.ec2.compute.internal.EC2TemplateBuilderImpl;
@ -40,11 +38,11 @@ public class AWSEC2TemplateBuilderImpl extends EC2TemplateBuilderImpl {
@Inject @Inject
protected AWSEC2TemplateBuilderImpl(@Memoized Supplier<Set<? extends Location>> locations, 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, 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) { 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; package org.jclouds.aws.ec2.compute.config;
import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import javax.inject.Named;
import javax.inject.Singleton; import javax.inject.Singleton;
import org.jclouds.aws.ec2.compute.AWSEC2TemplateBuilderImpl; 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.aws.ec2.compute.suppliers.AWSEC2HardwareSupplier;
import org.jclouds.compute.config.BaseComputeServiceContextModule; import org.jclouds.compute.config.BaseComputeServiceContextModule;
import org.jclouds.compute.domain.Image; import org.jclouds.compute.domain.Image;
import org.jclouds.compute.extensions.ImageExtension;
import org.jclouds.compute.extensions.SecurityGroupExtension;
import org.jclouds.compute.options.TemplateOptions; import org.jclouds.compute.options.TemplateOptions;
import org.jclouds.ec2.compute.config.EC2BindComputeStrategiesByClass; import org.jclouds.ec2.compute.config.EC2BindComputeStrategiesByClass;
import org.jclouds.ec2.compute.domain.RegionAndName; 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.AuthorizationException;
import org.jclouds.rest.suppliers.SetAndThrowAuthorizationExceptionSupplier; import org.jclouds.rest.suppliers.SetAndThrowAuthorizationExceptionSupplier;
import com.google.common.base.Optional;
import com.google.common.base.Supplier; import com.google.common.base.Supplier;
import com.google.common.base.Suppliers; import com.google.common.base.Suppliers;
import com.google.common.base.Throwables; 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 // duplicates EC2ComputeServiceContextModule; but that's easiest thing to do with guice; could extract to common util
// TODO: have a another look at this (Adrian) // TODO: have a another look at this (Adrian)
@Override @Override
protected Supplier<Set<? extends Image>> supplyNonParsingImageCache( protected Supplier<Set<? extends Image>> supplyNonParsingImages(final Supplier<Set<? extends Image>> imageSupplier,
AtomicReference<AuthorizationException> authException, @Named(PROPERTY_SESSION_INTERVAL) long seconds, Injector injector) {
final Supplier<Set<? extends Image>> imageSupplier, Injector injector) {
final Supplier<LoadingCache<RegionAndName, ? extends Image>> cache = injector.getInstance(Key final Supplier<LoadingCache<RegionAndName, ? extends Image>> cache = injector.getInstance(Key
.get(new TypeLiteral<Supplier<LoadingCache<RegionAndName, ? extends Image>>>() { .get(new TypeLiteral<Supplier<LoadingCache<RegionAndName, ? extends Image>>>() {
})); }));
@ -170,14 +163,4 @@ public class AWSEC2ComputeServiceContextModule extends BaseComputeServiceContext
protected TemplateOptions provideTemplateOptions(Injector injector, TemplateOptions options) { protected TemplateOptions provideTemplateOptions(Injector injector, TemplateOptions options) {
return options.as(EC2TemplateOptions.class).userData("#cloud-config\nrepo_upgrade: none\n".getBytes()); 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.ec2.AWSEC2Api;
import org.jclouds.aws.util.AWSUtils; import org.jclouds.aws.util.AWSUtils;
import org.jclouds.compute.domain.Image; 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.compute.functions.EC2ImageParser;
import org.jclouds.ec2.options.DescribeImagesOptions; import org.jclouds.ec2.options.DescribeImagesOptions;
import org.jclouds.sshj.config.SshjSshClientModule; import org.jclouds.sshj.config.SshjSshClientModule;
@ -33,7 +34,7 @@ import com.google.inject.Module;
* Live test for aws-ec2 {@link ImageExtension} implementation * Live test for aws-ec2 {@link ImageExtension} implementation
*/ */
@Test(groups = "live", singleThreaded = true, testName = "AWSEC2ImageExtensionLiveTest") @Test(groups = "live", singleThreaded = true, testName = "AWSEC2ImageExtensionLiveTest")
public class AWSEC2ImageExtensionLiveTest extends BaseImageExtensionLiveTest { public class AWSEC2ImageExtensionLiveTest extends EC2ImageExtensionLiveTest {
public AWSEC2ImageExtensionLiveTest() { public AWSEC2ImageExtensionLiveTest() {
provider = "aws-ec2"; 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.rest.config.BinderUtils.bindHttpApi;
import static org.jclouds.util.Predicates2.retry; import static org.jclouds.util.Predicates2.retry;
import javax.inject.Named;
import javax.inject.Singleton;
import java.net.URI; import java.net.URI;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import com.google.common.base.Function; import javax.inject.Named;
import com.google.common.base.Functions; import javax.inject.Singleton;
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 org.jclouds.collect.Memoized; import org.jclouds.collect.Memoized;
import org.jclouds.compute.ComputeService; import org.jclouds.compute.ComputeService;
import org.jclouds.compute.ComputeServiceAdapter; 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.NodeMetadata;
import org.jclouds.compute.domain.OperatingSystem; import org.jclouds.compute.domain.OperatingSystem;
import org.jclouds.compute.domain.OsFamily; 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.compute.options.TemplateOptions;
import org.jclouds.domain.Location; import org.jclouds.domain.Location;
import org.jclouds.domain.LoginCredentials; 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.ImplicitLocationSupplier;
import org.jclouds.location.suppliers.implicit.FirstZone; 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 public final class GoogleComputeEngineServiceContextModule
extends ComputeServiceAdapterContextModule<Instance, MachineType, Image, Location> { extends ComputeServiceAdapterContextModule<Instance, MachineType, Image, Location> {
@ -197,15 +196,6 @@ public final class GoogleComputeEngineServiceContextModule
return CacheBuilder.newBuilder().build(in); 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 = private static final Map<Instance.Status, NodeMetadata.Status> toPortableNodeStatus =
ImmutableMap.<Instance.Status, NodeMetadata.Status>builder() ImmutableMap.<Instance.Status, NodeMetadata.Status>builder()
.put(Instance.Status.PROVISIONING, NodeMetadata.Status.PENDING) .put(Instance.Status.PROVISIONING, NodeMetadata.Status.PENDING)