mirror of https://github.com/apache/jclouds.git
Allows extensions to fall back on using an optional name in keystone/openstack
This commit is contained in:
parent
b64d05abb9
commit
415a8a6600
|
@ -92,15 +92,15 @@ public class KeystoneHttpApiModule extends HttpApiModule<KeystoneApi> {
|
|||
}
|
||||
|
||||
// Allow providers to cleanly contribute their own aliases
|
||||
public static MapBinder<URI, URI> aliasBinder(Binder binder) {
|
||||
return MapBinder.newMapBinder(binder, URI.class, URI.class, Aliases.class).permitDuplicates();
|
||||
public static MapBinder<URI, URI> namespaceAliasBinder(Binder binder) {
|
||||
return MapBinder.newMapBinder(binder, URI.class, URI.class, NamespaceAliases.class).permitDuplicates();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(ImplicitOptionalConverter.class).to(PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet.class);
|
||||
super.configure();
|
||||
aliasBinder(binder());
|
||||
namespaceAliasBinder(binder());
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
|
|
@ -26,6 +26,6 @@ import javax.inject.Qualifier;
|
|||
@Retention(value = RetentionPolicy.RUNTIME)
|
||||
@Target(value = { ElementType.TYPE, ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
|
||||
@Qualifier
|
||||
public @interface Aliases {
|
||||
public @interface NamespaceAliases {
|
||||
|
||||
}
|
|
@ -18,6 +18,7 @@ package org.jclouds.openstack.v2_0.functions;
|
|||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.collect.Iterables.any;
|
||||
import static org.jclouds.openstack.v2_0.predicates.ExtensionPredicates.nameEquals;
|
||||
import static org.jclouds.openstack.v2_0.predicates.ExtensionPredicates.namespaceOrAliasEquals;
|
||||
import static org.jclouds.util.Optionals2.unwrapIfOptional;
|
||||
|
||||
|
@ -28,7 +29,7 @@ import java.util.Set;
|
|||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.jclouds.openstack.keystone.v2_0.config.Aliases;
|
||||
import org.jclouds.openstack.keystone.v2_0.config.NamespaceAliases;
|
||||
import org.jclouds.openstack.v2_0.domain.Extension;
|
||||
import org.jclouds.reflect.InvocationSuccess;
|
||||
import org.jclouds.rest.functions.ImplicitOptionalConverter;
|
||||
|
@ -39,7 +40,7 @@ import com.google.common.collect.ImmutableMap;
|
|||
import com.google.common.collect.Sets;
|
||||
|
||||
/**
|
||||
* We use the annotation {@link org.jclouds.openstack.services.Extension} to bind a class that implements an extension
|
||||
* We use the annotation {@link Extension} to bind a class that implements an extension
|
||||
* API to an {@link Extension}.
|
||||
*/
|
||||
public class PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet implements
|
||||
|
@ -49,7 +50,7 @@ public class PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensio
|
|||
|
||||
@Inject
|
||||
PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet(
|
||||
LoadingCache<String, Set<? extends Extension>> extensions, @Aliases Map<URI, Set<URI>> aliases) {
|
||||
LoadingCache<String, Set<? extends Extension>> extensions, @NamespaceAliases Map<URI, Set<URI>> aliases) {
|
||||
this.extensions = extensions;
|
||||
this.aliases = aliases == null ? ImmutableMap.<URI, Set<URI>> of() : ImmutableMap.copyOf(aliases);
|
||||
}
|
||||
|
@ -63,16 +64,29 @@ public class PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensio
|
|||
URI namespace = URI.create(ext.get().namespace());
|
||||
List<Object> args = input.getInvocation().getArgs();
|
||||
Set<URI> aliasesForNamespace = aliases.containsKey(namespace) ? aliases.get(namespace) : Sets.<URI> newHashSet();
|
||||
String name = ext.get().name();
|
||||
|
||||
if (args.isEmpty()) {
|
||||
if (any(extensions.getUnchecked(""), namespaceOrAliasEquals(namespace, aliasesForNamespace)))
|
||||
return input.getResult();
|
||||
// Could not find extension by namespace or namespace alias. Try to find it by name next:
|
||||
if ( !"".equals(name)) {
|
||||
if (any(extensions.getUnchecked(""), nameEquals(name)))
|
||||
return input.getResult();
|
||||
}
|
||||
} else if (args.size() == 1) {
|
||||
String arg0 = checkNotNull(args.get(0), "arg[0] in %s", input).toString();
|
||||
if (any(extensions.getUnchecked(arg0), namespaceOrAliasEquals(namespace, aliasesForNamespace)))
|
||||
return input.getResult();
|
||||
// Could not find extension by namespace or namespace alias. Try to find it by name next:
|
||||
if (!"".equals(name)) {
|
||||
if (any(extensions.getUnchecked(arg0), nameEquals(name)))
|
||||
return input.getResult();
|
||||
}
|
||||
} else {
|
||||
throw new RuntimeException(String.format("expecting zero or one args %s", input));
|
||||
}
|
||||
|
||||
return Optional.absent();
|
||||
} else {
|
||||
// No extension annotation, should check whether to return absent
|
||||
|
|
|
@ -33,7 +33,7 @@ public class ExtensionPredicates {
|
|||
|
||||
/**
|
||||
* matches namespace of the given extension
|
||||
*
|
||||
*
|
||||
* @param namespace
|
||||
* ex {@code http://docs.openstack.org/ext/keypairs/api/v1.1}
|
||||
* @return predicate that will match namespace of the given extension
|
||||
|
@ -56,7 +56,7 @@ public class ExtensionPredicates {
|
|||
|
||||
/**
|
||||
* matches alias of the given extension
|
||||
*
|
||||
*
|
||||
* @param alias
|
||||
* ex. {@code os-keypairs}
|
||||
* @return predicate that will alias of the given extension
|
||||
|
@ -75,13 +75,13 @@ public class ExtensionPredicates {
|
|||
return "aliasEquals(" + alias + ")";
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
/**
|
||||
* matches namespace of the given extension
|
||||
*
|
||||
*
|
||||
* @param namespace
|
||||
* ex {@code http://docs.openstack.org/ext/keypairs/api/v1.1}
|
||||
* @param namespacesAliases
|
||||
* @param namespaceAliases
|
||||
* Collection of ex {@code http://docs.openstack.org/compute/ext/keypairs/api/v1.1}
|
||||
* @return predicate that will match namespace of the given extension
|
||||
*/
|
||||
|
@ -102,4 +102,27 @@ public class ExtensionPredicates {
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* matches name of the given extension
|
||||
*
|
||||
* @param name
|
||||
* ex {@code http://docs.openstack.org/ext/keypairs/api/v1.1}
|
||||
* @return predicate that will match name of the given extension
|
||||
*/
|
||||
public static Predicate<Extension> nameEquals(final String name) {
|
||||
checkNotNull(name, "extension name must be defined");
|
||||
|
||||
return new Predicate<Extension>() {
|
||||
@Override
|
||||
public boolean apply(Extension ext) {
|
||||
return name.equals(ext.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "nameEquals(" + name + ")";
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,15 +28,27 @@ import javax.inject.Qualifier;
|
|||
* the context of the extension, we must consider the <a href=
|
||||
* "http://docs.openstack.org/api/openstack-compute/2/content/Extensions-d1e1444.html"
|
||||
* >extensions call</a>.
|
||||
*
|
||||
*
|
||||
* <br/>
|
||||
* For our purposes, the minimal context of an extension is the type of the
|
||||
* service it extends ex. {@link ServiceType#COMPUTE}, and its namespace ex. <a
|
||||
* href
|
||||
* ="http://docs.openstack.org/ext/keypairs/api/v1.1">http://docs.openstack.org
|
||||
* /ext/keypairs/api/v1.1</a>.
|
||||
*
|
||||
*
|
||||
*
|
||||
* <br/><br/>
|
||||
* A keystone extension example:<br/><br/>
|
||||
* {<br/>
|
||||
* "updated": "2014-12-03T00:00:00Z",<br/>
|
||||
* "name": "DiskConfig",<br/>
|
||||
* "links": [<br/>
|
||||
*<br/>
|
||||
* ],<br/>
|
||||
* "namespace": "http://docs.openstack.org/compute/ext/fake_xml",<br/>
|
||||
* "alias": "OS-DCF",<br/>
|
||||
* "description": "Disk Management Extension."<br/>
|
||||
* },<br/>
|
||||
*<br/>
|
||||
* @see ServiceType
|
||||
* @see <a href=
|
||||
* "http://docs.openstack.org/api/openstack-compute/2/content/Extensions-d1e1444.html"
|
||||
|
@ -51,14 +63,14 @@ public @interface Extension {
|
|||
|
||||
/**
|
||||
* the service type this is an extension of.
|
||||
*
|
||||
*
|
||||
* <h3>note</h3>
|
||||
*
|
||||
*
|
||||
* This isn't necessarily one of the built-in {@link ServiceType services},
|
||||
* it could be an extension of a custom service.
|
||||
*
|
||||
*
|
||||
* @return the service type this is an extension of.
|
||||
*
|
||||
*
|
||||
*/
|
||||
String of();
|
||||
|
||||
|
@ -66,9 +78,18 @@ public @interface Extension {
|
|||
* namespace ex. <a href
|
||||
* ="http://docs.openstack.org/ext/keypairs/api/v1.1">http
|
||||
* ://docs.openstack.org /ext/keypairs/api/v1.1</a>.
|
||||
*
|
||||
*
|
||||
* @return the namespace of the extension
|
||||
*/
|
||||
String namespace();
|
||||
|
||||
/**
|
||||
* @return the name of the extension
|
||||
*/
|
||||
String name() default "";
|
||||
|
||||
/**
|
||||
* @return the alias of the extension
|
||||
*/
|
||||
String alias() default "";
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ import java.util.List;
|
|||
import java.util.Set;
|
||||
|
||||
import org.jclouds.date.internal.SimpleDateFormatDateService;
|
||||
import org.jclouds.openstack.keystone.v2_0.config.Aliases;
|
||||
import org.jclouds.openstack.keystone.v2_0.config.NamespaceAliases;
|
||||
import org.jclouds.openstack.v2_0.ServiceType;
|
||||
import org.jclouds.openstack.v2_0.domain.Extension;
|
||||
import org.jclouds.reflect.Invocation;
|
||||
|
@ -70,11 +70,19 @@ public class PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensio
|
|||
|
||||
}
|
||||
|
||||
@org.jclouds.openstack.v2_0.services.Extension(of = ServiceType.COMPUTE, name = "Floating_ips", namespace = "http://docs.openstack.org/fake")
|
||||
interface FloatingIPNamedApi {
|
||||
|
||||
}
|
||||
|
||||
interface NovaApi {
|
||||
|
||||
@Delegate
|
||||
Optional<FloatingIPApi> getFloatingIPExtensionApi(String region);
|
||||
|
||||
@Delegate
|
||||
Optional<FloatingIPNamedApi> getFloatingIPNamedExtensionApi(String region);
|
||||
|
||||
@Delegate
|
||||
Optional<KeyPairApi> getKeyPairExtensionApi(String region);
|
||||
|
||||
|
@ -85,6 +93,11 @@ public class PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensio
|
|||
Invocation.create(method(NovaApi.class, "getFloatingIPExtensionApi", String.class), args), "foo");
|
||||
}
|
||||
|
||||
InvocationSuccess getFloatingIPNamedExtension(List<Object> args) throws SecurityException, NoSuchMethodException {
|
||||
return InvocationSuccess.create(
|
||||
Invocation.create(method(NovaApi.class, "getFloatingIPNamedExtensionApi", String.class), args), "foo");
|
||||
}
|
||||
|
||||
InvocationSuccess getKeyPairExtension(List<Object> args) throws SecurityException, NoSuchMethodException {
|
||||
return InvocationSuccess.create(
|
||||
Invocation.create(method(NovaApi.class, "getKeyPairExtensionApi", String.class), args), "foo");
|
||||
|
@ -108,7 +121,7 @@ public class PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensio
|
|||
/**
|
||||
* It is possible that the /extensions call returned the correct extension, but that the
|
||||
* namespaces were different, for whatever reason. One way to address this is to have a multimap
|
||||
* of the authoritative namespace to alternate onces, which could be wired up with guice
|
||||
* of the authoritative namespace to alternate ones, which could be wired up with guice
|
||||
*
|
||||
*/
|
||||
public void testPresentWhenAliasForExtensionMapsToNamespace() throws SecurityException, NoSuchMethodException {
|
||||
|
@ -125,6 +138,22 @@ public class PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensio
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* In devstack and going forward, namespaces are being deprecated. When namespace is missing (or replaced with a
|
||||
* "fake" /fake namespace), allow matching by name and alias.
|
||||
*
|
||||
*/
|
||||
public void testPresentWhenNameSpaceIsMissingAndMatchByNameOrAlias() throws SecurityException, NoSuchMethodException {
|
||||
Extension floatingIpsWithFakeNamespace = floatingIps.toBuilder()
|
||||
.namespace(URI.create("http://docs.openstack.org/ext/fake"))
|
||||
.build();
|
||||
|
||||
Multimap<URI, URI> aliases = ImmutableMultimap.of();
|
||||
|
||||
assertEquals(whenExtensionsAndAliasesInRegionInclude("region", ImmutableSet.of(floatingIpsWithFakeNamespace), aliases).apply(
|
||||
getFloatingIPNamedExtension(ImmutableList.<Object> of("region"))), Optional.of("foo"));
|
||||
}
|
||||
|
||||
private PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet whenExtensionsInRegionInclude(
|
||||
String region, Extension... extensions) {
|
||||
return whenExtensionsAndAliasesInRegionInclude(region, ImmutableSet.copyOf(extensions), ImmutableMultimap.<URI, URI> of());
|
||||
|
@ -141,7 +170,7 @@ public class PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensio
|
|||
@Override
|
||||
protected void configure() {
|
||||
MapBinder<URI, URI> aliasBindings = MapBinder.newMapBinder(binder(),
|
||||
URI.class, URI.class, Aliases.class).permitDuplicates();
|
||||
URI.class, URI.class, NamespaceAliases.class).permitDuplicates();
|
||||
for (URI key : aliases.keySet()) {
|
||||
for (URI value : aliases.get(key)) {
|
||||
aliasBindings.addBinding(key).toInstance(value);
|
||||
|
|
|
@ -40,7 +40,7 @@ import java.net.URI;
|
|||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.jclouds.openstack.keystone.v2_0.config.KeystoneHttpApiModule.aliasBinder;
|
||||
import static org.jclouds.openstack.keystone.v2_0.config.KeystoneHttpApiModule.namespaceAliasBinder;
|
||||
|
||||
/**
|
||||
* Configures the Nova connection.
|
||||
|
@ -61,7 +61,7 @@ public class NovaHttpApiModule extends HttpApiModule<NovaApi> {
|
|||
|
||||
// Intentionally private so subclasses use the Guice multibindings to contribute their aliases
|
||||
private void bindDefaultAliases() {
|
||||
MapBinder<URI, URI> aliases = aliasBinder(binder());
|
||||
MapBinder<URI, URI> aliases = namespaceAliasBinder(binder());
|
||||
aliases.addBinding(URI.create(ExtensionNamespaces.SECURITY_GROUPS)).toInstance(
|
||||
URI.create("http://docs.openstack.org/compute/ext/securitygroups/api/v1.1"));
|
||||
aliases.addBinding(URI.create(ExtensionNamespaces.FLOATING_IPS)).toInstance(
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
*/
|
||||
package org.jclouds.rackspace.cloudservers.uk.config;
|
||||
|
||||
import static org.jclouds.openstack.keystone.v2_0.config.KeystoneHttpApiModule.aliasBinder;
|
||||
import static org.jclouds.openstack.keystone.v2_0.config.KeystoneHttpApiModule.namespaceAliasBinder;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
|
@ -35,7 +35,7 @@ public class CloudServersUKHttpApiModule extends NovaHttpApiModule {
|
|||
@Override
|
||||
protected void configure() {
|
||||
super.configure();
|
||||
MapBinder<URI, URI> aliases = aliasBinder(binder());
|
||||
MapBinder<URI, URI> aliases = namespaceAliasBinder(binder());
|
||||
aliases.addBinding(URI.create(ExtensionNamespaces.VOLUME_ATTACHMENTS)).toInstance(
|
||||
URI.create("http://docs.openstack.org/compute/ext/volumes/api/v1.1"));
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
*/
|
||||
package org.jclouds.rackspace.cloudservers.us.config;
|
||||
|
||||
import static org.jclouds.openstack.keystone.v2_0.config.KeystoneHttpApiModule.aliasBinder;
|
||||
import static org.jclouds.openstack.keystone.v2_0.config.KeystoneHttpApiModule.namespaceAliasBinder;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
|
@ -36,7 +36,7 @@ public class CloudServersUSHttpApiModule extends NovaHttpApiModule {
|
|||
@Override
|
||||
protected void configure() {
|
||||
super.configure();
|
||||
MapBinder<URI, URI> aliases = aliasBinder(binder());
|
||||
MapBinder<URI, URI> aliases = namespaceAliasBinder(binder());
|
||||
aliases.addBinding(URI.create(ExtensionNamespaces.VOLUME_ATTACHMENTS)).toInstance(
|
||||
URI.create("http://docs.openstack.org/compute/ext/volumes/api/v1.1"));
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue