From b34c2533921215e07162f6e57d39f55dd20fcf15 Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Sun, 7 Oct 2012 16:18:34 -0700 Subject: [PATCH] added id to endpoint and used only endpoint.versionId to enforce endpointVersion --- .../v2_0/config/KeystoneProperties.java | 32 ++-- .../keystone/v2_0/domain/Access.java | 75 +++++--- .../keystone/v2_0/domain/Endpoint.java | 165 ++++++++++++----- .../openstack/keystone/v2_0/domain/Role.java | 2 +- .../keystone/v2_0/domain/Service.java | 40 +++-- .../keystone/v2_0/domain/Tenant.java | 2 +- .../openstack/keystone/v2_0/domain/User.java | 40 +++-- ...ionIdToURIFromAccessForTypeAndVersion.java | 170 ++++++++++++++---- .../v2_0/features/TokenApiExpectTest.java | 14 +- .../keystone/v2_0/parse/ParseAccessTest.java | 127 +++++++------ .../v2_0/parse/ParseAdminAccessTest.java | 98 +++++----- .../v2_0/parse/ParseRackspaceAccessTest.java | 127 ++++++------- .../ParseRandomEndpointVersionAccessTest.java | 112 ++++++++++++ ...dToURIFromAccessForTypeAndVersionTest.java | 131 ++++++++++++++ .../test/resources/access_version_uids.json | 0 ...java => EndpointIdIsRandomExpectTest.java} | 28 +-- 16 files changed, 826 insertions(+), 337 deletions(-) create mode 100644 apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/parse/ParseRandomEndpointVersionAccessTest.java create mode 100644 apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/suppliers/LocationIdToURIFromAccessForTypeAndVersionTest.java rename apis/{openstack-nova => openstack-keystone}/src/test/resources/access_version_uids.json (100%) rename apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/{OverrideApiVersionExpectTest.java => EndpointIdIsRandomExpectTest.java} (72%) diff --git a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/config/KeystoneProperties.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/config/KeystoneProperties.java index fc5146f8f5..5ae8f6aa8d 100644 --- a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/config/KeystoneProperties.java +++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/config/KeystoneProperties.java @@ -22,47 +22,55 @@ import org.jclouds.openstack.v2_0.ServiceType; /** * Configuration properties and constants used in Keystone connections. - * + * * @author Adrian Cole */ public interface KeystoneProperties { /** * Type of credentials used to log into the auth service. - * + * *

valid values

* - * + * * @see CredentialTypes - * @see */ public static final String CREDENTIAL_TYPE = "jclouds.keystone.credential-type"; - + /** - * set this property to specify the tenant id of the authenticated user. Cannot be used simultaneously with {@link #TENANT_NAME} + * set this property to specify the tenant id of the authenticated user. + * Cannot be used simultaneously with {@link #TENANT_NAME} + * * @see openstack docs */ public static final String TENANT_ID = "jclouds.keystone.tenant-id"; - + /** - * set this property to specify the tenant name of the authenticated user. Cannot be used simultaneously with {@link #TENANT_ID} + * set this property to specify the tenant name of the authenticated user. + * Cannot be used simultaneously with {@link #TENANT_ID} + * * @see openstack docs */ public static final String TENANT_NAME = "jclouds.keystone.tenant-name"; - + /** - * set this property to {@code true} to designate that the service requires explicit specification of either {@link #TENANT_NAME} or {@link #TENANT_ID} + * set this property to {@code true} to designate that the service requires + * explicit specification of either {@link #TENANT_NAME} or + * {@link #TENANT_ID} + * * @see openstack docs */ public static final String REQUIRES_TENANT = "jclouds.keystone.requires-tenant"; - + /** * type of the keystone service. ex. {@code compute} - * + * * @see ServiceType */ public static final String SERVICE_TYPE = "jclouds.keystone.service-type"; diff --git a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Access.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Access.java index c4baf00dd0..7e8f58a8c1 100644 --- a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Access.java +++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Access.java @@ -27,16 +27,18 @@ import org.jclouds.javax.annotation.Nullable; import com.google.common.base.Objects; import com.google.common.base.Objects.ToStringHelper; +import com.google.common.collect.ForwardingSet; import com.google.common.collect.ImmutableSet; /** * TODO * * @author Adrian Cole - * @see + * @see */ -public class Access implements Comparable { +public class Access extends ForwardingSet implements Comparable { public static Builder builder() { return new ConcreteBuilder(); @@ -46,12 +48,12 @@ public class Access implements Comparable { return new ConcreteBuilder().fromAccess(this); } - public static abstract class Builder> { + public static abstract class Builder> { protected abstract T self(); protected Token token; protected User user; - protected Set serviceCatalog = ImmutableSet.of(); + protected ImmutableSet.Builder serviceCatalog = ImmutableSet. builder(); /** * @see Access#getToken() @@ -70,26 +72,44 @@ public class Access implements Comparable { } /** - * @see Access#getServiceCatalog() + * @see Access#delegate() */ - public T serviceCatalog(Set serviceCatalog) { - this.serviceCatalog = ImmutableSet.copyOf(checkNotNull(serviceCatalog, "serviceCatalog")); + public T service(Service service) { + this.serviceCatalog.add(service); return self(); } + /** + * @see Access#delegate() + */ + public T services(Iterable serviceCatalog) { + this.serviceCatalog.addAll(serviceCatalog); + return self(); + } + + /** + * @see #services(Iterable) + */ + @Deprecated + public T serviceCatalog(Set serviceCatalog) { + this.serviceCatalog.addAll(serviceCatalog); + return self(); + } + + /** + * @see #services(Iterable) + */ + @Deprecated public T serviceCatalog(Service... in) { return serviceCatalog(ImmutableSet.copyOf(in)); } public Access build() { - return new Access(token, user, serviceCatalog); + return new Access(token, user, serviceCatalog.build()); } public T fromAccess(Access in) { - return this - .token(in.getToken()) - .user(in.getUser()) - .serviceCatalog(in.getServiceCatalog()); + return this.token(in.getToken()).user(in.getUser()).services(in); } } @@ -104,13 +124,11 @@ public class Access implements Comparable { private final User user; private final Set serviceCatalog; - @ConstructorProperties({ - "token", "user", "serviceCatalog" - }) + @ConstructorProperties({ "token", "user", "serviceCatalog" }) protected Access(Token token, User user, @Nullable Set serviceCatalog) { this.token = checkNotNull(token, "token"); this.user = checkNotNull(user, "user"); - this.serviceCatalog = serviceCatalog == null ? ImmutableSet.of() : ImmutableSet.copyOf(serviceCatalog); + this.serviceCatalog = serviceCatalog == null ? ImmutableSet. of() : ImmutableSet.copyOf(serviceCatalog); } /** @@ -128,8 +146,9 @@ public class Access implements Comparable { } /** - * TODO + * Please access the service catalog via normal collection mechanisms */ + @Deprecated public Set getServiceCatalog() { return this.serviceCatalog; } @@ -141,24 +160,25 @@ public class Access implements Comparable { @Override public boolean equals(Object obj) { - if (this == obj) return true; - if (obj == null || getClass() != obj.getClass()) return false; + if (this == obj) + return true; + if (obj == null || getClass() != obj.getClass()) + return false; Access that = Access.class.cast(obj); - return Objects.equal(this.token, that.token) - && Objects.equal(this.user, that.user) + return Objects.equal(this.token, that.token) && Objects.equal(this.user, that.user) && Objects.equal(this.serviceCatalog, that.serviceCatalog); } protected ToStringHelper string() { - return Objects.toStringHelper(this) - .add("token", token).add("user", user).add("serviceCatalog", serviceCatalog); + return Objects.toStringHelper(this).omitNullValues().add("token", token).add("user", user) + .add("serviceCatalog", serviceCatalog); } @Override public String toString() { return string().toString(); } - + @Override public int compareTo(Access that) { if (that == null) @@ -168,4 +188,9 @@ public class Access implements Comparable { return this.token.compareTo(that.token); } + @Override + protected Set delegate() { + return serviceCatalog; + } + } diff --git a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Endpoint.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Endpoint.java index 9dfd2e845d..9846c37aa7 100644 --- a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Endpoint.java +++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Endpoint.java @@ -27,13 +27,15 @@ import com.google.common.base.Objects; import com.google.common.base.Objects.ToStringHelper; /** - * An network-accessible address, usually described by URL, where a service may be accessed. If - * using an extension for templates, you can create an endpoint template, which represents the - * templates of all the consumable services that are available across the regions. + * An network-accessible address, usually described by URL, where a service may + * be accessed. If using an extension for templates, you can create an endpoint + * template, which represents the templates of all the consumable services that + * are available across the regions. * * @author AdrianCole - * @see + * @see */ public class Endpoint { @@ -45,9 +47,10 @@ public class Endpoint { return new ConcreteBuilder().fromEndpoint(this); } - public static abstract class Builder> { + public static abstract class Builder> { protected abstract T self(); + protected String id; protected String versionId; protected String region; protected URI publicURL; @@ -57,6 +60,15 @@ public class Endpoint { protected URI versionInfo; protected URI versionList; + + /** + * @see Endpoint#getId() + */ + public T id(String id) { + this.id = id; + return self(); + } + /** * @see Endpoint#getVersionId() */ @@ -113,6 +125,41 @@ public class Endpoint { return self(); } + /** + * @see Endpoint#getPublicURL() + */ + public T publicURL(String publicURL) { + return publicURL(URI.create(publicURL)); + } + + /** + * @see Endpoint#getInternalURL() + */ + public T internalURL(String internalURL) { + return internalURL(URI.create(internalURL)); + } + + /** + * @see Endpoint#getAdminURL() + */ + public T adminURL(String adminURL) { + return adminURL(URI.create(adminURL)); + } + + /** + * @see Endpoint#getVersionInfo() + */ + public T versionInfo(String versionInfo) { + return versionInfo(URI.create(versionInfo)); + } + + /** + * @see Endpoint#getVersionList() + */ + public T versionList(String versionList) { + return versionList(URI.create(versionList)); + } + /** * @see Endpoint#getTenantId() */ @@ -122,19 +169,14 @@ public class Endpoint { } public Endpoint build() { - return new Endpoint(null, versionId, region, publicURL, internalURL, adminURL, versionInfo, versionList, null, tenantId); + return new Endpoint(id, versionId, region, publicURL, internalURL, adminURL, versionInfo, versionList, null, + tenantId); } public T fromEndpoint(Endpoint in) { - return this - .versionId(in.getVersionId()) - .region(in.getRegion()) - .publicURL(in.getPublicURL()) - .internalURL(in.getInternalURL()) - .adminURL(in.getAdminURL()) - .versionInfo(in.getVersionInfo()) - .versionList(in.getVersionList()) - .tenantId(in.getTenantId()); + return this.versionId(in.getVersionId()).region(in.getRegion()).publicURL(in.getPublicURL()) + .internalURL(in.getInternalURL()).adminURL(in.getAdminURL()).versionInfo(in.getVersionInfo()) + .versionList(in.getVersionList()).tenantId(in.getTenantId()); } } @@ -145,22 +187,26 @@ public class Endpoint { } } - private final String versionId; + private final String id; + private final String tenantId; private final String region; private final URI publicURL; private final URI internalURL; private final URI adminURL; + + // fields not defined in + // https://github.com/openstack/keystone/blob/master/keystone/service.py + private final String versionId; private final URI versionInfo; private final URI versionList; - private final String tenantId; - @ConstructorProperties({ - "id", "versionId", "region", "publicURL", "internalURL", "adminURL", "versionInfo", "versionList", "tenantName", "tenantId" - }) - protected Endpoint(@Nullable String id, @Nullable String versionId, @Nullable String region, @Nullable URI publicURL, - @Nullable URI internalURL, @Nullable URI adminURL, @Nullable URI versionInfo, @Nullable URI versionList, - @Nullable String tenantName, @Nullable String tenantId) { - this.versionId = versionId != null ? versionId : id; + @ConstructorProperties({ "id", "versionId", "region", "publicURL", "internalURL", "adminURL", "versionInfo", + "versionList", "tenantName", "tenantId" }) + protected Endpoint(@Nullable String id, @Nullable String versionId, @Nullable String region, + @Nullable URI publicURL, @Nullable URI internalURL, @Nullable URI adminURL, @Nullable URI versionInfo, + @Nullable URI versionList, @Nullable String tenantName, @Nullable String tenantId) { + this.id = id; + this.versionId = versionId; this.tenantId = tenantId != null ? tenantId : tenantName; this.region = region; this.publicURL = publicURL; @@ -171,10 +217,26 @@ public class Endpoint { } /** - * When providing an ID, it is assumed that the endpoint exists in the current OpenStack - * deployment + * When providing an ID, it is assumed that the endpoint exists in the + * current OpenStack deployment * - * @return the versionId of the endpoint in the current OpenStack deployment, or null if not specified + * @return the id of the endpoint in the current OpenStack deployment, or + * null if not specified + */ + @Nullable + public String getId() { + return this.id; + } + + /** + * + *

Note

+ * + * This is not defined in
KeyStone, rather only in Rackspace */ @Nullable public String getVersionId() { @@ -213,11 +275,31 @@ public class Endpoint { return this.adminURL; } + /** + * + *

Note

+ * + * This is not defined in KeyStone, rather only in Rackspace + */ @Nullable public URI getVersionInfo() { return this.versionInfo; } + /** + * + *

Note

+ * + * This is not defined in KeyStone, rather only in Rackspace + */ @Nullable public URI getVersionList() { return this.versionList; @@ -233,28 +315,29 @@ public class Endpoint { @Override public int hashCode() { - return Objects.hashCode(versionId, region, publicURL, internalURL, adminURL, versionInfo, versionList, tenantId); + return Objects.hashCode(id, versionId, region, publicURL, internalURL, adminURL, versionInfo, versionList, + tenantId); } @Override public boolean equals(Object obj) { - if (this == obj) return true; - if (obj == null || getClass() != obj.getClass()) return false; + if (this == obj) + return true; + if (obj == null || getClass() != obj.getClass()) + return false; Endpoint that = Endpoint.class.cast(obj); - return Objects.equal(this.versionId, that.versionId) - && Objects.equal(this.region, that.region) - && Objects.equal(this.publicURL, that.publicURL) - && Objects.equal(this.internalURL, that.internalURL) - && Objects.equal(this.adminURL, that.adminURL) - && Objects.equal(this.versionInfo, that.versionInfo) - && Objects.equal(this.versionList, that.versionList) + return Objects.equal(this.id, that.id) && Objects.equal(this.versionId, that.versionId) + && Objects.equal(this.region, that.region) && Objects.equal(this.publicURL, that.publicURL) + && Objects.equal(this.internalURL, that.internalURL) && Objects.equal(this.adminURL, that.adminURL) + && Objects.equal(this.versionInfo, that.versionInfo) && Objects.equal(this.versionList, that.versionList) && Objects.equal(this.tenantId, that.tenantId); } protected ToStringHelper string() { - return Objects.toStringHelper(this).omitNullValues() - .add("versionId", versionId).add("region", region).add("publicURL", publicURL).add("internalURL", internalURL) - .add("adminURL", adminURL).add("versionInfo", versionInfo).add("versionList", versionList).add("tenantId", tenantId); + return Objects.toStringHelper(this).omitNullValues().add("id", id).add("versionId", versionId) + .add("region", region).add("publicURL", publicURL).add("internalURL", internalURL) + .add("adminURL", adminURL).add("versionInfo", versionInfo).add("versionList", versionList) + .add("tenantId", tenantId); } @Override diff --git a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Role.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Role.java index 88381659b1..700e4a75e5 100644 --- a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Role.java +++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Role.java @@ -195,7 +195,7 @@ public class Role { } protected ToStringHelper string() { - return Objects.toStringHelper(this) + return Objects.toStringHelper(this).omitNullValues() .add("id", id).add("name", name).add("description", description).add("serviceId", serviceId).add("tenantId", tenantId); } diff --git a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Service.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Service.java index 23a1bffbab..4198992815 100644 --- a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Service.java +++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Service.java @@ -25,7 +25,6 @@ import java.util.Set; import com.google.common.base.Objects; import com.google.common.base.Objects.ToStringHelper; -import com.google.common.collect.ComparisonChain; import com.google.common.collect.ForwardingSet; import com.google.common.collect.ImmutableSet; @@ -38,7 +37,7 @@ import com.google.common.collect.ImmutableSet; * @see */ -public class Service extends ForwardingSet implements Comparable { +public class Service extends ForwardingSet { public static Builder builder() { return new ConcreteBuilder(); @@ -53,7 +52,7 @@ public class Service extends ForwardingSet implements Comparable endpoints = ImmutableSet.of(); + protected ImmutableSet.Builder endpoints = ImmutableSet.builder(); /** * @see Service#getType() @@ -72,26 +71,38 @@ public class Service extends ForwardingSet implements Comparable endpoints) { - this.endpoints = ImmutableSet.copyOf(checkNotNull(endpoints, "endpoints")); + public T endpoint(Endpoint endpoint) { + this.endpoints.add(endpoint); return self(); } + /** + * @see Service#delegate() + */ + public T endpoints(Iterable endpoints) { + this.endpoints.addAll(endpoints); + return self(); + } + + /** + * @see #endpoints(Iterable) + */ + @Deprecated public T endpoints(Endpoint... in) { return endpoints(ImmutableSet.copyOf(in)); } public Service build() { - return new Service(type, name, endpoints); + return new Service(type, name, endpoints.build()); } public T fromService(Service in) { return this .type(in.getType()) .name(in.getName()) - .endpoints(in.getEndpoints()); + .endpoints(in); } } private static class ConcreteBuilder extends Builder { @@ -131,8 +142,9 @@ public class Service extends ForwardingSet implements Comparable getEndpoints() { return this.endpoints; } @@ -153,7 +165,7 @@ public class Service extends ForwardingSet implements Comparable implements Comparable delegate() { return endpoints; diff --git a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Tenant.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Tenant.java index 14100567f1..c5b9853d30 100644 --- a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Tenant.java +++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Tenant.java @@ -148,7 +148,7 @@ public class Tenant { } protected ToStringHelper string() { - return Objects.toStringHelper(this) + return Objects.toStringHelper(this).omitNullValues() .add("id", id).add("name", name).add("description", description); } diff --git a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/User.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/User.java index 82fbcd58cf..18aeacd4ef 100644 --- a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/User.java +++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/User.java @@ -27,6 +27,7 @@ import org.jclouds.javax.annotation.Nullable; import com.google.common.base.Objects; import com.google.common.base.Objects.ToStringHelper; +import com.google.common.collect.ForwardingSet; import com.google.common.collect.ImmutableSet; /** @@ -37,10 +38,10 @@ import com.google.common.collect.ImmutableSet; * tenant. * * @author Adrian Cole - * @see */ -public class User { +public class User extends ForwardingSet { public static Builder builder() { return new ConcreteBuilder(); @@ -55,7 +56,7 @@ public class User { protected String id; protected String name; - protected Set roles = ImmutableSet.of(); + protected ImmutableSet.Builder roles = ImmutableSet. builder(); /** * @see User#getId() @@ -74,26 +75,38 @@ public class User { } /** - * @see User#getRoles() + * @see User#delegate() */ - public T roles(Set roles) { - this.roles = ImmutableSet.copyOf(checkNotNull(roles, "roles")); + public T role(Role role) { + this.roles.add(role); return self(); } + /** + * @see User#delegate() + */ + public T roles(Iterable roles) { + this.roles.addAll(roles); + return self(); + } + + /** + * @see #roles(Iterable) + */ + @Deprecated public T roles(Role... in) { return roles(ImmutableSet.copyOf(in)); } public User build() { - return new User(id, name, roles); + return new User(id, name, roles.build()); } public T fromUser(User in) { return this .id(in.getId()) .name(in.getName()) - .roles(in.getRoles()); + .roles(in); } } @@ -114,7 +127,7 @@ public class User { protected User(String id, String name, @Nullable Set roles) { this.id = checkNotNull(id, "id"); this.name = checkNotNull(name, "name"); - this.roles = roles == null ? ImmutableSet.of() : ImmutableSet.copyOf(checkNotNull(roles, "roles")); + this.roles = roles == null ? ImmutableSet.of() : ImmutableSet.copyOf(roles); } /** @@ -134,8 +147,10 @@ public class User { } /** + * Please use User as a Set * @return the roles assigned to the user */ + @Deprecated public Set getRoles() { return this.roles; } @@ -156,7 +171,7 @@ public class User { } protected ToStringHelper string() { - return Objects.toStringHelper(this) + return Objects.toStringHelper(this).omitNullValues() .add("id", id).add("name", name).add("roles", roles); } @@ -165,4 +180,9 @@ public class User { return string().toString(); } + @Override + protected Set delegate() { + return roles; + } + } diff --git a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/suppliers/LocationIdToURIFromAccessForTypeAndVersion.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/suppliers/LocationIdToURIFromAccessForTypeAndVersion.java index a316ee1b62..a4b968f54c 100644 --- a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/suppliers/LocationIdToURIFromAccessForTypeAndVersion.java +++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/suppliers/LocationIdToURIFromAccessForTypeAndVersion.java @@ -18,29 +18,63 @@ */ package org.jclouds.openstack.keystone.v2_0.suppliers; +import static com.google.common.collect.Iterables.any; +import static com.google.common.collect.Iterables.concat; +import static com.google.common.collect.Iterables.size; +import static com.google.common.collect.Iterables.tryFind; +import static com.google.common.collect.Multimaps.index; + import java.net.URI; +import java.util.Collection; import java.util.Map; import java.util.NoSuchElementException; -import java.util.Set; -import javax.inject.Inject; +import javax.annotation.Resource; import javax.inject.Singleton; +import org.jclouds.javax.annotation.Nullable; +import org.jclouds.logging.Logger; import org.jclouds.openstack.keystone.v2_0.domain.Access; import org.jclouds.openstack.keystone.v2_0.domain.Endpoint; import org.jclouds.openstack.keystone.v2_0.domain.Service; import org.jclouds.openstack.keystone.v2_0.functions.EndpointToSupplierURI; +import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Function; +import com.google.common.base.Optional; import com.google.common.base.Predicate; import com.google.common.base.Supplier; +import com.google.common.collect.FluentIterable; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableMap.Builder; import com.google.common.collect.Iterables; import com.google.common.collect.Maps; -import com.google.common.collect.Sets; +import com.google.common.collect.Multimap; +import com.google.inject.Inject; import com.google.inject.assistedinject.Assisted; @Singleton public class LocationIdToURIFromAccessForTypeAndVersion implements Supplier>> { + + public static interface Factory { + /** + * + * @param apiType + * type of the api, according to the provider. ex. + * {@code compute} {@code object-store} + * @param apiVersion + * version of the api, or null if not available + * @return locations mapped to default uri + * @throws NoSuchElementException + * if the {@code apiType} is not present in the catalog + */ + LocationIdToURIFromAccessForTypeAndVersion createForApiTypeAndVersion(@Assisted("apiType") String apiType, + @Nullable @Assisted("apiVersion") String apiVersion) throws NoSuchElementException; + } + + @Resource + protected Logger logger = Logger.NULL; + protected final Supplier access; protected final EndpointToSupplierURI endpointToSupplierURI; protected final Function endpointToLocationId; @@ -49,8 +83,8 @@ public class LocationIdToURIFromAccessForTypeAndVersion implements Supplier access, - EndpointToSupplierURI endpointToSupplierURI, Function endpointToLocationId, - @Assisted("apiType") String apiType, @Assisted("apiVersion") String apiVersion) { + EndpointToSupplierURI endpointToSupplierURI, Function endpointToLocationId, + @Assisted("apiType") String apiType, @Nullable @Assisted("apiVersion") String apiVersion) { this.access = access; this.endpointToSupplierURI = endpointToSupplierURI; this.endpointToLocationId = endpointToLocationId; @@ -60,38 +94,112 @@ public class LocationIdToURIFromAccessForTypeAndVersion implements Supplier> get() { - Access accessResponse = access.get(); - Set services = Sets.filter(accessResponse.getServiceCatalog(), new Predicate() { + FluentIterable services = FluentIterable.from(access.get()).filter(apiTypeEquals); + if (services.toImmutableSet().size() == 0) + throw new NoSuchElementException(String.format("apiType %s not found in catalog %s", apiType, services)); - @Override - public boolean apply(Service input) { - return input.getType().equals(apiType); - } + Iterable endpoints = concat(services); - }); - if (services.size() == 0) - throw new NoSuchElementException(String.format("apiType %s not found in catalog %s", apiType, - accessResponse.getServiceCatalog())); + if (size(endpoints) == 0) + throw new NoSuchElementException( + String.format("no endpoints for apiType %s in services %s", apiType, services)); - Iterable endpoints = Iterables.filter(Iterables.concat(services), new Predicate() { + boolean checkVersionId = any(endpoints, versionAware); - @Override - public boolean apply(Endpoint input) { - if (input.getVersionId() == null) { - return true; - } - return input.getVersionId().equals(apiVersion); - } - - }); - - if (Iterables.size(endpoints) == 0) - throw new NoSuchElementException(String.format( + Multimap locationToEndpoints = index(endpoints, endpointToLocationId); + Map locationToEndpoint; + if (checkVersionId && apiVersion != null) { + locationToEndpoint = refineToVersionSpecificEndpoint(locationToEndpoints); + if (locationToEndpoint.size() == 0) + throw new NoSuchElementException(String.format( "no endpoints for apiType %s are of version %s, or version agnostic: %s", apiType, apiVersion, - services)); + locationToEndpoints)); + } else { + locationToEndpoint = firstEndpointInLocation(locationToEndpoints); + } - Map locationIdToEndpoint = Maps.uniqueIndex(endpoints, endpointToLocationId); - return Maps.transformValues(locationIdToEndpoint, endpointToSupplierURI); + logger.debug("endpoints for apiType %s and version %s: %s", apiType, apiVersion, locationToEndpoints); + return Maps.transformValues(locationToEndpoint, endpointToSupplierURI); + } + + @VisibleForTesting + Map firstEndpointInLocation(Multimap locationToEndpoints) { + Builder locationToEndpointBuilder = ImmutableMap. builder(); + for (String locationId : locationToEndpoints.keySet()) { + Collection endpoints = locationToEndpoints.get(locationId); + switch (endpoints.size()) { + case 0: + logNoEndpointsInLocation(locationId); + break; + default: + locationToEndpointBuilder.put(locationId, Iterables.get(endpoints, 0)); + } + } + return locationToEndpointBuilder.build(); + } + + @VisibleForTesting + Map refineToVersionSpecificEndpoint(Multimap locationToEndpoints) { + Builder locationToEndpointBuilder = ImmutableMap. builder(); + for (String locationId : locationToEndpoints.keySet()) { + Collection endpoints = locationToEndpoints.get(locationId); + switch (endpoints.size()) { + case 0: + logNoEndpointsInLocation(locationId); + break; + default: + putIfPresent(locationId, strictMatchEndpointVersion(endpoints, locationId), locationToEndpointBuilder); + } + + } + return locationToEndpointBuilder.build(); + } + + /** + * Prioritizes endpoint.versionId over endpoint.id when matching + */ + private Optional strictMatchEndpointVersion(Iterable endpoints, String locationId) { + Optional endpointOfVersion = tryFind(endpoints, apiVersionEqualsVersionId); + if (!endpointOfVersion.isPresent()) + logger.debug("no endpoints of apiType %s matched expected version %s in location %s: %s", apiType, apiVersion, + locationId, endpoints); + return endpointOfVersion; + } + + private void logNoEndpointsInLocation(String locationId) { + logger.debug("no endpoints found for apiType %s in location %s", apiType, locationId); + } + + private final Predicate apiVersionEqualsVersionId = new Predicate() { + + @Override + public boolean apply(Endpoint input) { + return input.getVersionId().equals(apiVersion); + } + + }; + + private final Predicate versionAware = new Predicate() { + + @Override + public boolean apply(Endpoint input) { + return input.getVersionId() != null; + } + + }; + + private final Predicate apiTypeEquals = new Predicate() { + + @Override + public boolean apply(Service input) { + return input.getType().equals(apiType); + } + + }; + + private static void putIfPresent(K key, Optional value, Builder builder) { + if (value.isPresent()) + builder.put(key, value.get()); } @Override diff --git a/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/features/TokenApiExpectTest.java b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/features/TokenApiExpectTest.java index 9132077d6e..9b59b98ab0 100644 --- a/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/features/TokenApiExpectTest.java +++ b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/features/TokenApiExpectTest.java @@ -55,7 +55,7 @@ public class TokenApiExpectTest extends BaseKeystoneRestApiExpectTest endpoints = api.listEndpointsForToken("XXXXXX"); - + assertEquals(endpoints, ImmutableSet.of( Endpoint.builder().publicURL(URI.create("https://csnode.jclouds.org/v2.0/")) .adminURL(URI.create("https://csnode.jclouds.org:35357/v2.0/")) - .region("region-a.geo-1").versionId("2.0").build() + .region("region-a.geo-1").id("2.0").versionId("2.0").build() )); } - + @Test public void testGetEndpointsForTokenFailNotFound() { TokenApi api = requestsSendResponses( @@ -157,5 +157,5 @@ public class TokenApiExpectTest extends BaseKeystoneRestApiExpectTest { @SelectJson("access") @Consumes(MediaType.APPLICATION_JSON) public Access expected() { - return Access.builder().token( - Token.builder().expires(new SimpleDateFormatDateService().iso8601DateParse("2012-01-18T21:35:59.050Z")) - .id("Auth_4f173437e4b013bee56d1007").tenant( - Tenant.builder().id("40806637803162").name("user@jclouds.org-default-tenant").build()) - .build()).user( - User.builder().id("36980896575174").name("user@jclouds.org").roles( - Role.builder().id("00000000004022").serviceId("110").name("Admin").tenantId("40806637803162") - .build(), - Role.builder().id("00000000004024").serviceId("140").name("user").tenantId("40806637803162") - .build(), - Role.builder().id("00000000004004").serviceId("100").name("domainuser").build(), - Role.builder().id("00000000004016").serviceId("120").name("netadmin") - .tenantId("40806637803162").build()).build()).serviceCatalog( - - Service.builder().name("Object Storage").type("object-store").endpoints( - Endpoint.builder().tenantId("40806637803162").publicURL( - URI.create("https://objects.jclouds.org/v1.0/40806637803162")) - .adminURL(URI.create("https://objects.jclouds.org/v1.0/")) - .region("region-a.geo-1").versionId("1.0").build()).build(), - - Service.builder().name("Identity").type("identity").endpoints( - Endpoint.builder().publicURL(URI.create("https://csnode.jclouds.org/v2.0/")) - .adminURL(URI.create("https://csnode.jclouds.org:35357/v2.0/")) - .region("region-a.geo-1").versionId("2.0").build()).build(), - - Service.builder().name("Image Management").type("image").endpoints( - Endpoint.builder().tenantId("40806637803162").publicURL( - URI.create("https://glance.jclouds.org:9292/v1.0")).region("az-1.region-a.geo-1") - .versionId("1.0").build()).build(), - - Service.builder().name("Compute").type("compute").endpoints( - Endpoint.builder() - .tenantId("3456") - .publicURL(URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456")) - .region("az-1.region-a.geo-1") - .versionId("1.1") - .versionInfo(URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/")) - .versionList(URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com")).build(), - Endpoint.builder() - .tenantId("3456") - .publicURL(URI.create("https://az-2.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456")) - .region("az-2.region-a.geo-1") - .versionId("1.1") - .versionInfo(URI.create("https://az-2.region-a.geo-1.compute.hpcloudsvc.com/v1.1/")) - .versionList(URI.create("https://az-2.region-a.geo-1.compute.hpcloudsvc.com")).build(), - Endpoint.builder() - .tenantId("3456") - .publicURL(URI.create("https://az-3.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456")) - .region("az-3.region-a.geo-1") - .versionId("1.1") - .versionInfo(URI.create("https://az-3.region-a.geo-1.compute.hpcloudsvc.com/v1.1/")) - .versionList(URI.create("https://az-3.region-a.geo-1.compute.hpcloudsvc.com")).build()).build(), - - Service.builder().name("Quantum Service").type("network").endpoints( - Endpoint.builder() - .tenantId("3456") - .publicURL(URI.create("https://csnode.jclouds.org:9696/v1.0/tenants/3456")) - .internalURL(URI.create("https://csnode.jclouds.org:9696/v1.0/tenants/3456")) - .adminURL(URI.create("https://csnode.jclouds.org:9696/v1.0")) - .region("region-a.geo-1") - .versionId("1.0").build() - ).build()) - - .build(); + return Access.builder() + .token(Token.builder() + .expires(new SimpleDateFormatDateService().iso8601DateParse("2012-01-18T21:35:59.050Z")) + .id("Auth_4f173437e4b013bee56d1007") + .tenant(Tenant.builder().id("40806637803162").name("user@jclouds.org-default-tenant").build()).build()) + .user(User.builder() + .id("36980896575174").name("user@jclouds.org") + .role(Role.builder().id("00000000004022").serviceId("110").name("Admin").tenantId("40806637803162").build()) + .role(Role.builder().id("00000000004024").serviceId("140").name("user").tenantId("40806637803162").build()) + .role(Role.builder().id("00000000004004").serviceId("100").name("domainuser").build()) + .role(Role.builder().id("00000000004016").serviceId("120").name("netadmin").tenantId("40806637803162").build()).build()) + .service(Service.builder().name("Object Storage").type("object-store") + .endpoint(Endpoint.builder() + .tenantId("40806637803162") + .publicURL("https://objects.jclouds.org/v1.0/40806637803162") + .adminURL("https://objects.jclouds.org/v1.0/") + .id("1.0") + .region("region-a.geo-1").build()).build()) + .service(Service.builder().name("Identity").type("identity") + .endpoint(Endpoint.builder() + .publicURL("https://csnode.jclouds.org/v2.0/") + .adminURL("https://csnode.jclouds.org:35357/v2.0/") + .region("region-a.geo-1") + .id("2.0") + .versionId("2.0").build()).build()) + .service(Service.builder().name("Image Management").type("image") + .endpoint(Endpoint.builder() + .tenantId("40806637803162") + .publicURL("https://glance.jclouds.org:9292/v1.0") + .region("az-1.region-a.geo-1") + .id("1.0").build()).build()) + .service(Service.builder().name("Compute").type("compute") + .endpoint(Endpoint.builder() + .tenantId("3456") + .publicURL("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456") + .region("az-1.region-a.geo-1") + .versionId("1.1") + .versionInfo("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/") + .versionList("https://az-1.region-a.geo-1.compute.hpcloudsvc.com").build()) + .endpoint(Endpoint.builder() + .tenantId("3456") + .publicURL("https://az-2.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456") + .region("az-2.region-a.geo-1") + .versionId("1.1") + .versionInfo("https://az-2.region-a.geo-1.compute.hpcloudsvc.com/v1.1/") + .versionList("https://az-2.region-a.geo-1.compute.hpcloudsvc.com").build()) + .endpoint(Endpoint.builder() + .tenantId("3456") + .publicURL("https://az-3.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456") + .region("az-3.region-a.geo-1") + .versionId("1.1") + .versionInfo("https://az-3.region-a.geo-1.compute.hpcloudsvc.com/v1.1/") + .versionList("https://az-3.region-a.geo-1.compute.hpcloudsvc.com").build()).build()) + .service(Service.builder().name("Quantum Service").type("network") + .endpoint(Endpoint.builder() + .tenantId("3456") + .publicURL("https://csnode.jclouds.org:9696/v1.0/tenants/3456") + .internalURL("https://csnode.jclouds.org:9696/v1.0/tenants/3456") + .adminURL("https://csnode.jclouds.org:9696/v1.0") + .region("region-a.geo-1") + .versionId("1.0").build()).build()).build(); } } diff --git a/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/parse/ParseAdminAccessTest.java b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/parse/ParseAdminAccessTest.java index 89d697d2e6..b5e96b6801 100644 --- a/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/parse/ParseAdminAccessTest.java +++ b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/parse/ParseAdminAccessTest.java @@ -18,8 +18,6 @@ */ package org.jclouds.openstack.keystone.v2_0.parse; -import java.net.URI; - import javax.ws.rs.Consumes; import javax.ws.rs.core.MediaType; @@ -36,7 +34,7 @@ import org.jclouds.rest.annotations.SelectJson; import org.testng.annotations.Test; /** - * @author Adam Lowe + * @author Adam Lowe, Adrian Cole */ @Test(groups = "unit", testName = "ParseAdminAccessTest") public class ParseAdminAccessTest extends BaseItemParserTest { @@ -50,52 +48,52 @@ public class ParseAdminAccessTest extends BaseItemParserTest { @SelectJson("access") @Consumes(MediaType.APPLICATION_JSON) public Access expected() { - return Access.builder().token( - Token.builder().expires(new SimpleDateFormatDateService().iso8601SecondsDateParse("2012-08-01T13:08:52Z")) - .id("946b8ad1ede4422f87ab21dcba27896d").tenant( - Tenant.builder().id("2fdc88ae152948c690b97ba307acae9b").name("admin").build()) - .build()).user( - User.builder().id("b4d134cfe3cf43ad8ba0c2fc5b5d8f91").name("admin").roles( - Role.builder().name("admin").build(), - Role.builder().name("KeystoneServiceAdmin").build(), - Role.builder().name("KeystoneAdmin").build()).build()) - .serviceCatalog( - Service.builder().name("Compute Service").type("compute").endpoints( - Endpoint.builder() - .adminURL(URI.create("http://10.0.1.13:8774/v2/2fdc88ae152948c690b97ba307acae9b")) - .internalURL(URI.create("http://10.0.1.13:8774/v2/2fdc88ae152948c690b97ba307acae9b")) - .publicURL(URI.create("http://10.0.1.13:8774/v2/2fdc88ae152948c690b97ba307acae9b")) - .region("RegionOne").build()).build(), - Service.builder().name("S3 Service").type("s3").endpoints( - Endpoint.builder() - .adminURL(URI.create("http://10.0.1.13:3333")) - .internalURL(URI.create("http://10.0.1.13:3333")) - .publicURL(URI.create("http://10.0.1.13:3333")) - .region("RegionOne").build()).build(), - Service.builder().name("Image Service").type("image").endpoints( - Endpoint.builder() - .adminURL(URI.create("http://10.0.1.13:9292")) - .internalURL(URI.create("http://10.0.1.13:9292")) - .publicURL(URI.create("http://10.0.1.13:9292")) - .region("RegionOne").build()).build(), - Service.builder().name("Volume Service").type("volume").endpoints( - Endpoint.builder() - .adminURL(URI.create("http://10.0.1.13:8776/v1/2fdc88ae152948c690b97ba307acae9b")) - .internalURL(URI.create("http://10.0.1.13:8776/v1/2fdc88ae152948c690b97ba307acae9b")) - .publicURL(URI.create("http://10.0.1.13:8776/v1/2fdc88ae152948c690b97ba307acae9b")) - .region("RegionOne").build()).build(), - Service.builder().name("EC2 Service").type("ec2").endpoints( - Endpoint.builder() - .adminURL(URI.create("http://10.0.1.13:8773/services/Admin")) - .internalURL(URI.create("http://10.0.1.13:8773/services/Cloud")) - .publicURL(URI.create("http://10.0.1.13:8773/services/Cloud")) - .region("RegionOne").build()).build(), - Service.builder().name("Identity Service").type("identity").endpoints( - Endpoint.builder() - .adminURL(URI.create("http://10.0.1.13:35357/v2.0")) - .internalURL(URI.create("http://10.0.1.13:5000/v2.0")) - .publicURL(URI.create("http://10.0.1.13:5000/v2.0")) - .region("RegionOne").build()).build() - ).build(); + return Access.builder() + .token(Token.builder() + .expires(new SimpleDateFormatDateService().iso8601SecondsDateParse("2012-08-01T13:08:52Z")) + .id("946b8ad1ede4422f87ab21dcba27896d") + .tenant(Tenant.builder().id("2fdc88ae152948c690b97ba307acae9b").name("admin").build()).build()) + .user(User.builder() + .id("b4d134cfe3cf43ad8ba0c2fc5b5d8f91") + .name("admin") + .role(Role.builder().name("admin").build()) + .role(Role.builder().name("KeystoneServiceAdmin").build()) + .role(Role.builder().name("KeystoneAdmin").build()).build()) + .service(Service.builder().name("Compute Service").type("compute") + .endpoint(Endpoint.builder() + .adminURL("http://10.0.1.13:8774/v2/2fdc88ae152948c690b97ba307acae9b") + .internalURL("http://10.0.1.13:8774/v2/2fdc88ae152948c690b97ba307acae9b") + .publicURL("http://10.0.1.13:8774/v2/2fdc88ae152948c690b97ba307acae9b") + .region("RegionOne").build()).build()) + .service(Service.builder().name("S3 Service").type("s3") + .endpoint(Endpoint.builder() + .adminURL("http://10.0.1.13:3333") + .internalURL("http://10.0.1.13:3333") + .publicURL("http://10.0.1.13:3333") + .region("RegionOne").build()).build()) + .service(Service.builder().name("Image Service").type("image") + .endpoint(Endpoint.builder() + .adminURL("http://10.0.1.13:9292") + .internalURL("http://10.0.1.13:9292") + .publicURL("http://10.0.1.13:9292") + .region("RegionOne").build()).build()) + .service(Service.builder().name("Volume Service").type("volume") + .endpoint(Endpoint.builder() + .adminURL("http://10.0.1.13:8776/v1/2fdc88ae152948c690b97ba307acae9b") + .internalURL("http://10.0.1.13:8776/v1/2fdc88ae152948c690b97ba307acae9b") + .publicURL("http://10.0.1.13:8776/v1/2fdc88ae152948c690b97ba307acae9b") + .region("RegionOne").build()).build()) + .service(Service.builder().name("EC2 Service").type("ec2") + .endpoint(Endpoint.builder() + .adminURL("http://10.0.1.13:8773/services/Admin") + .internalURL("http://10.0.1.13:8773/services/Cloud") + .publicURL("http://10.0.1.13:8773/services/Cloud") + .region("RegionOne").build()).build()) + .service(Service.builder().name("Identity Service").type("identity") + .endpoint(Endpoint.builder() + .adminURL("http://10.0.1.13:35357/v2.0") + .internalURL("http://10.0.1.13:5000/v2.0") + .publicURL("http://10.0.1.13:5000/v2.0") + .region("RegionOne").build()).build()).build(); } } diff --git a/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/parse/ParseRackspaceAccessTest.java b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/parse/ParseRackspaceAccessTest.java index 670b1c5ab7..27b3c8e7e6 100644 --- a/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/parse/ParseRackspaceAccessTest.java +++ b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/parse/ParseRackspaceAccessTest.java @@ -18,8 +18,6 @@ */ package org.jclouds.openstack.keystone.v2_0.parse; -import java.net.URI; - import javax.ws.rs.Consumes; import javax.ws.rs.core.MediaType; @@ -38,7 +36,7 @@ import org.testng.annotations.Test; /** * @author Adrian Cole */ -@Test(groups = "unit", testName = "ParseAccessTest") +@Test(groups = "unit", testName = "ParseRackspaceAccessTest") public class ParseRackspaceAccessTest extends BaseItemParserTest { @Override @@ -50,64 +48,69 @@ public class ParseRackspaceAccessTest extends BaseItemParserTest { @SelectJson("access") @Consumes(MediaType.APPLICATION_JSON) public Access expected() { - return Access.builder().token( - Token.builder().expires(new SimpleDateFormatDateService().iso8601DateParse("2012-06-06T20:56:47.000-05:00")) - .id("Auth_4f173437e4b013bee56d1007").tenant( - Tenant.builder().id("40806637803162").name("40806637803162").build()) - .build()).user( - User.builder().id("54321").name("joe").roles( - Role.builder().id("3").name("identity:user-admin").description("User Admin Role.") - .build()).build()).serviceCatalog( - - Service.builder().name("cloudDatabases").type("rax:database").endpoints( - Endpoint.builder().tenantId("40806637803162").publicURL( - URI.create("https://dfw.databases.api.rackspacecloud.com/v1.0/40806637803162")) - .region("DFW").build(), - Endpoint.builder().tenantId("40806637803162").publicURL( - URI.create("https://ord.databases.api.rackspacecloud.com/v1.0/40806637803162")) - .region("ORD").build()).build(), - - Service.builder().name("cloudServers").type("compute").endpoints( - Endpoint.builder().tenantId("40806637803162").publicURL( - URI.create("https://servers.api.rackspacecloud.com/v1.0/40806637803162")) - .versionId("1.0").versionInfo(URI.create("https://servers.api.rackspacecloud.com/v1.0")) - .versionList(URI.create("https://servers.api.rackspacecloud.com/")).build()).build(), - - Service.builder().name("cloudFiles").type("object-store").endpoints( - Endpoint.builder().tenantId("MossoCloudFS_dc1f419c-5059-4c87-a389-3f2e33a77b22").publicURL( - URI.create("https://storage101.dfw1.clouddrive.com/v1/MossoCloudFS_dc1f419c-5059-4c87-a389-3f2e33a77b22")) - .internalURL( - URI.create("https://snet-storage101.dfw1.clouddrive.com/v1/MossoCloudFS_dc1f419c-5059-4c87-a389-3f2e33a77b22")) - .region("DFW").build()).build(), - - Service.builder().name("cloudServersOpenStack").type("compute").endpoints( - Endpoint.builder().tenantId("40806637803162").publicURL( - URI.create("https://dfw.servers.api.rackspacecloud.com/v2/40806637803162")) - .versionInfo(URI.create("https://dfw.servers.api.rackspacecloud.com/v2")) - .versionList(URI.create("https://dfw.servers.api.rackspacecloud.com/")) - .versionId("2") - .region("DFW").build()).build(), - - Service.builder().name("cloudLoadBalancers").type("rax:load-balancer").endpoints( - Endpoint.builder().tenantId("40806637803162").publicURL( - URI.create("https://ord.loadbalancers.api.rackspacecloud.com/v1.0/40806637803162")) - .region("ORD").build(), - Endpoint.builder().tenantId("40806637803162").publicURL( - URI.create("https://dfw.loadbalancers.api.rackspacecloud.com/v1.0/40806637803162")) - .region("DFW").build()).build(), - - Service.builder().name("cloudMonitoring").type("rax:monitor").endpoints( - Endpoint.builder().tenantId("40806637803162").publicURL( - URI.create("https://monitoring.api.rackspacecloud.com/v1.0/40806637803162")).build()).build(), - - Service.builder().name("cloudDNS").type("dnsextension:dns").endpoints( - Endpoint.builder().tenantId("40806637803162").publicURL( - URI.create("https://dns.api.rackspacecloud.com/v1.0/40806637803162")).build()).build(), - - Service.builder().name("cloudFilesCDN").type("rax:object-cdn").endpoints( - Endpoint.builder().tenantId("MossoCloudFS_dc1f419c-5059-4c87-a389-3f2e33a77b22").publicURL( - URI.create("https://cdn1.clouddrive.com/v1/MossoCloudFS_dc1f419c-5059-4c87-a389-3f2e33a77b22")) - .region("DFW").build()).build() - ).build(); + return Access.builder() + .token(Token.builder() + .expires(new SimpleDateFormatDateService().iso8601DateParse("2012-06-06T20:56:47.000-05:00")) + .id("Auth_4f173437e4b013bee56d1007") + .tenant(Tenant.builder().id("40806637803162").name("40806637803162").build()).build()) + .user(User.builder() + .id("54321") + .name("joe") + .role(Role.builder() + .id("3") + .name("identity:user-admin") + .description("User Admin Role.").build()).build()) + .service(Service.builder().name("cloudDatabases").type("rax:database") + .endpoint(Endpoint.builder() + .tenantId("40806637803162") + .publicURL("https://dfw.databases.api.rackspacecloud.com/v1.0/40806637803162") + .region("DFW").build()) + .endpoint(Endpoint.builder() + .tenantId("40806637803162") + .publicURL("https://ord.databases.api.rackspacecloud.com/v1.0/40806637803162") + .region("ORD").build()).build()) + .service(Service.builder().name("cloudServers").type("compute") + .endpoint(Endpoint.builder() + .tenantId("40806637803162") + .publicURL("https://servers.api.rackspacecloud.com/v1.0/40806637803162") + .versionId("1.0") + .versionInfo("https://servers.api.rackspacecloud.com/v1.0") + .versionList("https://servers.api.rackspacecloud.com/").build()).build()) + .service(Service.builder().name("cloudFiles").type("object-store") + .endpoint(Endpoint.builder() + .tenantId("MossoCloudFS_dc1f419c-5059-4c87-a389-3f2e33a77b22") + .publicURL("https://storage101.dfw1.clouddrive.com/v1/MossoCloudFS_dc1f419c-5059-4c87-a389-3f2e33a77b22") + .internalURL("https://snet-storage101.dfw1.clouddrive.com/v1/MossoCloudFS_dc1f419c-5059-4c87-a389-3f2e33a77b22") + .region("DFW").build()).build()) + .service(Service.builder().name("cloudServersOpenStack").type("compute") + .endpoint(Endpoint.builder() + .tenantId("40806637803162") + .publicURL("https://dfw.servers.api.rackspacecloud.com/v2/40806637803162") + .versionInfo("https://dfw.servers.api.rackspacecloud.com/v2") + .versionList("https://dfw.servers.api.rackspacecloud.com/") + .versionId("2") + .region("DFW").build()).build()) + .service(Service.builder().name("cloudLoadBalancers").type("rax:load-balancer") + .endpoint(Endpoint.builder() + .tenantId("40806637803162") + .publicURL("https://ord.loadbalancers.api.rackspacecloud.com/v1.0/40806637803162") + .region("ORD").build()) + .endpoint(Endpoint.builder() + .tenantId("40806637803162") + .publicURL("https://dfw.loadbalancers.api.rackspacecloud.com/v1.0/40806637803162") + .region("DFW").build()).build()) + .service(Service.builder().name("cloudMonitoring").type("rax:monitor") + .endpoint(Endpoint.builder() + .tenantId("40806637803162") + .publicURL("https://monitoring.api.rackspacecloud.com/v1.0/40806637803162").build()).build()) + .service(Service.builder().name("cloudDNS").type("dnsextension:dns") + .endpoint(Endpoint.builder() + .tenantId("40806637803162") + .publicURL("https://dns.api.rackspacecloud.com/v1.0/40806637803162").build()).build()) + .service(Service.builder().name("cloudFilesCDN").type("rax:object-cdn") + .endpoint(Endpoint.builder() + .tenantId("MossoCloudFS_dc1f419c-5059-4c87-a389-3f2e33a77b22") + .publicURL("https://cdn1.clouddrive.com/v1/MossoCloudFS_dc1f419c-5059-4c87-a389-3f2e33a77b22") + .region("DFW").build()).build()).build(); } } diff --git a/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/parse/ParseRandomEndpointVersionAccessTest.java b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/parse/ParseRandomEndpointVersionAccessTest.java new file mode 100644 index 0000000000..6665a30d55 --- /dev/null +++ b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/parse/ParseRandomEndpointVersionAccessTest.java @@ -0,0 +1,112 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.jclouds.openstack.keystone.v2_0.parse; + +import javax.ws.rs.Consumes; +import javax.ws.rs.core.MediaType; + +import org.jclouds.date.internal.SimpleDateFormatDateService; +import org.jclouds.json.BaseItemParserTest; +import org.jclouds.openstack.keystone.v2_0.domain.Access; +import org.jclouds.openstack.keystone.v2_0.domain.Endpoint; +import org.jclouds.openstack.keystone.v2_0.domain.Role; +import org.jclouds.openstack.keystone.v2_0.domain.Service; +import org.jclouds.openstack.keystone.v2_0.domain.Tenant; +import org.jclouds.openstack.keystone.v2_0.domain.Token; +import org.jclouds.openstack.keystone.v2_0.domain.User; +import org.jclouds.rest.annotations.SelectJson; +import org.testng.annotations.Test; + +/** + * @author Adrian Cole + */ +@Test(groups = "unit", testName = "ParseRandomEndpointVersionAccessTest") +public class ParseRandomEndpointVersionAccessTest extends BaseItemParserTest { + + @Override + public String resource() { + return "/access_version_uids.json"; + } + + @Override + @SelectJson("access") + @Consumes(MediaType.APPLICATION_JSON) + public Access expected() { + return Access.builder() + .token(Token.builder() + .expires(new SimpleDateFormatDateService().iso8601SecondsDateParse("2012-09-29T19:53:45Z")) + .id("b267e2e240624b108b1ed5bba6e5882e") + .tenant(Tenant.builder() + // "enabled": true, + .id("82d8d2f865484776a1daf1e2245d3317") + .name("demo").build()).build()) + .service(Service.builder().type("compute").name("nova") + .endpoint(Endpoint.builder() + .adminURL("http://10.10.10.10:8774/v2/82d8d2f865484776a1daf1e2245d3317") + .region("RegionOne") + .internalURL("http://10.10.10.10:8774/v2/82d8d2f865484776a1daf1e2245d3317") + .id("bb3ce9ccdc5045909882688b90cc3ff0") + .publicURL("http://10.10.10.10:8774/v2/82d8d2f865484776a1daf1e2245d3317").build()).build()) + .service(Service.builder().type("s3").name("s3") + .endpoint(Endpoint.builder() + .adminURL("http://10.10.10.10:3333") + .region("RegionOne") + .internalURL("http://10.10.10.10:3333") + .id("9646263f31ea4f499732c5e1370ecf5e") + .publicURL("http://10.10.10.10:3333").build()).build()) + .service(Service.builder().type("image").name("glance") + .endpoint(Endpoint.builder() + .adminURL("http://10.10.10.10:9292") + .region("RegionOne") + .internalURL("http://10.10.10.10:9292") + .id("aa5d0b2574824ba097dc07faacf3be65") + .publicURL("http://10.10.10.10:9292").build()).build()) + .service(Service.builder().type("volume").name("cinder") + .endpoint(Endpoint.builder() + .adminURL("http://10.10.10.10:8776/v1/82d8d2f865484776a1daf1e2245d3317") + .region("RegionOne") + .internalURL("http://10.10.10.10:8776/v1/82d8d2f865484776a1daf1e2245d3317") + .id("7679065b1405447eb5f1a38a6b99ccc0") + .publicURL("http://10.10.10.10:8776/v1/82d8d2f865484776a1daf1e2245d3317").build()).build()) + .service(Service.builder().type("ec2").name("ec2") + .endpoint(Endpoint.builder() + .adminURL("http://10.10.10.10:8773/services/Admin") + .region("RegionOne") + .internalURL("http://10.10.10.10:8773/services/Cloud") + .id("22b007f023fb4c42be094916eb2bf18b") + .publicURL("http://10.10.10.10:8773/services/Cloud").build()).build()) + .service(Service.builder().type("identity").name("keystone") + .endpoint(Endpoint.builder() + .adminURL("http://10.10.10.10:35357/v2.0") + .region("RegionOne") + .internalURL("http://10.10.10.10:5000/v2.0") + .id("57ee5fb4f9a840f3b965909681d0fc53") + .publicURL("http://10.10.10.10:5000/v2.0").build()).build()) + .user(User.builder() + .id("ca248caf55844c14a4876c22112bbbb9") + .name("demo") +// .username("demo") + .role(Role.builder().name("Member").build()).build()).build(); +// "metadata": { +// "is_admin": 0, +// "roles": ["1f697d8e3ace4f5a80f7701e554ee5d9"] +// } + + } +} diff --git a/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/suppliers/LocationIdToURIFromAccessForTypeAndVersionTest.java b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/suppliers/LocationIdToURIFromAccessForTypeAndVersionTest.java new file mode 100644 index 0000000000..bb7af066b7 --- /dev/null +++ b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/suppliers/LocationIdToURIFromAccessForTypeAndVersionTest.java @@ -0,0 +1,131 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.jclouds.openstack.keystone.v2_0.suppliers; + +import static org.testng.Assert.assertEquals; + +import java.net.URI; +import java.util.Map; +import java.util.NoSuchElementException; + +import javax.inject.Singleton; + +import org.jclouds.location.Provider; +import org.jclouds.openstack.keystone.v2_0.domain.Access; +import org.jclouds.openstack.keystone.v2_0.domain.Endpoint; +import org.jclouds.openstack.keystone.v2_0.functions.EndpointToRegion; +import org.jclouds.openstack.keystone.v2_0.parse.ParseAccessTest; +import org.jclouds.openstack.keystone.v2_0.parse.ParseRackspaceAccessTest; +import org.testng.annotations.Test; + +import com.google.common.base.Function; +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Maps; +import com.google.inject.AbstractModule; +import com.google.inject.Guice; +import com.google.inject.Provides; +import com.google.inject.TypeLiteral; +import com.google.inject.assistedinject.FactoryModuleBuilder; + +/** + * @author Adrian Cole + */ +@Test(groups = "unit", testName = "LocationIdToURIFromAccessForTypeAndVersionTest") +public class LocationIdToURIFromAccessForTypeAndVersionTest { + private final LocationIdToURIFromAccessForTypeAndVersion.Factory factory = Guice.createInjector(new AbstractModule() { + + @Override + protected void configure() { + bindConstant().annotatedWith(Provider.class).to("openstack-keystone"); + bind(new TypeLiteral>(){ + }).annotatedWith(Provider.class).toInstance(Suppliers.ofInstance(URI.create("https://identity"))); + bind(new TypeLiteral>(){}).to(EndpointToRegion.class); + install(new FactoryModuleBuilder().implement(LocationIdToURIFromAccessForTypeAndVersion.class, + LocationIdToURIFromAccessForTypeAndVersion.class).build( + LocationIdToURIFromAccessForTypeAndVersion.Factory.class)); + } + + @Provides + @Singleton + public Supplier provide() { + return Suppliers.ofInstance(new ParseAccessTest().expected()); + } + + }).getInstance(LocationIdToURIFromAccessForTypeAndVersion.Factory.class); + + public void testRegionUnmatchesOkWhenNoVersionIdSet() { + assertEquals(Maps.transformValues(factory.createForApiTypeAndVersion("compute", "1.1").get(), Suppliers + . supplierFunction()), ImmutableMap.of("az-1.region-a.geo-1", URI + .create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456"), "az-2.region-a.geo-1", URI + .create("https://az-2.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456"), "az-3.region-a.geo-1", URI + .create("https://az-3.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456"))); + } + + public void testRegionMatches() { + assertEquals(Maps.transformValues(factory.createForApiTypeAndVersion("compute", "1.1").get(), Suppliers + . supplierFunction()), ImmutableMap.of("az-1.region-a.geo-1", URI + .create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456"), "az-2.region-a.geo-1", URI + .create("https://az-2.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456"), "az-3.region-a.geo-1", URI + .create("https://az-3.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456"))); + } + + private final LocationIdToURIFromAccessForTypeAndVersion.Factory raxFactory = Guice.createInjector(new AbstractModule() { + + @Override + protected void configure() { + bindConstant().annotatedWith(Provider.class).to("rackspace"); + bind(new TypeLiteral>() { + }).annotatedWith(Provider.class).toInstance(Suppliers.ofInstance(URI.create("https://identity"))); + bind(new TypeLiteral>(){}).to(EndpointToRegion.class); + install(new FactoryModuleBuilder().implement(LocationIdToURIFromAccessForTypeAndVersion.class, + LocationIdToURIFromAccessForTypeAndVersion.class).build( + LocationIdToURIFromAccessForTypeAndVersion.Factory.class)); + } + + @Provides + @Singleton + public Supplier provide() { + return Suppliers.ofInstance(new ParseRackspaceAccessTest().expected()); + } + }).getInstance(LocationIdToURIFromAccessForTypeAndVersion.Factory.class); + + @Test(expectedExceptions = NoSuchElementException.class) + public void testWhenNotInList() { + assertEquals(Maps.transformValues(raxFactory.createForApiTypeAndVersion("goo", "1.0").get(), Suppliers + . supplierFunction()), ImmutableMap.of("rackspace", URI + .create("https://servers.api.rackspacecloud.com/v1.0/40806637803162"))); + } + + public void testProviderWhenNoRegions() { + Map withNoRegions = Maps.transformValues(raxFactory.createForApiTypeAndVersion("compute", "1.0") + .get(), Suppliers. supplierFunction()); + assertEquals(withNoRegions, ImmutableMap.of("rackspace", URI + .create("https://servers.api.rackspacecloud.com/v1.0/40806637803162"))); + } + + public void testOkWithNoVersions() { + assertEquals(Maps.transformValues(raxFactory.createForApiTypeAndVersion("rax:database", null).get(), Suppliers + . supplierFunction()), ImmutableMap.of("DFW", URI + .create("https://dfw.databases.api.rackspacecloud.com/v1.0/40806637803162"), "ORD", URI + .create("https://ord.databases.api.rackspacecloud.com/v1.0/40806637803162"))); + } + +} diff --git a/apis/openstack-nova/src/test/resources/access_version_uids.json b/apis/openstack-keystone/src/test/resources/access_version_uids.json similarity index 100% rename from apis/openstack-nova/src/test/resources/access_version_uids.json rename to apis/openstack-keystone/src/test/resources/access_version_uids.json diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/OverrideApiVersionExpectTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/EndpointIdIsRandomExpectTest.java similarity index 72% rename from apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/OverrideApiVersionExpectTest.java rename to apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/EndpointIdIsRandomExpectTest.java index 5b2c021de0..d58db693a4 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/OverrideApiVersionExpectTest.java +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/EndpointIdIsRandomExpectTest.java @@ -18,11 +18,11 @@ */ package org.jclouds.openstack.nova.v2_0; +import static org.jclouds.Constants.PROPERTY_ENDPOINT; import static org.testng.Assert.assertEquals; import java.util.Properties; -import org.jclouds.Constants; import org.jclouds.http.HttpRequest; import org.jclouds.http.HttpResponse; import org.jclouds.openstack.nova.v2_0.internal.BaseNovaApiExpectTest; @@ -31,13 +31,14 @@ import org.testng.annotations.Test; import com.google.common.collect.ImmutableSet; /** - * + * Tests to ensure that we can pick the only endpoint of a service + * * @author Adrian Cole */ -@Test(groups = "unit", testName = "OverrideApiVersionExpectTest") -public class OverrideApiVersionExpectTest extends BaseNovaApiExpectTest { +@Test(groups = "unit", testName = "EndpointIdIsRandomExpectTest") +public class EndpointIdIsRandomExpectTest extends BaseNovaApiExpectTest { - public OverrideApiVersionExpectTest() { + public EndpointIdIsRandomExpectTest() { this.identity = "demo:demo"; this.credential = "password"; } @@ -45,20 +46,21 @@ public class OverrideApiVersionExpectTest extends BaseNovaApiExpectTest { @Override protected Properties setupProperties() { Properties overrides = super.setupProperties(); - overrides.setProperty(Constants.PROPERTY_ENDPOINT, "http://10.10.10.10:5000/v2.0/"); - overrides.setProperty(provider + ".api-version", "bb3ce9ccdc5045909882688b90cc3ff0"); + overrides.setProperty(PROPERTY_ENDPOINT, "http://10.10.10.10:5000/v2.0/"); return overrides; } public void testVersionMatchOnConfiguredZonesWhenResponseIs2xx() { - - HttpRequest authenticate = HttpRequest.builder().method("POST") + + HttpRequest authenticate = HttpRequest + .builder() + .method("POST") .endpoint("http://10.10.10.10:5000/v2.0/tokens") .addHeader("Accept", "application/json") - .payload(payloadFromStringWithContentType( - "{\"auth\":{\"passwordCredentials\":{\"username\":\"demo\",\"password\":\"password\"},\"tenantName\":\"demo\"}}" - , "application/json")).build(); - + .payload( + payloadFromStringWithContentType( + "{\"auth\":{\"passwordCredentials\":{\"username\":\"demo\",\"password\":\"password\"},\"tenantName\":\"demo\"}}", + "application/json")).build(); HttpResponse authenticationResponse = HttpResponse.builder().statusCode(200) .payload(payloadFromResourceWithContentType("/access_version_uids.json", "application/json")).build();