diff --git a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v1_1/ServiceAsyncClient.java b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v1_1/AuthenticationAsyncClient.java similarity index 93% rename from common/openstack/src/main/java/org/jclouds/openstack/keystone/v1_1/ServiceAsyncClient.java rename to common/openstack/src/main/java/org/jclouds/openstack/keystone/v1_1/AuthenticationAsyncClient.java index d47967c2c8..be75da1b5a 100644 --- a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v1_1/ServiceAsyncClient.java +++ b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v1_1/AuthenticationAsyncClient.java @@ -35,17 +35,17 @@ import com.google.common.util.concurrent.ListenableFuture; * Provides asynchronous access to Service via their REST API. *

* - * @see ServiceClient + * @see AuthenticationClient * @see * @author Adrian Cole */ @Path("/v1.1") -public interface ServiceAsyncClient { +public interface AuthenticationAsyncClient { /** - * @see ServiceClient#authenticate + * @see AuthenticationClient#authenticate */ @POST @SelectJson("auth") diff --git a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v1_1/ServiceClient.java b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v1_1/AuthenticationClient.java similarity index 95% rename from common/openstack/src/main/java/org/jclouds/openstack/keystone/v1_1/ServiceClient.java rename to common/openstack/src/main/java/org/jclouds/openstack/keystone/v1_1/AuthenticationClient.java index 5bbb520611..0e51289ad8 100644 --- a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v1_1/ServiceClient.java +++ b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v1_1/AuthenticationClient.java @@ -27,13 +27,13 @@ import org.jclouds.openstack.keystone.v1_1.domain.Auth; * Provides synchronous access to the KeyStone Service API. *

* - * @see ServiceAsyncClient + * @see AuthenticationAsyncClient * @see * @author Adrian Cole */ @Timeout(duration = 30, timeUnit = TimeUnit.SECONDS) -public interface ServiceClient { +public interface AuthenticationClient { /** * Authenticate to generate a token. diff --git a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v1_1/config/AuthenticationServiceModule.java b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v1_1/config/AuthenticationServiceModule.java index ba16265d27..4f70bc4c0f 100644 --- a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v1_1/config/AuthenticationServiceModule.java +++ b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v1_1/config/AuthenticationServiceModule.java @@ -35,8 +35,8 @@ import org.jclouds.http.annotation.ClientError; import org.jclouds.location.Provider; import org.jclouds.location.suppliers.RegionIdToURISupplier; import org.jclouds.openstack.Authentication; -import org.jclouds.openstack.keystone.v1_1.ServiceAsyncClient; -import org.jclouds.openstack.keystone.v1_1.ServiceClient; +import org.jclouds.openstack.keystone.v1_1.AuthenticationAsyncClient; +import org.jclouds.openstack.keystone.v1_1.AuthenticationClient; import org.jclouds.openstack.keystone.v1_1.domain.Auth; import org.jclouds.openstack.keystone.v1_1.handlers.RetryOnRenew; import org.jclouds.openstack.keystone.v1_1.suppliers.RegionIdToURIFromAuthForServiceSupplier; @@ -63,7 +63,7 @@ public class AuthenticationServiceModule extends AbstractModule { }).to(GetAuth.class); // ServiceClient is used directly for filters and retry handlers, so let's bind it // explicitly - bindClientAndAsyncClient(binder(), ServiceClient.class, ServiceAsyncClient.class); + bindClientAndAsyncClient(binder(), AuthenticationClient.class, AuthenticationAsyncClient.class); install(new FactoryModuleBuilder().implement(RegionIdToURISupplier.class, RegionIdToURIFromAuthForServiceSupplier.class).build(RegionIdToURISupplier.Factory.class)); bind(HttpRetryHandler.class).annotatedWith(ClientError.class).to(RetryOnRenew.class); @@ -88,7 +88,7 @@ public class AuthenticationServiceModule extends AbstractModule { public static class GetAuth extends RetryOnTimeOutExceptionFunction { @Inject - public GetAuth(final ServiceClient client) { + public GetAuth(final AuthenticationClient client) { super(new Function() { @Override diff --git a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/ServiceAsyncClient.java b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/AuthenticationAsyncClient.java similarity index 90% rename from common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/ServiceAsyncClient.java rename to common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/AuthenticationAsyncClient.java index 31c1ffcd70..2f160ed365 100644 --- a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/ServiceAsyncClient.java +++ b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/AuthenticationAsyncClient.java @@ -37,16 +37,16 @@ import com.google.common.util.concurrent.ListenableFuture; * Provides asynchronous access to Service via their REST API. *

* - * @see ServiceClient + * @see AuthenticationClient * @see * @author Adrian Cole */ @Path("/v2.0") -public interface ServiceAsyncClient { +public interface AuthenticationAsyncClient { /** - * @see ServiceClient#authenticateTenantWithCredentials(String,PasswordCredentials) + * @see AuthenticationClient#authenticateTenantWithCredentials(String,PasswordCredentials) */ @POST @SelectJson("access") @@ -57,7 +57,7 @@ public interface ServiceAsyncClient { PasswordCredentials passwordCredentials); /** - * @see ServiceClient#authenticateTenantWithCredentials(String,ApiAccessKeyCredentials) + * @see AuthenticationClient#authenticateTenantWithCredentials(String,ApiAccessKeyCredentials) */ @POST @SelectJson("access") diff --git a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/ServiceClient.java b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/AuthenticationClient.java similarity index 96% rename from common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/ServiceClient.java rename to common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/AuthenticationClient.java index 0e53e6067a..695b55f527 100644 --- a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/ServiceClient.java +++ b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/AuthenticationClient.java @@ -29,13 +29,13 @@ import org.jclouds.openstack.keystone.v2_0.domain.PasswordCredentials; * Provides synchronous access to the KeyStone Service API. *

* - * @see ServiceAsyncClient + * @see AuthenticationAsyncClient * @see * @author Adrian Cole */ @Timeout(duration = 30, timeUnit = TimeUnit.SECONDS) -public interface ServiceClient { +public interface AuthenticationClient { /** * Authenticate to generate a token. diff --git a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/config/KeystoneAuthenticationModule.java b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/config/KeystoneAuthenticationModule.java index 4730cf4efd..21b1733918 100644 --- a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/config/KeystoneAuthenticationModule.java +++ b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/config/KeystoneAuthenticationModule.java @@ -40,12 +40,14 @@ import org.jclouds.location.suppliers.ZoneIdsSupplier; import org.jclouds.location.suppliers.derived.RegionIdsFromRegionIdToURIKeySet; import org.jclouds.location.suppliers.derived.ZoneIdsFromZoneIdToURIKeySet; import org.jclouds.openstack.Authentication; -import org.jclouds.openstack.keystone.v2_0.ServiceAsyncClient; -import org.jclouds.openstack.keystone.v2_0.ServiceClient; +import org.jclouds.openstack.keystone.v2_0.AuthenticationAsyncClient; +import org.jclouds.openstack.keystone.v2_0.AuthenticationClient; import org.jclouds.openstack.keystone.v2_0.domain.Access; import org.jclouds.openstack.keystone.v2_0.functions.AuthenticateApiAccessKeyCredentials; import org.jclouds.openstack.keystone.v2_0.functions.AuthenticatePasswordCredentials; import org.jclouds.openstack.keystone.v2_0.handlers.RetryOnRenew; +import org.jclouds.openstack.keystone.v2_0.suppliers.RegionIdToAdminURIFromAccessForTypeAndVersionSupplier; +import org.jclouds.openstack.keystone.v2_0.suppliers.RegionIdToAdminURISupplier; import org.jclouds.openstack.keystone.v2_0.suppliers.RegionIdToURIFromAccessForTypeAndVersionSupplier; import org.jclouds.openstack.keystone.v2_0.suppliers.ZoneIdToURIFromAccessForTypeAndVersionSupplier; import org.jclouds.rest.annotations.ApiVersion; @@ -85,6 +87,8 @@ public class KeystoneAuthenticationModule extends AbstractModule { protected void configure() { install(new FactoryModuleBuilder().implement(RegionIdToURISupplier.class, RegionIdToURIFromAccessForTypeAndVersionSupplier.class).build(RegionIdToURISupplier.Factory.class)); + install(new FactoryModuleBuilder().implement(RegionIdToAdminURISupplier.class, + RegionIdToAdminURIFromAccessForTypeAndVersionSupplier.class).build(RegionIdToAdminURISupplier.Factory.class)); // dynamically build the region list as opposed to from properties bind(RegionIdsSupplier.class).to(RegionIdsFromRegionIdToURIKeySet.class); } @@ -98,6 +102,16 @@ public class KeystoneAuthenticationModule extends AbstractModule { RegionIdToURISupplier.Factory factory) { return factory.createForApiTypeAndVersion(serviceType, apiVersion); } + + // supply the region to id to AdminURL map from keystone, based on the servicetype and api version in + // config + @Provides + @Singleton + protected RegionIdToAdminURISupplier provideRegionIdToAdminURISupplierForApiVersion( + @Named(KeystoneProperties.SERVICE_TYPE) String serviceType, @ApiVersion String apiVersion, + RegionIdToAdminURISupplier.Factory factory) { + return factory.createForApiTypeAndVersion(serviceType, apiVersion); + } } @@ -132,7 +146,7 @@ public class KeystoneAuthenticationModule extends AbstractModule { bind(CredentialType.class).toProvider(CredentialTypeFromPropertyOrDefault.class); // ServiceClient is used directly for filters and retry handlers, so let's bind it // explicitly - bindClientAndAsyncClient(binder(), ServiceClient.class, ServiceAsyncClient.class); + bindClientAndAsyncClient(binder(), AuthenticationClient.class, AuthenticationAsyncClient.class); install(locationModule); } diff --git a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/ApiMetadata.java b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/ApiMetadata.java new file mode 100644 index 0000000000..61057d76da --- /dev/null +++ b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/ApiMetadata.java @@ -0,0 +1,170 @@ +/** + * 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.domain; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Collections; +import java.util.Date; +import java.util.Set; + +import org.jclouds.javax.annotation.Nullable; +import org.jclouds.openstack.domain.Link; +import org.jclouds.openstack.domain.Resource; + +import com.google.common.base.Objects; +import com.google.common.base.Objects.ToStringHelper; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Sets; +import com.google.gson.annotations.SerializedName; + +/** + * Class ApiMetadata + */ +public class ApiMetadata extends Resource { + + public static Builder builder() { + return new Builder(); + } + + public Builder toBuilder() { + return new Builder().fromApiMetadata(this); + } + + public static class Builder extends Resource.Builder { + private String status; + private Date updated; + private Set mediaTypes = Sets.newLinkedHashSet(); + + public Builder status(String status) { + this.status = status; + return this; + } + + public Builder updated(Date updated) { + this.updated = updated; + return this; + } + + public Builder mediaTypes(Set mediaTypes) { + this.mediaTypes = mediaTypes; + return this; + } + + public ApiMetadata build() { + return new ApiMetadata(id, name, links, updated, status, mediaTypes); + } + + public Builder fromApiMetadata(ApiMetadata in) { + return fromResource(in) + .status(in.getStatus()) + .updated(in.getUpdated()) + .mediaTypes(in.getMediaTypes()); + } + + /** + * {@inheritDoc} + */ + @Override + public Builder id(String id) { + return Builder.class.cast(super.id(id)); + } + + /** + * {@inheritDoc} + */ + @Override + public Builder name(String name) { + return Builder.class.cast(super.name(name)); + } + + /** + * {@inheritDoc} + */ + @Override + public Builder links(Set links) { + return Builder.class.cast(super.links(links)); + } + + /** + * {@inheritDoc} + */ + @Override + public Builder fromResource(Resource in) { + return Builder.class.cast(super.fromResource(in)); + } + } + + private final String status; + private final Date updated; + @SerializedName("media-types") + private final Set mediaTypes; + + protected ApiMetadata(String id, String name, Set links, Date updated, String status, Set mediaTypes) { + super(id, name, links); + this.status = status; + this.updated = updated; + this.mediaTypes = ImmutableSet.copyOf(checkNotNull(mediaTypes, "mediaTypes")); + } + + /** + */ + @Nullable + public String getStatus() { + return this.status; + } + + /** + */ + @Nullable + public Date getUpdated() { + return this.updated; + } + + /** + */ + @Nullable + public Set getMediaTypes() { + return Collections.unmodifiableSet(this.mediaTypes); + } + + @Override + public int hashCode() { + return Objects.hashCode(status, updated, mediaTypes); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null || getClass() != obj.getClass()) return false; + ApiMetadata that = ApiMetadata.class.cast(obj); + return Objects.equal(this.status, that.status) + && Objects.equal(this.updated, that.updated) + && Objects.equal(this.mediaTypes, that.mediaTypes) + ; + } + + protected ToStringHelper string() { + return super.string() + .add("status", status) + .add("updated", updated) + .add("mediaTypes", mediaTypes); + } + +} \ No newline at end of file diff --git a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Endpoint.java b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Endpoint.java index f42e86b1e1..0b57843d22 100644 --- a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Endpoint.java +++ b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Endpoint.java @@ -55,6 +55,7 @@ public class Endpoint implements Comparable { protected String region; protected URI publicURL; protected URI internalURL; + protected URI adminURL; protected String tenantId; /** @@ -89,6 +90,14 @@ public class Endpoint implements Comparable { return this; } + /** + * @see Endpoint#getInternalURL() + */ + public Builder adminURL(URI adminURL) { + this.adminURL = checkNotNull(adminURL, "adminURL"); + return this; + } + /** * @see Endpoint#getTenantId() */ @@ -98,7 +107,7 @@ public class Endpoint implements Comparable { } public Endpoint build() { - return new Endpoint(versionId, region, publicURL, internalURL, tenantId); + return new Endpoint(versionId, region, publicURL, internalURL, adminURL, tenantId); } public Builder fromEndpoint(Endpoint from) { @@ -113,17 +122,20 @@ public class Endpoint implements Comparable { protected final String region; protected final URI publicURL; protected final URI internalURL; + protected final URI adminURL; + // renamed half-way through @Deprecated protected String tenantName; protected final String tenantId; protected Endpoint(String versionId, String region, @Nullable URI publicURL, @Nullable URI internalURL, - @Nullable String tenantId) { + @Nullable URI adminURL, @Nullable String tenantId) { this.versionId = checkNotNull(versionId, "versionId"); this.region = checkNotNull(region, "region"); this.publicURL = publicURL; this.internalURL = internalURL; + this.adminURL = adminURL; this.tenantId = tenantId; } @@ -160,6 +172,14 @@ public class Endpoint implements Comparable { return internalURL; } + /** + * @return the admin url of the endpoint + */ + @Nullable + public URI getAdminURL() { + return adminURL; + } + /** * @return the tenant versionId of the endpoint or null */ @@ -176,7 +196,7 @@ public class Endpoint implements Comparable { if (object instanceof Endpoint) { final Endpoint other = Endpoint.class.cast(object); return equal(getVersionId(), other.getVersionId()) && equal(region, other.region) && equal(publicURL, other.publicURL) - && equal(internalURL, other.internalURL) && equal(getTenantId(), other.getTenantId()); + && equal(internalURL, other.internalURL) && equal(adminURL, other.adminURL) && equal(getTenantId(), other.getTenantId()); } else { return false; } @@ -184,13 +204,13 @@ public class Endpoint implements Comparable { @Override public int hashCode() { - return Objects.hashCode(getVersionId(), region, publicURL, internalURL, getTenantId()); + return Objects.hashCode(getVersionId(), region, publicURL, internalURL, adminURL, getTenantId()); } @Override public String toString() { return toStringHelper("").add("versionId", getVersionId()).add("region", region).add("publicURL", publicURL).add("internalURL", - internalURL).add("tenantId", getTenantId()).toString(); + internalURL).add("adminURL", adminURL).add("tenantId", getTenantId()).toString(); } @Override diff --git a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/MediaType.java b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/MediaType.java new file mode 100644 index 0000000000..cb3cf6d071 --- /dev/null +++ b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/MediaType.java @@ -0,0 +1,111 @@ +/** + * 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.domain; + +import org.jclouds.javax.annotation.Nullable; + +import com.google.common.base.Objects; +import com.google.common.base.Objects.ToStringHelper; + +/** + * Class MediaType + */ +public class MediaType { + + public static Builder builder() { + return new Builder(); + } + + public Builder toBuilder() { + return builder().fromMediaType(this); + } + + public static class Builder { + + private String base; + private String type; + + public Builder base(String base) { + this.base = base; + return this; + } + + public Builder type(String type) { + this.type = type; + return this; + } + + public MediaType build() { + return new MediaType(this); + } + + public Builder fromMediaType(MediaType in) { + return this.base(in.getBase()).type(in.getType()); + } + } + + private final String base; + private final String type; + + protected MediaType(Builder builder) { + this.base = builder.base; + this.type = builder.type; + } + + /** + */ + @Nullable + public String getBase() { + return this.base; + } + + /** + */ + @Nullable + public String getType() { + return this.type; + } + + @Override + public int hashCode() { + return Objects.hashCode(base, type); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null || getClass() != obj.getClass()) return false; + MediaType that = MediaType.class.cast(obj); + return Objects.equal(this.base, that.base) + && Objects.equal(this.type, that.type) + ; + } + + protected ToStringHelper string() { + return Objects.toStringHelper("") + .add("base", base) + .add("type", type); + } + + @Override + public String toString() { + return string().toString(); + } + +} \ No newline at end of file diff --git a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Service.java b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Service.java index 77a3bc27cf..6f8d579f74 100644 --- a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Service.java +++ b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Service.java @@ -52,7 +52,7 @@ public class Service implements Comparable { protected Set endpoints = ImmutableSet.of(); /** - * @see Service#getId() + * @see Service#getType() */ public Builder type(String type) { this.type = checkNotNull(type, "type"); diff --git a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Tenant.java b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Tenant.java index 840892e338..35f884629c 100644 --- a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Tenant.java +++ b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Tenant.java @@ -23,12 +23,16 @@ import static com.google.common.base.Objects.equal; import static com.google.common.base.Objects.toStringHelper; import static com.google.common.base.Preconditions.checkNotNull; +import javax.ws.rs.DefaultValue; + +import org.jclouds.javax.annotation.Nullable; + import com.google.common.base.Objects; /** * A container used to group or isolate resources and/or identity objects. Depending on the service * operator, a tenant may map to a customer, account, organization, or project. - * + * * @author Adrian Cole * @see @@ -46,6 +50,7 @@ public class Tenant implements Comparable { public static class Builder { protected String id; protected String name; + protected String description; /** * @see Tenant#getId() @@ -63,8 +68,16 @@ public class Tenant implements Comparable { return this; } + /** + * @see Tenant#getDescription() + */ + public Builder description(String description) { + this.description = description; + return this; + } + public Tenant build() { - return new Tenant(id, name); + return new Tenant(id, name, description); } public Builder fromTenant(Tenant from) { @@ -74,15 +87,17 @@ public class Tenant implements Comparable { protected final String id; protected final String name; + protected final String description; - public Tenant(String id, String name) { + protected Tenant(String id, String name, String description) { this.id = checkNotNull(id, "id"); this.name = checkNotNull(name, "name"); + this.description = description; } /** * When providing an ID, it is assumed that the tenant exists in the current OpenStack deployment - * + * * @return the id of the tenant in the current OpenStack deployment */ public String getId() { @@ -96,6 +111,14 @@ public class Tenant implements Comparable { return name; } + /** + * @return the description of the tenant + */ + @Nullable + public String getDescription() { + return description; + } + @Override public boolean equals(Object object) { if (this == object) { @@ -103,7 +126,8 @@ public class Tenant implements Comparable { } if (object instanceof Tenant) { final Tenant other = Tenant.class.cast(object); - return equal(id, other.id) && equal(name, other.name); + return equal(id, other.id) && equal(name, other.name) + && equal(description, other.description); } else { return false; } @@ -111,12 +135,12 @@ public class Tenant implements Comparable { @Override public int hashCode() { - return Objects.hashCode(id, name); + return Objects.hashCode(id, name, description); } @Override public String toString() { - return toStringHelper("").add("id", id).add("name", name).toString(); + return toStringHelper("").add("id", id).add("name", name).add("description", description).toString(); } @Override diff --git a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/User.java b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/User.java index 0ce93ccec3..e8d5ff7dcb 100644 --- a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/User.java +++ b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/User.java @@ -98,11 +98,17 @@ public class User implements Comparable { protected final String name; protected final Set roles; - public User(String id, String name, Set roles) { + protected User(String id, String name, Set roles) { this.id = checkNotNull(id, "id"); this.name = checkNotNull(name, "name"); this.roles = ImmutableSet.copyOf(checkNotNull(roles, "roles")); } + + protected User() { + id = null; + name = null; + roles = ImmutableSet.of(); + } /** * When providing an ID, it is assumed that the user exists in the current OpenStack deployment diff --git a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/functions/AdminURL.java b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/functions/AdminURL.java new file mode 100644 index 0000000000..b4454b6497 --- /dev/null +++ b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/functions/AdminURL.java @@ -0,0 +1,43 @@ +/** + * 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.functions; + +import java.net.URI; + +import javax.inject.Singleton; + +import org.jclouds.openstack.keystone.v2_0.domain.Endpoint; + +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; + +/** + * @author Adam Lowe + */ +@Singleton +public class AdminURL implements EndpointToSupplierAdminURI { + @Override + public Supplier apply(Endpoint input) { + return Suppliers.ofInstance(input.getAdminURL()); + } + + public String toString() { + return "supplyAdminURL()"; + } +} \ No newline at end of file diff --git a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/functions/AuthenticateApiAccessKeyCredentials.java b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/functions/AuthenticateApiAccessKeyCredentials.java index b62cb33d69..29be1b1ded 100644 --- a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/functions/AuthenticateApiAccessKeyCredentials.java +++ b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/functions/AuthenticateApiAccessKeyCredentials.java @@ -22,7 +22,7 @@ import javax.inject.Inject; import javax.inject.Singleton; import org.jclouds.domain.Credentials; -import org.jclouds.openstack.keystone.v2_0.ServiceClient; +import org.jclouds.openstack.keystone.v2_0.AuthenticationClient; import org.jclouds.openstack.keystone.v2_0.domain.Access; import org.jclouds.openstack.keystone.v2_0.domain.ApiAccessKeyCredentials; import org.jclouds.rest.AuthorizationException; @@ -31,10 +31,10 @@ import com.google.common.base.Function; @Singleton public class AuthenticateApiAccessKeyCredentials implements Function { - private final ServiceClient client; + private final AuthenticationClient client; @Inject - public AuthenticateApiAccessKeyCredentials(ServiceClient client) { + public AuthenticateApiAccessKeyCredentials(AuthenticationClient client) { this.client = client; } diff --git a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/functions/AuthenticatePasswordCredentials.java b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/functions/AuthenticatePasswordCredentials.java index 0191266ead..0c59e02ede 100644 --- a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/functions/AuthenticatePasswordCredentials.java +++ b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/functions/AuthenticatePasswordCredentials.java @@ -20,7 +20,7 @@ package org.jclouds.openstack.keystone.v2_0.functions; import javax.inject.Inject; import org.jclouds.domain.Credentials; -import org.jclouds.openstack.keystone.v2_0.ServiceClient; +import org.jclouds.openstack.keystone.v2_0.AuthenticationClient; import org.jclouds.openstack.keystone.v2_0.domain.Access; import org.jclouds.openstack.keystone.v2_0.domain.PasswordCredentials; import org.jclouds.rest.AuthorizationException; @@ -28,10 +28,10 @@ import org.jclouds.rest.AuthorizationException; import com.google.common.base.Function; public class AuthenticatePasswordCredentials implements Function { - private final ServiceClient client; + private final AuthenticationClient client; @Inject - public AuthenticatePasswordCredentials(ServiceClient client) { + public AuthenticatePasswordCredentials(AuthenticationClient client) { this.client = client; } diff --git a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/functions/EndpointToSupplierAdminURI.java b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/functions/EndpointToSupplierAdminURI.java new file mode 100644 index 0000000000..2061a2dc11 --- /dev/null +++ b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/functions/EndpointToSupplierAdminURI.java @@ -0,0 +1,28 @@ +/** + * 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.functions; + +import com.google.inject.ImplementedBy; + +/** + * @author Adam Lowe + */ +@ImplementedBy(AdminURL.class) +public interface EndpointToSupplierAdminURI extends EndpointToSupplierURI { +} \ No newline at end of file diff --git a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/functions/RegionToAdminEndpointURI.java b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/functions/RegionToAdminEndpointURI.java new file mode 100644 index 0000000000..a98bf9d87f --- /dev/null +++ b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/functions/RegionToAdminEndpointURI.java @@ -0,0 +1,16 @@ +package org.jclouds.openstack.keystone.v2_0.functions; + +import javax.inject.Inject; + +import org.jclouds.location.functions.RegionToEndpoint; +import org.jclouds.openstack.keystone.v2_0.suppliers.RegionIdToAdminURISupplier; + +/** + * @author Adam Lowe + */ +public class RegionToAdminEndpointURI extends RegionToEndpoint { + @Inject + public RegionToAdminEndpointURI(RegionIdToAdminURISupplier regionToEndpointSupplier) { + super(regionToEndpointSupplier); + } +} diff --git a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/suppliers/RegionIdToAdminURIFromAccessForTypeAndVersionSupplier.java b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/suppliers/RegionIdToAdminURIFromAccessForTypeAndVersionSupplier.java new file mode 100644 index 0000000000..74d15f5be0 --- /dev/null +++ b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/suppliers/RegionIdToAdminURIFromAccessForTypeAndVersionSupplier.java @@ -0,0 +1,45 @@ +/** + * 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 javax.inject.Inject; +import javax.inject.Singleton; + +import org.jclouds.openstack.keystone.v2_0.domain.Access; +import org.jclouds.openstack.keystone.v2_0.functions.EndpointToRegion; +import org.jclouds.openstack.keystone.v2_0.functions.EndpointToSupplierAdminURI; + +import com.google.common.base.Supplier; +import com.google.inject.assistedinject.Assisted; + +@Singleton +public class RegionIdToAdminURIFromAccessForTypeAndVersionSupplier extends + LocationIdToURIFromAccessForTypeAndVersionSupplier implements RegionIdToAdminURISupplier { + @Inject + public RegionIdToAdminURIFromAccessForTypeAndVersionSupplier(Supplier access, + EndpointToSupplierAdminURI endpointToSupplierURI, EndpointToRegion endpointToRegion, + @Assisted("apiType") String apiType, @Assisted("apiVersion") String apiVersion) { + super(access, endpointToSupplierURI, endpointToRegion, apiType, apiVersion); + } + + @Override + public String toString() { + return "regionIdToAdminURIFromAccessForTypeAndVersion(" + apiType + ", " + apiVersion + ")"; + } +} \ No newline at end of file diff --git a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/suppliers/RegionIdToAdminURISupplier.java b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/suppliers/RegionIdToAdminURISupplier.java new file mode 100644 index 0000000000..9f2b1c15b7 --- /dev/null +++ b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/suppliers/RegionIdToAdminURISupplier.java @@ -0,0 +1,28 @@ +package org.jclouds.openstack.keystone.v2_0.suppliers; + +import java.net.URI; +import java.util.Map; + +import com.google.common.base.Supplier; +import com.google.inject.ImplementedBy; +import com.google.inject.assistedinject.Assisted; + +/** + * @author Adam Lowe + */ +@ImplementedBy(RegionIdToAdminURIFromAccessForTypeAndVersionSupplier.class) +public interface RegionIdToAdminURISupplier extends Supplier>> { + 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 + * @return regions mapped to default uri + */ + RegionIdToAdminURISupplier createForApiTypeAndVersion(@Assisted("apiType") String apiType, + @Assisted("apiVersion") String apiVersion); + } +} diff --git a/common/openstack/src/main/java/org/jclouds/openstack/services/ServiceType.java b/common/openstack/src/main/java/org/jclouds/openstack/services/ServiceType.java index 106150cb47..4d97aec0fd 100644 --- a/common/openstack/src/main/java/org/jclouds/openstack/services/ServiceType.java +++ b/common/openstack/src/main/java/org/jclouds/openstack/services/ServiceType.java @@ -41,4 +41,8 @@ public interface ServiceType { * Image Service (Glance) */ public static final String IMAGE = "image"; + /** + * Identity Service (Keystone) + */ + public static final String IDENTITY = "identity"; } \ No newline at end of file diff --git a/common/openstack/src/test/java/org/jclouds/openstack/keystone/v2_0/functions/AdminURLTest.java b/common/openstack/src/test/java/org/jclouds/openstack/keystone/v2_0/functions/AdminURLTest.java new file mode 100644 index 0000000000..33021208b7 --- /dev/null +++ b/common/openstack/src/test/java/org/jclouds/openstack/keystone/v2_0/functions/AdminURLTest.java @@ -0,0 +1,50 @@ +/** + * 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.functions; + +import static org.testng.Assert.assertEquals; + +import java.net.URI; + +import org.jclouds.openstack.keystone.v2_0.domain.Endpoint; +import org.testng.annotations.Test; + +/** + * @author Adam Lowe + */ +@Test(groups = "unit", testName = "AdminURLTest") +public class AdminURLTest { + private final AdminURL fn = new AdminURL(); + + public void testAdminURL() { + assertEquals(fn.apply( + Endpoint.builder().region("LON").versionId("1.0").adminURL( + URI.create("https://cdn3.clouddrive.com/v1/MossoCloudFS_83a9d536-2e25-4166-bd3b-a503a934f953")) + .build()).get(), URI + .create("https://cdn3.clouddrive.com/v1/MossoCloudFS_83a9d536-2e25-4166-bd3b-a503a934f953")); + } + + public void testPublicURLNullReturnsInternalURL() { + assertEquals(fn + .apply( + Endpoint.builder().region("lon").versionId("1.0") + .internalURL(URI.create("https://192.168.1.1")).build()).get(), null); + } + +} diff --git a/common/openstack/src/test/java/org/jclouds/openstack/keystone/v2_0/parse/ParseAccessTest.java b/common/openstack/src/test/java/org/jclouds/openstack/keystone/v2_0/parse/ParseAccessTest.java index bf7492dcf6..146a376533 100644 --- a/common/openstack/src/test/java/org/jclouds/openstack/keystone/v2_0/parse/ParseAccessTest.java +++ b/common/openstack/src/test/java/org/jclouds/openstack/keystone/v2_0/parse/ParseAccessTest.java @@ -67,11 +67,13 @@ public class ParseAccessTest extends BaseItemParserTest { 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/")).region( - "region-a.geo-1").versionId("2.0").build()).build(), + 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( diff --git a/common/openstack/src/test/java/org/jclouds/openstack/keystone/v2_0/parse/ParseApiMetadataTest.java b/common/openstack/src/test/java/org/jclouds/openstack/keystone/v2_0/parse/ParseApiMetadataTest.java new file mode 100644 index 0000000000..83718ee799 --- /dev/null +++ b/common/openstack/src/test/java/org/jclouds/openstack/keystone/v2_0/parse/ParseApiMetadataTest.java @@ -0,0 +1,70 @@ +/** + * 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 java.net.URI; + +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.domain.Link; +import org.jclouds.openstack.keystone.v2_0.domain.Access; +import org.jclouds.openstack.keystone.v2_0.domain.ApiMetadata; +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; + +import com.google.common.collect.ImmutableSet; + +/** + * @author Adam Lowe + */ +@Test(groups = "unit", testName = "ParseApiMetadataTest") +public class ParseApiMetadataTest extends BaseItemParserTest { + + @Override + public String resource() { + return "/apiMetadataResponse.json"; + } + + @Override + @SelectJson("version") + @Consumes(MediaType.APPLICATION_JSON) + public ApiMetadata expected() { + return ApiMetadata.builder().id("v2.0") + .links(ImmutableSet.of(Link.builder().relation(Link.Relation.SELF).href(URI.create("http://172.16.89.140:5000/v2.0/")).build(), + Link.builder().relation(Link.Relation.DESCRIBEDBY).type("text/html").href(URI.create("http://docs.openstack.org/api/openstack-identity-service/2.0/content/")).build(), + Link.builder().relation(Link.Relation.DESCRIBEDBY).type("application/pdf").href(URI.create("http://docs.openstack.org/api/openstack-identity-service/2.0/identity-dev-guide-2.0.pdf")).build() + )) + .status("beta") + .updated(new SimpleDateFormatDateService().iso8601SecondsDateParse("2011-11-19T00:00:00Z")) + .mediaTypes(ImmutableSet.of( + org.jclouds.openstack.keystone.v2_0.domain.MediaType.builder().base("application/json").type("application/vnd.openstack.identity-v2.0+json").build(), + org.jclouds.openstack.keystone.v2_0.domain.MediaType.builder().base("application/xml").type("application/vnd.openstack.identity-v2.0+xml").build() + )) + .build(); + } +} diff --git a/common/openstack/src/test/java/org/jclouds/openstack/keystone/v2_0/suppliers/RegionIdToAdminURIFromAccessForTypeAndVersionSupplierTest.java b/common/openstack/src/test/java/org/jclouds/openstack/keystone/v2_0/suppliers/RegionIdToAdminURIFromAccessForTypeAndVersionSupplierTest.java new file mode 100644 index 0000000000..de4453dccc --- /dev/null +++ b/common/openstack/src/test/java/org/jclouds/openstack/keystone/v2_0/suppliers/RegionIdToAdminURIFromAccessForTypeAndVersionSupplierTest.java @@ -0,0 +1,71 @@ +/** + * 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 javax.inject.Singleton; + +import org.jclouds.openstack.keystone.v2_0.domain.Access; +import org.jclouds.openstack.keystone.v2_0.parse.ParseAccessTest; +import org.testng.annotations.Test; + +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.assistedinject.FactoryModuleBuilder; + +/** + * @author Adam Lowe + */ +@Test(groups = "unit", testName = "RegionIdToAdminURIFromAccessForTypeAndVersionSupplierTest") +public class RegionIdToAdminURIFromAccessForTypeAndVersionSupplierTest { + private final RegionIdToAdminURISupplier.Factory factory = Guice.createInjector(new AbstractModule() { + + @Override + protected void configure() { + install(new FactoryModuleBuilder().implement(RegionIdToAdminURISupplier.class, + RegionIdToAdminURIFromAccessForTypeAndVersionSupplier.class).build(RegionIdToAdminURISupplier.Factory.class)); + } + + @SuppressWarnings("unused") + @Provides + @Singleton + public Supplier provide() { + return Suppliers.ofInstance(new ParseAccessTest().expected()); + } + }).getInstance(RegionIdToAdminURISupplier.Factory.class); + + public void testRegionMatches() { + assertEquals(Maps.transformValues(factory.createForApiTypeAndVersion("identity", "2.0").get(), Suppliers + . supplierFunction()), ImmutableMap.of("region-a.geo-1", URI.create("https://csnode.jclouds.org:35357/v2.0/"))); + Map map = Maps.newLinkedHashMap(); + map.put("region-a.geo-1", null); + assertEquals(Maps.transformValues(factory.createForApiTypeAndVersion("compute", "1.1").get(), Suppliers + . supplierFunction()), map); + } + +} diff --git a/common/openstack/src/test/resources/apiMetadataResponse.json b/common/openstack/src/test/resources/apiMetadataResponse.json new file mode 100644 index 0000000000..09934299c9 --- /dev/null +++ b/common/openstack/src/test/resources/apiMetadataResponse.json @@ -0,0 +1 @@ +{"version": {"status": "beta", "updated": "2011-11-19T00:00:00Z", "media-types": [{"base": "application/json", "type": "application/vnd.openstack.identity-v2.0+json"}, {"base": "application/xml", "type": "application/vnd.openstack.identity-v2.0+xml"}], "id": "v2.0", "links": [{"href": "http://172.16.89.140:5000/v2.0/", "rel": "self"}, {"href": "http://docs.openstack.org/api/openstack-identity-service/2.0/content/", "type": "text/html", "rel": "describedby"}, {"href": "http://docs.openstack.org/api/openstack-identity-service/2.0/identity-dev-guide-2.0.pdf", "type": "application/pdf", "rel": "describedby"}]}} \ No newline at end of file diff --git a/common/openstack/src/test/resources/keystoneAuthResponse.json b/common/openstack/src/test/resources/keystoneAuthResponse.json index 4d872152f8..4751ca3d2b 100644 --- a/common/openstack/src/test/resources/keystoneAuthResponse.json +++ b/common/openstack/src/test/resources/keystoneAuthResponse.json @@ -56,9 +56,11 @@ "type": "identity", "endpoints": [ { + "adminURL": "https://csnode.jclouds.org:35357/v2.0/", "publicURL": "https://csnode.jclouds.org/v2.0/", "region": "region-a.geo-1", "id": "2.0", + "versionId":"2.0", "list": "https://csnode.jclouds.org/extension" } ] diff --git a/core/src/test/java/org/jclouds/rest/internal/BaseRestClientExpectTest.java b/core/src/test/java/org/jclouds/rest/internal/BaseRestClientExpectTest.java index 8e77a564be..f4ab26c2c8 100644 --- a/core/src/test/java/org/jclouds/rest/internal/BaseRestClientExpectTest.java +++ b/core/src/test/java/org/jclouds/rest/internal/BaseRestClientExpectTest.java @@ -367,12 +367,15 @@ public abstract class BaseRestClientExpectTest { */ public boolean httpRequestsAreEqual(HttpRequest a, HttpRequest b) { try { + if (a == null || b == null) { + return false; + } + if (a.getPayload() == null || b.getPayload() == null) { + return Objects.equal(a, b); + } + switch (compareHttpRequestAsType(a)) { case XML: { - if (a == null || b == null || a.getPayload() == null || b.getPayload() == null) { - return false; - } - Diff diff = XMLUnit.compareXML(Strings2.toStringAndClose(a.getPayload().getInput()), Strings2 .toStringAndClose(b.getPayload().getInput())); @@ -404,10 +407,7 @@ public abstract class BaseRestClientExpectTest { return diff.identical() && Objects.equal(a.getHeaders(), b.getHeaders()); } - case JSON: { - if (a == null || b == null || a.getPayload() == null || b.getPayload() == null) { - return false; - } + case JSON: { JsonParser parser = new JsonParser(); JsonElement payloadA = parser.parse(Strings2.toStringAndClose(a.getPayload().getInput())); JsonElement payloadB = parser.parse(Strings2.toStringAndClose(b.getPayload().getInput())); diff --git a/labs/openstack-keystone/pom.xml b/labs/openstack-keystone/pom.xml new file mode 100644 index 0000000000..57fd0b756c --- /dev/null +++ b/labs/openstack-keystone/pom.xml @@ -0,0 +1,134 @@ + + + + 4.0.0 + + org.jclouds + jclouds-project + 1.5.0-SNAPSHOT + ../../project/pom.xml + + org.jclouds.labs + openstack-keystone + jcloud openstack-keystone api + jclouds components to access an implementation of OpenStack Keystone + bundle + + + + http://localhost:5000 + + 2.0 + + FIXME_IDENTITY + FIXME_CREDENTIALS + passwordCredentials + + + + + org.jclouds.common + openstack-common + ${project.version} + + + org.jclouds + jclouds-core + ${project.version} + test-jar + test + + + org.jclouds.common + openstack-common + ${project.version} + test-jar + test + + + org.jclouds.driver + jclouds-slf4j + ${project.version} + test + + + ch.qos.logback + logback-classic + 1.0.0 + test + + + + + + live + + + + org.apache.maven.plugins + maven-surefire-plugin + + + integration + integration-test + + test + + + + ${test.openstack-keystone.endpoint} + ${test.openstack-keystone.api-version} + ${test.openstack-keystone.build-version} + ${test.openstack-keystone.identity} + ${test.openstack-keystone.credential} + ${test.jclouds.keystone.credential-type} + + + + + + + + + + + + + + org.apache.felix + maven-bundle-plugin + + + ${project.artifactId} + org.jclouds.openstack.keystone.v2_0*;version="${project.version}" + + org.jclouds.rest.internal;version="${project.version}", + org.jclouds*;version="${project.version}", + * + + + + + + + + diff --git a/labs/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/AdminAsyncClient.java b/labs/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/AdminAsyncClient.java new file mode 100644 index 0000000000..fa58e3b901 --- /dev/null +++ b/labs/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/AdminAsyncClient.java @@ -0,0 +1,177 @@ +/** + * 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; + +import java.util.Set; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.HEAD; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.MediaType; + +import org.jclouds.openstack.filters.AuthenticateRequest; +import org.jclouds.openstack.keystone.v2_0.domain.ApiMetadata; +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.Tenant; +import org.jclouds.openstack.keystone.v2_0.domain.Token; +import org.jclouds.openstack.keystone.v2_0.domain.User; +import org.jclouds.rest.annotations.ExceptionParser; +import org.jclouds.rest.annotations.RequestFilters; +import org.jclouds.rest.annotations.SelectJson; +import org.jclouds.rest.annotations.SkipEncoding; +import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404; +import org.jclouds.rest.functions.ReturnFalseOnNotFoundOr404; +import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404; + +import com.google.common.util.concurrent.ListenableFuture; + +/** + * Provides asynchronous access to Admin via their REST API. + *

+ * + * @see AdminClient + * @see + * @author Adam Lowe + */ +@SkipEncoding({ '/', '=' }) +public interface AdminAsyncClient { + /** + * @see AdminClient#getApiMetadata() + */ + @GET + @SelectJson("version") + @Consumes(MediaType.APPLICATION_JSON) + @Path("/") + @ExceptionParser(ReturnNullOnNotFoundOr404.class) + ListenableFuture getApiMetadata(); + + /** + * @see AdminClient#listTenants() + */ + @GET + @SelectJson("tenants") + @Consumes(MediaType.APPLICATION_JSON) + @Path("/tenants") + @RequestFilters(AuthenticateRequest.class) + @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class) + ListenableFuture> listTenants(); + + /** @see AdminClient#getToken(String) */ + @GET + @SelectJson("token") + @Consumes(MediaType.APPLICATION_JSON) + @Path("/tokens/{token}") + @RequestFilters(AuthenticateRequest.class) + @ExceptionParser(ReturnNullOnNotFoundOr404.class) + ListenableFuture getToken(@PathParam("token") String token); + + /** @see AdminClient#getUserOfToken(String) */ + @GET + @SelectJson("user") + @Consumes(MediaType.APPLICATION_JSON) + @Path("/tokens/{token}") + @RequestFilters(AuthenticateRequest.class) + @ExceptionParser(ReturnNullOnNotFoundOr404.class) + ListenableFuture getUserOfToken(@PathParam("token") String token); + + /** @see AdminClient#checkTokenIsValid(String) */ + @HEAD + @Path("/tokens/{token}") + @RequestFilters(AuthenticateRequest.class) + @ExceptionParser(ReturnFalseOnNotFoundOr404.class) + ListenableFuture checkTokenIsValid(@PathParam("token") String token); + + /** @see AdminClient#getEndpointsForToken(String) */ + @GET + @SelectJson("endpoints") + @Consumes(MediaType.APPLICATION_JSON) + @Path("/tokens/{token}/endpoints") + @RequestFilters(AuthenticateRequest.class) + @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class) + ListenableFuture> getEndpointsForToken(@PathParam("token") String token); + + /** @see AdminClient#getTenant(String) */ + @GET + @SelectJson("tenant") + @Consumes(MediaType.APPLICATION_JSON) + @Path("/tenants/{tenantId}") + @RequestFilters(AuthenticateRequest.class) + @ExceptionParser(ReturnNullOnNotFoundOr404.class) + ListenableFuture getTenant(@PathParam("tenantId") String tenantId); + + /** @see AdminClient#getTenantByName(String) */ + @GET + @SelectJson("tenant") + @Consumes(MediaType.APPLICATION_JSON) + @Path("/tenants") + @RequestFilters(AuthenticateRequest.class) + @ExceptionParser(ReturnNullOnNotFoundOr404.class) + ListenableFuture getTenantByName(@QueryParam("name") String tenantName); + + /** @see AdminClient#listUsers() */ + @GET + @SelectJson("users") + @Consumes(MediaType.APPLICATION_JSON) + @Path("/users") + @RequestFilters(AuthenticateRequest.class) + @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class) + ListenableFuture> listUsers(); + + /** @see AdminClient#getUser(String) */ + @GET + @SelectJson("user") + @Consumes(MediaType.APPLICATION_JSON) + @Path("/users/{userId}") + @RequestFilters(AuthenticateRequest.class) + @ExceptionParser(ReturnNullOnNotFoundOr404.class) + ListenableFuture getUser(@PathParam("userId") String userId); + + /** @see AdminClient#getUserByName(String) */ + @GET + @SelectJson("user") + @Consumes(MediaType.APPLICATION_JSON) + @Path("/users") + @RequestFilters(AuthenticateRequest.class) + @ExceptionParser(ReturnNullOnNotFoundOr404.class) + ListenableFuture getUserByName(@QueryParam("name") String userName); + + /** @see AdminClient#listRolesOfUser(String) */ + @GET + @SelectJson("roles") + @Consumes(MediaType.APPLICATION_JSON) + @Path("/users/{userId}/roles") + @RequestFilters(AuthenticateRequest.class) + @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class) + ListenableFuture> listRolesOfUser(@PathParam("userId") String userId); + + /** @see AdminClient#listRolesOfUserOnTenant(String, String) */ + @GET + @SelectJson("roles") + @Consumes(MediaType.APPLICATION_JSON) + @Path("/tenants/{tenantId}/users/{userId}/roles") + @RequestFilters(AuthenticateRequest.class) + @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class) + ListenableFuture> listRolesOfUserOnTenant(@PathParam("userId") String userId, @PathParam("tenantId") String tenantId); +} diff --git a/labs/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/AdminClient.java b/labs/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/AdminClient.java new file mode 100644 index 0000000000..def212949f --- /dev/null +++ b/labs/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/AdminClient.java @@ -0,0 +1,145 @@ +/** + * 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; + +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import org.jclouds.concurrent.Timeout; +import org.jclouds.openstack.keystone.v2_0.domain.ApiMetadata; +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.Tenant; +import org.jclouds.openstack.keystone.v2_0.domain.Token; +import org.jclouds.openstack.keystone.v2_0.domain.User; + +/** + * Provides synchronous access to the KeyStone Admin API. + *

+ * + * @author Adam Lowe + * @see AdminAsyncClient + * @see + */ +@Timeout(duration = 30, timeUnit = TimeUnit.SECONDS) +public interface AdminClient { + + /** + * Discover API version information, links to documentation (PDF, HTML, WADL), and supported media types + * + * @return the requested information + */ + ApiMetadata getApiMetadata(); + + /** + * The operation returns a list of tenants which the current token provides access to. + */ + Set listTenants(); + + /** + * Retrieve information about a tenant, by tenant ID + * + * @return the information about the tenant + */ + Tenant getTenant(String tenantId); + + /** + * Retrieve information about a tenant, by tenant name + *

+ * NOTE: currently not working in openstack ( https://bugs.launchpad.net/keystone/+bug/956687 ) + * + * @return the information about the tenant + */ + Tenant getTenantByName(String tenantName); + + /** + * Validate a token and, if it is valid, return access information regarding the tenant (though not the service catalog)/ + * + * @return the requested information + */ + Token getToken(String token); + + /** + * Validate a token and, if it is valid, return access information regarding the tenant (though not the service catalog)/ + * + * @return the requested information + */ + User getUserOfToken(String token); + + /** + * Validate a token. This is a high-performance variant of the #getToken() call that does not return any further + * information. + * + * @return true if the token is valid + */ + Boolean checkTokenIsValid(String token); + + /** + * List all endpoints for a token + *

+ * NOTE: currently not working in openstack ( https://bugs.launchpad.net/keystone/+bug/988672 ) + * + * @return the set of endpoints + */ + Set getEndpointsForToken(String token); + + /** + * Retrieve the list of users + *

+ * NOTE: this method is not in API documentation for keystone, but does work + * + * @return the list of users + */ + Set listUsers(); + + /** + * Retrieve information about a user, by user ID + * + * @return the information about the user + */ + User getUser(String userId); + + /** + * Retrieve information about a user, by user name + *

+ * NOTE: currently not working in openstack ( https://bugs.launchpad.net/keystone/+bug/956687 ) + * + * @return the information about the user + */ + User getUserByName(String userName); + + /** + * Retrieves the list of global roles associated with a specific user (excludes tenant roles). + *

+ * NOTE: Broken in openstack ( https://bugs.launchpad.net/keystone/+bug/933565 ) + * + * @return the set of Roles granted to the user + */ + Set listRolesOfUser(String userId); + + /** + * List the roles a user has been granted on a specific tenant + * + * @return the set of roles + */ + Set listRolesOfUserOnTenant(String userId, String tenantId); + +} \ No newline at end of file diff --git a/labs/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/KeystoneApiMetadata.java b/labs/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/KeystoneApiMetadata.java new file mode 100644 index 0000000000..7e038f26c6 --- /dev/null +++ b/labs/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/KeystoneApiMetadata.java @@ -0,0 +1,102 @@ +/** + * 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; + +import static org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties.SERVICE_TYPE; + +import java.net.URI; +import java.util.Properties; + +import org.jclouds.apis.ApiMetadata; +import org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties; +import org.jclouds.openstack.keystone.v2_0.config.KeystoneRestClientModule; +import org.jclouds.openstack.services.ServiceType; +import org.jclouds.rest.RestContext; +import org.jclouds.rest.internal.BaseRestApiMetadata; + +import com.google.common.collect.ImmutableSet; +import com.google.common.reflect.TypeToken; +import com.google.inject.Module; + +/** + * Implementation of {@link org.jclouds.apis.ApiMetadata} for Keystone 2.0 API + * + * @author Adrian Cole + */ +public class KeystoneApiMetadata extends BaseRestApiMetadata { + + /** The serialVersionUID */ + private static final long serialVersionUID = 6725672099385580694L; + + public static final TypeToken> CONTEXT_TOKEN = new TypeToken>() { + }; + + @Override + public Builder toBuilder() { + return new Builder().fromApiMetadata(this); + } + + public KeystoneApiMetadata() { + this(new Builder()); + } + + protected KeystoneApiMetadata(Builder builder) { + super(builder); + } + + public static Properties defaultProperties() { + Properties properties = BaseRestApiMetadata.defaultProperties(); + // auth fail can happen while cloud-init applies keypair updates + properties.setProperty("jclouds.ssh.max-retries", "7"); + properties.setProperty("jclouds.ssh.retry-auth", "true"); + // TODO: this doesn't actually do anything yet. + properties.setProperty(KeystoneProperties.VERSION, "2.0"); + properties.put(SERVICE_TYPE, ServiceType.IDENTITY); + return properties; + } + + public static class Builder extends BaseRestApiMetadata.Builder { + + protected Builder() { + super(KeystoneClient.class, KeystoneAsyncClient.class); + id("openstack-keystone") + .name("OpenStack Keystone Essex+ API") + .identityName("tenantId:user") + .credentialName("password") + .documentation(URI.create("http://api.openstack.org/")) + .version("2.0") + .defaultEndpoint("http://localhost:5000") + .defaultProperties(KeystoneApiMetadata.defaultProperties()) + .defaultModules(ImmutableSet.>of(KeystoneRestClientModule.class)); + } + + @Override + public KeystoneApiMetadata build() { + return new KeystoneApiMetadata(this); + } + + @Override + public Builder fromApiMetadata(ApiMetadata in) { + super.fromApiMetadata(in); + return this; + } + + } + +} \ No newline at end of file diff --git a/labs/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/KeystoneAsyncClient.java b/labs/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/KeystoneAsyncClient.java new file mode 100644 index 0000000000..cb88070c2f --- /dev/null +++ b/labs/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/KeystoneAsyncClient.java @@ -0,0 +1,59 @@ +/** + * 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; + +import java.util.Set; + +import javax.ws.rs.Path; + +import org.jclouds.Constants; +import org.jclouds.javax.annotation.Nullable; +import org.jclouds.location.Region; +import org.jclouds.location.functions.RegionToEndpoint; +import org.jclouds.openstack.keystone.v2_0.functions.RegionToAdminEndpointURI; +import org.jclouds.rest.annotations.Delegate; +import org.jclouds.rest.annotations.EndpointParam; + +import com.google.inject.Provides; + +/** + * Provides access to Openstack keystone resources via their REST API. + *

+ * + * @author Adam Lowe + * @see + * @see KeystoneClient + */ +@Path("/v{" + Constants.PROPERTY_API_VERSION + "}") +public interface KeystoneAsyncClient { + /** + * @return the Region codes configured + */ + @Provides + @Region + Set getConfiguredRegions(); + + /** Provides asynchronous access to Identity user-accessible features */ + @Delegate + ServiceAsyncClient getServiceClientForRegion(@EndpointParam(parser = RegionToEndpoint.class) @Nullable String region); + + /** Provides asynchronous access to the KeyStone Admin API */ + @Delegate + AdminAsyncClient getAdminClientForRegion(@EndpointParam(parser = RegionToAdminEndpointURI.class) @Nullable String region); +} diff --git a/labs/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/KeystoneClient.java b/labs/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/KeystoneClient.java new file mode 100644 index 0000000000..2c63a5b074 --- /dev/null +++ b/labs/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/KeystoneClient.java @@ -0,0 +1,58 @@ +/** + * 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; + +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import org.jclouds.concurrent.Timeout; +import org.jclouds.javax.annotation.Nullable; +import org.jclouds.location.Region; +import org.jclouds.location.functions.RegionToEndpoint; +import org.jclouds.openstack.keystone.v2_0.functions.RegionToAdminEndpointURI; +import org.jclouds.rest.annotations.Delegate; +import org.jclouds.rest.annotations.EndpointParam; + +import com.google.inject.Provides; + +/** + * Provides access to Openstack keystone resources via their REST API. + *

+ * + * @author Adam Lowe + * @see + */ +@Timeout(duration = 10, timeUnit = TimeUnit.SECONDS) +public interface KeystoneClient { + /** + * @return the Region codes configured + */ + @Provides + @Region + Set getConfiguredRegions(); + + /** Provides synchronous access to Identity user-accessible features */ + @Delegate + ServiceClient getServiceClientForRegion(@EndpointParam(parser = RegionToEndpoint.class) @Nullable String region); + + /** Provides synchronous access to the KeyStone Admin API */ + @Delegate + AdminClient getAdminClientForRegion(@EndpointParam(parser = RegionToAdminEndpointURI.class) @Nullable String region); + +} \ No newline at end of file diff --git a/labs/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/ServiceAsyncClient.java b/labs/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/ServiceAsyncClient.java new file mode 100644 index 0000000000..9cc665fd91 --- /dev/null +++ b/labs/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/ServiceAsyncClient.java @@ -0,0 +1,71 @@ +/** + * 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; + +import java.util.Set; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.core.MediaType; + +import org.jclouds.openstack.filters.AuthenticateRequest; +import org.jclouds.openstack.keystone.v2_0.domain.ApiMetadata; +import org.jclouds.openstack.keystone.v2_0.domain.Tenant; +import org.jclouds.rest.annotations.ExceptionParser; +import org.jclouds.rest.annotations.RequestFilters; +import org.jclouds.rest.annotations.SelectJson; +import org.jclouds.rest.annotations.SkipEncoding; +import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404; +import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404; + +import com.google.common.util.concurrent.ListenableFuture; + +/** + * Provides asynchronous access to Service via their REST API. + *

+ * + * @see ServiceClient + * @see + * @author Adam Lowe + */ +@SkipEncoding({ '/', '=' }) +public interface ServiceAsyncClient { + /** + * @see ServiceClient#getApiMetadata() + */ + @GET + @SelectJson("version") + @Consumes(MediaType.APPLICATION_JSON) + @Path("/") + @ExceptionParser(ReturnNullOnNotFoundOr404.class) + ListenableFuture getApiMetadata(); + + /** + * @see ServiceClient#listTenants() + */ + @GET + @SelectJson("tenants") + @Consumes(MediaType.APPLICATION_JSON) + @Path("/tenants") + @RequestFilters(AuthenticateRequest.class) + @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class) + ListenableFuture> listTenants(); +} diff --git a/labs/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/ServiceClient.java b/labs/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/ServiceClient.java new file mode 100644 index 0000000000..a09a78e3eb --- /dev/null +++ b/labs/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/ServiceClient.java @@ -0,0 +1,51 @@ +/** + * 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; + +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import org.jclouds.concurrent.Timeout; +import org.jclouds.openstack.keystone.v2_0.domain.ApiMetadata; +import org.jclouds.openstack.keystone.v2_0.domain.Tenant; + +/** + * Provides synchronous access to the KeyStone Service API. + *

+ * + * @author Adam Lowe + * @see ServiceAsyncClient + * @see + */ +@Timeout(duration = 30, timeUnit = TimeUnit.SECONDS) +public interface ServiceClient { + + /** + * Discover API version information, links to documentation (PDF, HTML, WADL), and supported media types + * + * @return the requested information + */ + ApiMetadata getApiMetadata(); + + /** + * The operation returns a list of tenants which the current token provides access to. + */ + Set listTenants(); +} \ No newline at end of file diff --git a/labs/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/config/KeystoneParserModule.java b/labs/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/config/KeystoneParserModule.java new file mode 100644 index 0000000000..c808961900 --- /dev/null +++ b/labs/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/config/KeystoneParserModule.java @@ -0,0 +1,36 @@ +/** + * 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.config; + +import org.jclouds.json.config.GsonModule; +import org.jclouds.json.config.GsonModule.DateAdapter; + +import com.google.inject.AbstractModule; + +/** + * @author Adam Lowe + */ +public class KeystoneParserModule extends AbstractModule { + + + @Override + protected void configure() { + bind(DateAdapter.class).to(GsonModule.Iso8601DateAdapter.class); + } +} diff --git a/labs/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/config/KeystoneRestClientModule.java b/labs/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/config/KeystoneRestClientModule.java new file mode 100644 index 0000000000..70b672c5cf --- /dev/null +++ b/labs/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/config/KeystoneRestClientModule.java @@ -0,0 +1,76 @@ +/** + * 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.config; + +import java.util.Map; + +import org.jclouds.http.HttpErrorHandler; +import org.jclouds.http.annotation.ClientError; +import org.jclouds.http.annotation.Redirection; +import org.jclouds.http.annotation.ServerError; +import org.jclouds.openstack.keystone.v2_0.AdminAsyncClient; +import org.jclouds.openstack.keystone.v2_0.AdminClient; +import org.jclouds.openstack.keystone.v2_0.KeystoneAsyncClient; +import org.jclouds.openstack.keystone.v2_0.KeystoneClient; +import org.jclouds.openstack.keystone.v2_0.ServiceAsyncClient; +import org.jclouds.openstack.keystone.v2_0.ServiceClient; +import org.jclouds.openstack.keystone.v2_0.handlers.KeystoneErrorHandler; +import org.jclouds.rest.ConfiguresRestClient; +import org.jclouds.rest.config.RestClientModule; + +import com.google.common.collect.ImmutableMap; + +/** + * Configures the Keystone connection. + * + * @author Adam Lowe + */ +@ConfiguresRestClient +public class KeystoneRestClientModule extends RestClientModule { + + public static final Map, Class> DELEGATE_MAP = ImmutableMap., Class> builder() + .put(ServiceClient.class, ServiceAsyncClient.class).put(AdminClient.class, AdminAsyncClient.class) + .build(); + + public KeystoneRestClientModule() { + super(DELEGATE_MAP); + } + + @Override + protected void configure() { + install(new KeystoneParserModule()); + super.configure(); + } + + @Override + protected void installLocations() { + // TODO: select this from KeystoneProperties.VERSION; note you select from + // a guice provided property, so it will have to come from somewhere else, maybe we move + // this to the the ContextBuilder + install(KeystoneAuthenticationModule.forRegions()); + super.installLocations(); + } + + @Override + protected void bindErrorHandlers() { + bind(HttpErrorHandler.class).annotatedWith(Redirection.class).to(KeystoneErrorHandler.class); + bind(HttpErrorHandler.class).annotatedWith(ClientError.class).to(KeystoneErrorHandler.class); + bind(HttpErrorHandler.class).annotatedWith(ServerError.class).to(KeystoneErrorHandler.class); + } +} diff --git a/labs/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/handlers/KeystoneErrorHandler.java b/labs/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/handlers/KeystoneErrorHandler.java new file mode 100644 index 0000000000..007270f21a --- /dev/null +++ b/labs/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/handlers/KeystoneErrorHandler.java @@ -0,0 +1,65 @@ +/** + * 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.handlers; + +import static org.jclouds.http.HttpUtils.closeClientButKeepContentStream; + +import javax.inject.Singleton; + +import org.jclouds.http.HttpCommand; +import org.jclouds.http.HttpErrorHandler; +import org.jclouds.http.HttpResponse; +import org.jclouds.http.HttpResponseException; +import org.jclouds.rest.AuthorizationException; +import org.jclouds.rest.ResourceNotFoundException; + +/** + * This will parse and set an appropriate exception on the command object. + * + * @author Adam Lowe + * + */ +// TODO: is there error spec someplace? let's type errors, etc. +@Singleton +public class KeystoneErrorHandler implements HttpErrorHandler { + public void handleError(HttpCommand command, HttpResponse response) { + // it is important to always read fully and close streams + byte[] data = closeClientButKeepContentStream(response); + String message = data != null ? new String(data) : null; + + Exception exception = message != null ? new HttpResponseException(command, response, message) + : new HttpResponseException(command, response); + message = message != null ? message : String.format("%s -> %s", command.getCurrentRequest().getRequestLine(), + response.getStatusLine()); + switch (response.getStatusCode()) { + case 400: + break; + case 401: + case 403: + exception = new AuthorizationException(message, exception); + break; + case 404: + if (!command.getCurrentRequest().getMethod().equals("DELETE")) { + exception = new ResourceNotFoundException(message, exception); + } + break; + } + command.setException(exception); + } +} diff --git a/labs/openstack-keystone/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata b/labs/openstack-keystone/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata new file mode 100644 index 0000000000..72609ba3b7 --- /dev/null +++ b/labs/openstack-keystone/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata @@ -0,0 +1 @@ +org.jclouds.openstack.keystone.v2_0.KeystoneApiMetadata diff --git a/labs/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/AdminClientExpectTest.java b/labs/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/AdminClientExpectTest.java new file mode 100644 index 0000000000..87cd71cd47 --- /dev/null +++ b/labs/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/AdminClientExpectTest.java @@ -0,0 +1,379 @@ +/** + * 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 1.1 (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-1.1 + * + * 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; + +import static javax.ws.rs.core.MediaType.APPLICATION_JSON; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertNull; +import static org.testng.Assert.assertTrue; + +import java.net.URI; +import java.util.Set; + +import org.jclouds.date.DateService; +import org.jclouds.date.internal.SimpleDateFormatDateService; +import org.jclouds.http.HttpResponseException; +import org.jclouds.openstack.domain.Link; +import org.jclouds.openstack.keystone.v2_0.domain.ApiMetadata; +import org.jclouds.openstack.keystone.v2_0.domain.Endpoint; +import org.jclouds.openstack.keystone.v2_0.domain.MediaType; +import org.jclouds.openstack.keystone.v2_0.domain.Role; +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.openstack.keystone.v2_0.internal.BaseKeystoneRestClientExpectTest; +import org.jclouds.rest.AuthorizationException; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableMultimap; +import com.google.common.collect.ImmutableSet; + +/** + * Tests parsing and Guice wiring of UserClient + * + * @author Adam Lowe + */ +public class AdminClientExpectTest extends BaseKeystoneRestClientExpectTest { + private DateService dateService = new SimpleDateFormatDateService(); + + public AdminClientExpectTest() { + endpoint = "https://csnode.jclouds.org:35357"; + } + + public void testGetApiMetaData() { + AdminClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, + standardRequestBuilder(endpoint + "/v2.0/"). + headers(ImmutableMultimap.of("Accept", APPLICATION_JSON)).build(), + standardResponseBuilder(200).payload(payloadFromResourceWithContentType("/api_metadata.json", APPLICATION_JSON)).build()) + .getAdminClientForRegion("region-a.geo-1"); + ApiMetadata metadata = client.getApiMetadata(); + assertNotNull(metadata); + assertEquals(metadata.getId(), "v2.0"); + + ApiMetadata expected = ApiMetadata.builder().id("v2.0") + .links(ImmutableSet.of(Link.builder().relation(Link.Relation.SELF).href(URI.create("http://172.16.89.140:5000/v2.0/")).build(), + Link.builder().relation(Link.Relation.DESCRIBEDBY).type("text/html").href(URI.create("http://docs.openstack.org/api/openstack-identity-service/2.0/content/")).build(), + Link.builder().relation(Link.Relation.DESCRIBEDBY).type("application/pdf").href(URI.create("http://docs.openstack.org/api/openstack-identity-service/2.0/identity-dev-guide-2.0.pdf")).build() + )) + .status("beta") + .updated(new SimpleDateFormatDateService().iso8601SecondsDateParse("2011-11-19T00:00:00Z")) + .mediaTypes(ImmutableSet.of( + MediaType.builder().base("application/json").type("application/vnd.openstack.identity-v2.0+json").build(), + MediaType.builder().base("application/xml").type("application/vnd.openstack.identity-v2.0+xml").build() + )) + .build(); + + assertEquals(metadata, expected); + } + + public void testGetApiMetaDataFailNotFound() { + AdminClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, + standardRequestBuilder(endpoint + "/v2.0/").headers(ImmutableMultimap.of("Accept", APPLICATION_JSON)).build(), + standardResponseBuilder(404).build()).getAdminClientForRegion("region-a.geo-1"); + assertNull(client.getApiMetadata()); + } + + public void testListTenants() { + AdminClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, + standardRequestBuilder(endpoint + "/v2.0/tenants").build(), + standardResponseBuilder(200).payload(payloadFromResourceWithContentType("/tenant_list.json", APPLICATION_JSON)).build()) + .getAdminClientForRegion("region-a.geo-1"); + Set tenants = client.listTenants(); + assertNotNull(tenants); + assertFalse(tenants.isEmpty()); + + Set expected = ImmutableSet.of( + Tenant.builder().name("demo").id("05d1dc7af71646deba64cfc17b81bec0").build(), + Tenant.builder().name("admin").id("7aa2e17ec29f44d193c48feaba0852cc").build() + ); + + assertEquals(tenants, expected); + } + + public void testListTenantsFailNotFound() { + AdminClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, + standardRequestBuilder(endpoint + "/v2.0/tenants").build(), + standardResponseBuilder(404).build()).getAdminClientForRegion("region-a.geo-1"); + assertTrue(client.listTenants().isEmpty()); + } + + public void testGetTenant() { + AdminClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, + standardRequestBuilder(endpoint + "/v2.0/tenants/013ba41150a14830bec85ffe93353bcc").build(), + standardResponseBuilder(200).payload(payloadFromResourceWithContentType("/tenant_details.json", APPLICATION_JSON)).build()) + .getAdminClientForRegion("region-a.geo-1"); + Tenant tenant = client.getTenant("013ba41150a14830bec85ffe93353bcc"); + assertNotNull(tenant); + assertEquals(tenant, Tenant.builder().id("013ba41150a14830bec85ffe93353bcc").name("admin").build()); + } + + @Test(expectedExceptions = AuthorizationException.class) + public void testListTenantsFailNotAuthorized() { + AdminClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, + standardRequestBuilder(endpoint + "/v2.0/tenants/013ba41150a14830bec85ffe93353bcc").build(), + standardResponseBuilder(401).build()).getAdminClientForRegion("region-a.geo-1"); + client.getTenant("013ba41150a14830bec85ffe93353bcc"); + } + + public void testGetTenantByName() { + AdminClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, + standardRequestBuilder(endpoint + "/v2.0/tenants?name=admin").build(), + standardResponseBuilder(200).payload(payloadFromResourceWithContentType("/tenant_details.json", APPLICATION_JSON)).build()) + .getAdminClientForRegion("region-a.geo-1"); + Tenant tenant = client.getTenantByName("admin"); + assertNotNull(tenant); + assertEquals(tenant, Tenant.builder().id("013ba41150a14830bec85ffe93353bcc").name("admin").build()); + } + + public void testGetTenantByNameFailNotFound() { + AdminClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, + standardRequestBuilder(endpoint + "/v2.0/tenants?name=admin").build(), + standardResponseBuilder(404).build()).getAdminClientForRegion("region-a.geo-1"); + assertNull(client.getTenantByName("admin")); + } + + public void testGetToken() { + AdminClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, + standardRequestBuilder(endpoint + "/v2.0/tokens/sometokenorother").build(), + standardResponseBuilder(200).payload(payloadFromResourceWithContentType("/token_details.json", APPLICATION_JSON)).build()) + .getAdminClientForRegion("region-a.geo-1"); + Token token = client.getToken("sometokenorother"); + assertNotNull(token); + assertEquals(token, + Token.builder().id("167eccdc790946969ced473732e8109b").expires(dateService.iso8601SecondsDateParse("2012-04-28T12:42:50Z")) + .tenant(Tenant.builder().id("4cea93f5464b4f1c921fb3e0461d72b5").name("demo").build()).build()); + } + + public void testGetTokenFailNotFound() { + AdminClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, + standardRequestBuilder(endpoint + "/v2.0/tokens/sometokenorother").build(), + standardResponseBuilder(404).build()) + .getAdminClientForRegion("region-a.geo-1"); + assertNull(client.getToken("sometokenorother")); + } + + @Test(expectedExceptions = HttpResponseException.class) + public void testGetTokenFail500() { + AdminClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, + standardRequestBuilder(endpoint + "/v2.0/tokens/sometokenorother").build(), + standardResponseBuilder(500).build()).getAdminClientForRegion("region-a.geo-1"); + client.getToken("sometokenorother"); + } + + public void testGetUserOfToken() { + AdminClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, + standardRequestBuilder(endpoint + "/v2.0/tokens/sometokenorother").build(), + standardResponseBuilder(200).payload(payloadFromResourceWithContentType("/token_details.json", APPLICATION_JSON)).build()) + .getAdminClientForRegion("region-a.geo-1"); + User user = client.getUserOfToken("sometokenorother"); + assertNotNull(user); + assertEquals(user, User.builder().id("2b9b606181634ae9ac86fd95a8bc2cde").name("admin") + .roles(ImmutableSet.of(Role.builder().id("79cada5c02814b57a52e0eed4dd388cb").name("admin").build())) + .build()); + } + + public void testGetUserOfTokenFailNotFound() { + AdminClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, + standardRequestBuilder(endpoint + "/v2.0/tokens/sometokenorother").build(), + standardResponseBuilder(404).build()).getAdminClientForRegion("region-a.geo-1"); + assertNull(client.getUserOfToken("sometokenorother")); + } + + public void testCheckTokenIsValid() { + AdminClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, + standardRequestBuilder(endpoint + "/v2.0/tokens/sometokenorother").method("HEAD") + .headers(ImmutableMultimap.of("X-Auth-Token", authToken)).build(), + standardResponseBuilder(200).payload(payloadFromResourceWithContentType("/token_details.json", APPLICATION_JSON)).build()) + .getAdminClientForRegion("region-a.geo-1"); + assertTrue(client.checkTokenIsValid("sometokenorother")); + } + + public void testCheckTokenIsValidFailNotValid() { + AdminClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, + standardRequestBuilder(endpoint + "/v2.0/tokens/sometokenorother").method("HEAD") + .headers(ImmutableMultimap.of("X-Auth-Token", authToken)).build(), + standardResponseBuilder(404).build()).getAdminClientForRegion("region-a.geo-1"); + assertFalse(client.checkTokenIsValid("sometokenorother")); + } + + @Test + public void testGetEndpointsForToken() { + AdminClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, + standardRequestBuilder(endpoint + "/v2.0/tokens/XXXXXX/endpoints").build(), + standardResponseBuilder(200).payload(payloadFromResourceWithContentType("/user_endpoints.json", APPLICATION_JSON)).build()) + .getAdminClientForRegion("region-a.geo-1"); + Set endpoints = client.getEndpointsForToken("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() + )); + } + + @Test + public void testGetEndpointsForTokenFailNotFound() { + AdminClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, + standardRequestBuilder(endpoint + "/v2.0/tokens/XXXXXX/endpoints").build(), + standardResponseBuilder(404).build()) + .getAdminClientForRegion("region-a.geo-1"); + assertTrue(client.getEndpointsForToken("XXXXXX").isEmpty()); + } + + public void testListUsers() { + AdminClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, + standardRequestBuilder(endpoint + "/v2.0/users").build(), + standardResponseBuilder(200).payload(payloadFromResourceWithContentType("/user_list.json", APPLICATION_JSON)).build()) + .getAdminClientForRegion("region-a.geo-1"); + Set users = client.listUsers(); + assertNotNull(users); + assertFalse(users.isEmpty()); + + Set expected = ImmutableSet.of( + User.builder().name("nova").id("e021dfd758eb44a89f1c57c8ef3be8e2").build(), + User.builder().name("glance").id("3f6c1c9ba993495ead7d2eb2192e284f").build(), + User.builder().name("demo").id("667b2e1420604df8b67cd8ea57d4ee64").build(), + User.builder().name("admin").id("2b9b606181634ae9ac86fd95a8bc2cde").build() + ); + + assertEquals(users, expected); + } + + @Test(expectedExceptions = AuthorizationException.class) + public void testListUsersFailNotAuth() { + AdminClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, + standardRequestBuilder(endpoint + "/v2.0/users").build(), + standardResponseBuilder(401).build()).getAdminClientForRegion("region-a.geo-1"); + client.listUsers(); + } + + public void testGetUser() { + AdminClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, + standardRequestBuilder(endpoint + "/v2.0/users/e021dfd758eb44a89f1c57c8ef3be8e2").build(), + standardResponseBuilder(200).payload(payloadFromResourceWithContentType("/user_details.json", APPLICATION_JSON)).build()) + .getAdminClientForRegion("region-a.geo-1"); + User user = client.getUser("e021dfd758eb44a89f1c57c8ef3be8e2"); + assertNotNull(user); + assertEquals(user, User.builder().name("nova").id("e021dfd758eb44a89f1c57c8ef3be8e2").build()); + } + + public void testGetUserFailNotFound() { + AdminClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, + standardRequestBuilder(endpoint + "/v2.0/users/f021dfd758eb44a89f1c57c8ef3be8e2").build(), + standardResponseBuilder(404).build()).getAdminClientForRegion("region-a.geo-1"); + assertNull(client.getUser("f021dfd758eb44a89f1c57c8ef3be8e2")); + } + + public void testGetUserByName() { + AdminClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, + standardRequestBuilder(endpoint + "/v2.0/users?name=nova").build(), + standardResponseBuilder(200).payload(payloadFromResourceWithContentType("/user_details.json", APPLICATION_JSON)).build()) + .getAdminClientForRegion("region-a.geo-1"); + User user = client.getUserByName("nova"); + assertNotNull(user); + assertEquals(user, User.builder().name("nova").id("e021dfd758eb44a89f1c57c8ef3be8e2").build()); + } + + public void testGetUserByNameFailNotFound() { + AdminClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, + standardRequestBuilder(endpoint + "/v2.0/users?name=fred").build(), + standardResponseBuilder(404).build()).getAdminClientForRegion("region-a.geo-1"); + assertNull(client.getUserByName("fred")); + } + + public void testListRolesOfUser() { + AdminClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, + standardRequestBuilder(endpoint + "/v2.0/users/3f6c1c9ba993495ead7d2eb2192e284f/roles").build(), + standardResponseBuilder(200).payload(payloadFromResourceWithContentType("/user_role_list.json", APPLICATION_JSON)).build()) + .getAdminClientForRegion("region-a.geo-1"); + Set roles = client.listRolesOfUser("3f6c1c9ba993495ead7d2eb2192e284f"); + assertNotNull(roles); + assertFalse(roles.isEmpty()); + assertEquals(roles, ImmutableSet.of( + Role.builder().id("79cada5c02814b57a52e0eed4dd388cb").name("admin").build() + )); + } + + public void testListRolesOfUserFailNotFound() { + AdminClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, + standardRequestBuilder(endpoint + "/v2.0/users/4f6c1c9ba993495ead7d2eb2192e284f/roles").build(), + standardResponseBuilder(404).build()).getAdminClientForRegion("region-a.geo-1"); + assertTrue(client.listRolesOfUser("4f6c1c9ba993495ead7d2eb2192e284f").isEmpty()); + } + + @Test(expectedExceptions = HttpResponseException.class) + public void testListRolesOfUserFailNotImplemented() { + AdminClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, + standardRequestBuilder(endpoint + "/v2.0/users/5f6c1c9ba993495ead7d2eb2192e284f/roles").build(), + standardResponseBuilder(501).build()).getAdminClientForRegion("region-a.geo-1"); + assertTrue(client.listRolesOfUser("5f6c1c9ba993495ead7d2eb2192e284f").isEmpty()); + } + + public void testListRolesOfUserInTenant() { + AdminClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, + standardRequestBuilder(endpoint + "/v2.0/users/3f6c1c9ba993495ead7d2eb2192e284f/roles").build(), + standardResponseBuilder(200).payload(payloadFromResourceWithContentType("/user_tenant_role_list.json", APPLICATION_JSON)).build()) + .getAdminClientForRegion("region-a.geo-1"); + Set roles = client.listRolesOfUser("3f6c1c9ba993495ead7d2eb2192e284f"); + assertNotNull(roles); + assertFalse(roles.isEmpty()); + assertEquals(roles, ImmutableSet.of( + Role.builder().id("31c451195aac49b386039341e2c92a16").name("KeystoneServiceAdmin").build(), + Role.builder().id("79cada5c02814b57a52e0eed4dd388cb").name("admin").build(), + Role.builder().id("6ea17ddd37a6447794cb0e164d4db894").name("KeystoneAdmin").build())); + } + + public void testListRolesOfUserInTenantFailNotFound() { + AdminClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, + standardRequestBuilder(endpoint + "/v2.0/users/3f6c1c9ba993495ead7d2eb2192e284f/roles").build(), + standardResponseBuilder(404).build()).getAdminClientForRegion("region-a.geo-1"); + assertTrue(client.listRolesOfUser("3f6c1c9ba993495ead7d2eb2192e284f").isEmpty()); + } + +} diff --git a/labs/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/AdminClientLiveTest.java b/labs/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/AdminClientLiveTest.java new file mode 100644 index 0000000000..82ab9fc504 --- /dev/null +++ b/labs/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/AdminClientLiveTest.java @@ -0,0 +1,193 @@ +/** + * 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 1.1 (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-1.1 + * + * 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; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertNull; +import static org.testng.AssertJUnit.assertTrue; + +import java.net.URI; +import java.util.Set; + +import org.jclouds.http.HttpRequest; +import org.jclouds.openstack.filters.AuthenticateRequest; +import org.jclouds.openstack.keystone.v2_0.domain.ApiMetadata; +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.Tenant; +import org.jclouds.openstack.keystone.v2_0.domain.Token; +import org.jclouds.openstack.keystone.v2_0.domain.User; +import org.jclouds.openstack.keystone.v2_0.internal.BaseKeystoneClientLiveTest; +import org.jclouds.openstack.reference.AuthHeaders; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import com.google.common.collect.Iterables; + +/** + * Tests AdminClient + * + * @author Adam Lowe + */ +@Test(groups = "live", testName = "AdminClientLiveTest", singleThreaded = true) +public class AdminClientLiveTest extends BaseKeystoneClientLiveTest { + + protected String token; + + // Get the token currently in use (there's currently no listTokens()) + @BeforeMethod + public void grabToken() { + AuthenticateRequest ar = keystoneContext.getUtils().getInjector().getInstance(AuthenticateRequest.class); + HttpRequest test = ar.filter(HttpRequest.builder().method("GET").endpoint(URI.create(endpoint)).build()); + token = Iterables.getOnlyElement(test.getHeaders().get(AuthHeaders.AUTH_TOKEN)); + } + + public void testGetApiMetaData() { + for (String regionId : keystoneContext.getApi().getConfiguredRegions()) { + ApiMetadata result = keystoneContext.getApi().getAdminClientForRegion(regionId).getApiMetadata(); + assertNotNull(result); + assertNotNull(result.getId()); + assertNotNull(result.getStatus()); + assertNotNull(result.getUpdated()); + } + } + + public void testTenants() { + for (String regionId : keystoneContext.getApi().getConfiguredRegions()) { + AdminClient client = keystoneContext.getApi().getAdminClientForRegion(regionId); + Set result = client.listTenants(); + assertNotNull(result); + assertFalse(result.isEmpty()); + + for (Tenant tenant : result) { + assertNotNull(tenant.getId()); + + Tenant aTenant = client.getTenant(tenant.getId()); + assertEquals(aTenant, tenant); + } + } + } + + public void testToken() { + for (String regionId : keystoneContext.getApi().getConfiguredRegions()) { + AdminClient client = keystoneContext.getApi().getAdminClientForRegion(regionId); + assertTrue(client.checkTokenIsValid(token)); + Token result = client.getToken(token); + assertNotNull(result); + assertEquals(result.getId(), token); + assertNotNull(result.getTenant()); + + User user = client.getUserOfToken(token); + assertNotNull(user); + assertNotNull(user.getId()); + assertNotNull(user.getName()); + } + } + + public void testInvalidToken() { + for (String regionId : keystoneContext.getApi().getConfiguredRegions()) { + AdminClient client = keystoneContext.getApi().getAdminClientForRegion(regionId); + assertFalse(client.checkTokenIsValid("thisisnotarealtoken!")); + assertNull(client.getToken("thisisnotarealtoken!")); + } + } + + // TODO this threw 501 not implemented + @Test(enabled = false) + public void testTokenEndpoints() { + for (String regionId : keystoneContext.getApi().getConfiguredRegions()) { + AdminClient client = keystoneContext.getApi().getAdminClientForRegion(regionId); + Set endpoints = client.getEndpointsForToken(token); + assertNotNull(endpoints); + assertFalse(endpoints.isEmpty()); + } + } + + // TODO this threw 501 not implemented + @Test(enabled = false) + public void testInvalidTokenEndpoints() { + for (String regionId : keystoneContext.getApi().getConfiguredRegions()) { + AdminClient client = keystoneContext.getApi().getAdminClientForRegion(regionId); + assertTrue(client.getEndpointsForToken("thisisnotarealtoken!").isEmpty()); + } + } + + public void testUsers() { + for (String regionId : keystoneContext.getApi().getConfiguredRegions()) { + AdminClient client = keystoneContext.getApi().getAdminClientForRegion(regionId); + Set users = client.listUsers(); + assertNotNull(users); + assertFalse(users.isEmpty()); + for (User user : users) { + User aUser = client.getUser(user.getId()); + assertEquals(aUser, user); + } + } + } + + @Test(dependsOnMethods = {"testUsers", "testTenants"}) + public void testUserRolesOnTenant() { + for (String regionId : keystoneContext.getApi().getConfiguredRegions()) { + AdminClient client = keystoneContext.getApi().getAdminClientForRegion(regionId); + Set users = client.listUsers(); + Set tenants = client.listTenants(); + + for (User user : users) { + for (Tenant tenant : tenants) { + Set roles = client.listRolesOfUserOnTenant(user.getId(), tenant.getId()); + for (Role role : roles) { + assertNotNull(role.getId()); + } + } + } + } + } + + // TODO this functionality is currently broken in openstack + @Test(enabled = false, dependsOnMethods = "testUsers") + public void testListRolesOfUser() { + for (String regionId : keystoneContext.getApi().getConfiguredRegions()) { + AdminClient client = keystoneContext.getApi().getAdminClientForRegion(regionId); + for (User user : client.listUsers()) { + Set roles = client.listRolesOfUser(user.getId()); + for (Role role : roles) { + assertNotNull(role.getId()); + } + } + } + } + + // TODO this functionality is currently broken in openstack (possibly deprecated?) + @Test(enabled=false, dependsOnMethods = {"testUsers", "testTenants"}) + public void testUsersAndTenantsByName() { + for (String regionId : keystoneContext.getApi().getConfiguredRegions()) { + AdminClient client = keystoneContext.getApi().getAdminClientForRegion(regionId); + for (User user : client.listUsers()) { + User aUser = client.getUserByName(user.getName()); + assertEquals(aUser, user); + } + for (Tenant tenant : client.listTenants()) { + Tenant aTenant = client.getTenantByName(tenant.getName()); + assertEquals(aTenant, tenant); + } + } + } +} \ No newline at end of file diff --git a/labs/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/UserClientExpectTest.java b/labs/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/UserClientExpectTest.java new file mode 100644 index 0000000000..58697da9e3 --- /dev/null +++ b/labs/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/UserClientExpectTest.java @@ -0,0 +1,95 @@ +/** + * 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 1.1 (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-1.1 + * + * 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; + +import static javax.ws.rs.core.MediaType.APPLICATION_JSON; +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertNotNull; + +import java.net.URI; +import java.util.Set; + +import org.jclouds.date.internal.SimpleDateFormatDateService; +import org.jclouds.http.HttpRequest; +import org.jclouds.http.HttpResponse; +import org.jclouds.openstack.domain.Link; +import org.jclouds.openstack.keystone.v2_0.domain.ApiMetadata; +import org.jclouds.openstack.keystone.v2_0.domain.MediaType; +import org.jclouds.openstack.keystone.v2_0.domain.Tenant; +import org.jclouds.openstack.keystone.v2_0.internal.BaseKeystoneRestClientExpectTest; + +import com.google.common.collect.ImmutableMultimap; +import com.google.common.collect.ImmutableSet; + +/** + * Tests parsing and Guice wiring of UserClient + * + * @author Adam Lowe + */ +public class UserClientExpectTest extends BaseKeystoneRestClientExpectTest { + + public void testGetApiMetaData() { + ServiceClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, + HttpRequest.builder().method("GET").endpoint(URI.create(endpoint + "/v2.0/")). + headers(ImmutableMultimap.of("Accept", APPLICATION_JSON)).build(), + HttpResponse.builder().statusCode(200). + payload(payloadFromResourceWithContentType("/api_metadata.json", APPLICATION_JSON)).build()) + .getServiceClientForRegion("region-a.geo-1"); + ApiMetadata metadata = client.getApiMetadata(); + assertNotNull(metadata); + assertEquals(metadata.getId(), "v2.0"); + + ApiMetadata expected = ApiMetadata.builder().id("v2.0") + .links(ImmutableSet.of(Link.builder().relation(Link.Relation.SELF).href(URI.create("http://172.16.89.140:5000/v2.0/")).build(), + Link.builder().relation(Link.Relation.DESCRIBEDBY).type("text/html").href(URI.create("http://docs.openstack.org/api/openstack-identity-service/2.0/content/")).build(), + Link.builder().relation(Link.Relation.DESCRIBEDBY).type("application/pdf").href(URI.create("http://docs.openstack.org/api/openstack-identity-service/2.0/identity-dev-guide-2.0.pdf")).build() + )) + .status("beta") + .updated(new SimpleDateFormatDateService().iso8601SecondsDateParse("2011-11-19T00:00:00Z")) + .mediaTypes(ImmutableSet.of( + MediaType.builder().base("application/json").type("application/vnd.openstack.identity-v2.0+json").build(), + MediaType.builder().base("application/xml").type("application/vnd.openstack.identity-v2.0+xml").build() + )) + .build(); + + assertEquals(metadata, expected); + } + + public void testListTenants() { + ServiceClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, + HttpRequest.builder().method("GET").endpoint(URI.create(endpoint + "/v2.0/tenants")). + headers(ImmutableMultimap.of("Accept", APPLICATION_JSON, "X-Auth-Token", authToken)).build(), + HttpResponse.builder().statusCode(200). + payload(payloadFromResourceWithContentType("/tenant_list.json", APPLICATION_JSON)).build()) + .getServiceClientForRegion("region-a.geo-1"); + Set tenants = client.listTenants(); + assertNotNull(tenants); + assertFalse(tenants.isEmpty()); + + Set expected = ImmutableSet.of( + Tenant.builder().name("demo").id("05d1dc7af71646deba64cfc17b81bec0").build(), + Tenant.builder().name("admin").id("7aa2e17ec29f44d193c48feaba0852cc").build() + ); + + assertEquals(tenants, expected); + } +} diff --git a/labs/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/UserClientLiveTest.java b/labs/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/UserClientLiveTest.java new file mode 100644 index 0000000000..9399e9c190 --- /dev/null +++ b/labs/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/UserClientLiveTest.java @@ -0,0 +1,61 @@ +/** + * 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 1.1 (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-1.1 + * + * 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; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertNotNull; + +import java.util.Set; + +import org.jclouds.openstack.keystone.v2_0.domain.ApiMetadata; +import org.jclouds.openstack.keystone.v2_0.domain.Tenant; +import org.jclouds.openstack.keystone.v2_0.internal.BaseKeystoneClientLiveTest; +import org.testng.annotations.Test; + +/** + * Tests UserClient + * + * @author Adam Lowe + */ +@Test(groups = "live", testName = "UserClientLiveTest") +public class UserClientLiveTest extends BaseKeystoneClientLiveTest { + + public void testGetApiMetaData() { + for (String regionId : keystoneContext.getApi().getConfiguredRegions()) { + ApiMetadata result = keystoneContext.getApi().getServiceClientForRegion(regionId).getApiMetadata(); + assertNotNull(result); + assertNotNull(result.getId()); + assertNotNull(result.getStatus()); + assertNotNull(result.getUpdated()); + } + } + + public void testListTenants() { + for (String regionId : keystoneContext.getApi().getConfiguredRegions()) { + Set result = keystoneContext.getApi().getServiceClientForRegion(regionId).listTenants(); + assertNotNull(result); + assertFalse(result.isEmpty()); + + for (Tenant tenant : result) { + assertNotNull(tenant.getId()); + } + } + } +} diff --git a/labs/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/internal/BaseKeystoneClientLiveTest.java b/labs/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/internal/BaseKeystoneClientLiveTest.java new file mode 100644 index 0000000000..ef64a89a4b --- /dev/null +++ b/labs/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/internal/BaseKeystoneClientLiveTest.java @@ -0,0 +1,77 @@ +/** + * 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.internal; + +import static org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties.SERVICE_TYPE; + +import java.util.Properties; + +import org.jclouds.apis.BaseContextLiveTest; +import org.jclouds.openstack.keystone.v2_0.KeystoneApiMetadata; +import org.jclouds.openstack.keystone.v2_0.KeystoneAsyncClient; +import org.jclouds.openstack.keystone.v2_0.KeystoneClient; +import org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties; +import org.jclouds.openstack.services.ServiceType; +import org.jclouds.rest.RestContext; +import org.testng.annotations.AfterGroups; +import org.testng.annotations.BeforeGroups; +import org.testng.annotations.Test; + +import com.google.common.reflect.TypeToken; + +/** + * Tests behavior of {@code KeystoneClient} + * + * @author Adam Lowe + */ +@Test(groups = "live") +public class BaseKeystoneClientLiveTest extends BaseContextLiveTest> { + + public BaseKeystoneClientLiveTest() { + provider = "openstack-keystone"; + } + + protected RestContext keystoneContext; + + @BeforeGroups(groups = { "integration", "live" }) + @Override + public void setupContext() { + super.setupContext(); + keystoneContext = context; + } + + @Override + protected Properties setupProperties() { + Properties props = super.setupProperties(); + setIfTestSystemPropertyPresent(props, KeystoneProperties.CREDENTIAL_TYPE); + return props; + } + + @AfterGroups(groups = "live") + protected void tearDown() { + if (keystoneContext != null) + keystoneContext.close(); + } + + @Override + protected TypeToken> contextType() { + return KeystoneApiMetadata.CONTEXT_TOKEN; + } + +} diff --git a/labs/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/internal/BaseKeystoneRestClientExpectTest.java b/labs/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/internal/BaseKeystoneRestClientExpectTest.java new file mode 100644 index 0000000000..8d8afcffc0 --- /dev/null +++ b/labs/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/internal/BaseKeystoneRestClientExpectTest.java @@ -0,0 +1,86 @@ +/** + * 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 1.1 (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-1.1 + * + * 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.internal; + +import static org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties.SERVICE_TYPE; + +import java.net.URI; +import java.util.Properties; + +import javax.ws.rs.core.MediaType; + +import org.jclouds.Constants; +import org.jclouds.http.HttpRequest; +import org.jclouds.http.HttpResponse; +import org.jclouds.openstack.keystone.v1_1.config.AuthenticationServiceModule; +import org.jclouds.openstack.services.ServiceType; +import org.jclouds.rest.internal.BaseRestClientExpectTest; + +import com.google.common.base.Objects; +import com.google.common.collect.ImmutableMultimap; +import com.google.common.net.HttpHeaders; + +/** + * Base class for writing KeyStone 2.0 Rest Client Expect tests + * + * @author Adam Lowe + */ +public class BaseKeystoneRestClientExpectTest extends BaseRestClientExpectTest { + protected HttpRequest keystoneAuthWithUsernameAndPassword; + protected HttpRequest keystoneAuthWithAccessKeyAndSecretKey; + protected String authToken; + protected HttpResponse responseWithKeystoneAccess; + protected String endpoint = "https://csnode.jclouds.org"; + + public BaseKeystoneRestClientExpectTest() { + provider = "openstack-keystone"; + keystoneAuthWithUsernameAndPassword = KeystoneFixture.INSTANCE.initialAuthWithUsernameAndPassword(identity, + credential); + keystoneAuthWithAccessKeyAndSecretKey = KeystoneFixture.INSTANCE.initialAuthWithAccessKeyAndSecretKey(identity, + credential); + + authToken = KeystoneFixture.INSTANCE.getAuthToken(); + responseWithKeystoneAccess = KeystoneFixture.INSTANCE.responseWithAccess(); + // now, createContext arg will need tenant prefix + identity = KeystoneFixture.INSTANCE.getTenantName() + ":" + identity; + } + + protected HttpRequest.Builder standardRequestBuilder(String endpoint) { + return HttpRequest.builder().method("GET") + .headers(ImmutableMultimap.of("Accept", MediaType.APPLICATION_JSON, "X-Auth-Token", authToken)) + .endpoint(URI.create(endpoint)); + } + + protected HttpResponse.Builder standardResponseBuilder(int status) { + return HttpResponse.builder().statusCode(status); + } + + @Override + protected Properties setupProperties() { + Properties props = super.setupProperties(); + props.put(SERVICE_TYPE, ServiceType.IDENTITY); + return props; + } + + @Override + protected HttpRequestComparisonType compareHttpRequestAsType(HttpRequest input) { + return Objects.equal("HEAD", input.getMethod()) ? HttpRequestComparisonType.DEFAULT : HttpRequestComparisonType.JSON; + } + +} diff --git a/labs/openstack-keystone/src/test/resources/api_metadata.json b/labs/openstack-keystone/src/test/resources/api_metadata.json new file mode 100644 index 0000000000..09934299c9 --- /dev/null +++ b/labs/openstack-keystone/src/test/resources/api_metadata.json @@ -0,0 +1 @@ +{"version": {"status": "beta", "updated": "2011-11-19T00:00:00Z", "media-types": [{"base": "application/json", "type": "application/vnd.openstack.identity-v2.0+json"}, {"base": "application/xml", "type": "application/vnd.openstack.identity-v2.0+xml"}], "id": "v2.0", "links": [{"href": "http://172.16.89.140:5000/v2.0/", "rel": "self"}, {"href": "http://docs.openstack.org/api/openstack-identity-service/2.0/content/", "type": "text/html", "rel": "describedby"}, {"href": "http://docs.openstack.org/api/openstack-identity-service/2.0/identity-dev-guide-2.0.pdf", "type": "application/pdf", "rel": "describedby"}]}} \ No newline at end of file diff --git a/labs/openstack-keystone/src/test/resources/logback.xml b/labs/openstack-keystone/src/test/resources/logback.xml new file mode 100644 index 0000000000..a4f81d3fa6 --- /dev/null +++ b/labs/openstack-keystone/src/test/resources/logback.xml @@ -0,0 +1,51 @@ + + + + target/test-data/jclouds.log + + + %d %-5p [%c] [%thread] %m%n + + + + + target/test-data/jclouds-wire.log + + + %d %-5p [%c] [%thread] %m%n + + + + + target/test-data/jclouds-blobstore.log + + + %d %-5p [%c] [%thread] %m%n + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/labs/openstack-keystone/src/test/resources/tenant_details.json b/labs/openstack-keystone/src/test/resources/tenant_details.json new file mode 100644 index 0000000000..12cdc272e9 --- /dev/null +++ b/labs/openstack-keystone/src/test/resources/tenant_details.json @@ -0,0 +1 @@ +{"tenant": {"enabled": true, "description": null, "name": "admin", "id": "013ba41150a14830bec85ffe93353bcc"}} \ No newline at end of file diff --git a/labs/openstack-keystone/src/test/resources/tenant_list.json b/labs/openstack-keystone/src/test/resources/tenant_list.json new file mode 100644 index 0000000000..aadcd703c0 --- /dev/null +++ b/labs/openstack-keystone/src/test/resources/tenant_list.json @@ -0,0 +1 @@ +{"tenants_links": [], "tenants": [{"enabled": true, "description": null, "name": "demo", "id": "05d1dc7af71646deba64cfc17b81bec0"}, {"enabled": true, "description": null, "name": "admin", "id": "7aa2e17ec29f44d193c48feaba0852cc"}]} \ No newline at end of file diff --git a/labs/openstack-keystone/src/test/resources/token_details.json b/labs/openstack-keystone/src/test/resources/token_details.json new file mode 100644 index 0000000000..530fb5932b --- /dev/null +++ b/labs/openstack-keystone/src/test/resources/token_details.json @@ -0,0 +1 @@ +{"access": {"token": {"expires": "2012-04-28T12:42:50Z", "id": "167eccdc790946969ced473732e8109b", "tenant": {"id": "4cea93f5464b4f1c921fb3e0461d72b5", "enabled": true, "description": null, "name": "demo"}}, "user": {"username": "admin", "roles_links": [], "id": "2b9b606181634ae9ac86fd95a8bc2cde", "roles": [{"id": "79cada5c02814b57a52e0eed4dd388cb", "name": "admin"}], "name": "admin"}}} diff --git a/labs/openstack-keystone/src/test/resources/user_details.json b/labs/openstack-keystone/src/test/resources/user_details.json new file mode 100644 index 0000000000..17f1ae666e --- /dev/null +++ b/labs/openstack-keystone/src/test/resources/user_details.json @@ -0,0 +1 @@ +{"user": {"name": "nova", "enabled": true, "email": "nova@example.com", "id": "e021dfd758eb44a89f1c57c8ef3be8e2", "tenantId": "ab1da202f5774cceb5da2aeff1f0aa87"}} \ No newline at end of file diff --git a/labs/openstack-keystone/src/test/resources/user_endpoints.json b/labs/openstack-keystone/src/test/resources/user_endpoints.json new file mode 100644 index 0000000000..1e73bda239 --- /dev/null +++ b/labs/openstack-keystone/src/test/resources/user_endpoints.json @@ -0,0 +1,10 @@ +{"endpoints": [ + { + "adminURL": "https://csnode.jclouds.org:35357/v2.0/", + "publicURL": "https://csnode.jclouds.org/v2.0/", + "region": "region-a.geo-1", + "id": "2.0", + "versionId":"2.0", + "list": "https://csnode.jclouds.org/extension" + } + ]} \ No newline at end of file diff --git a/labs/openstack-keystone/src/test/resources/user_list.json b/labs/openstack-keystone/src/test/resources/user_list.json new file mode 100644 index 0000000000..641a557b8a --- /dev/null +++ b/labs/openstack-keystone/src/test/resources/user_list.json @@ -0,0 +1 @@ +{"users": [{"name": "admin", "enabled": true, "email": "admin@example.com", "id": "2b9b606181634ae9ac86fd95a8bc2cde", "tenantId": null}, {"name": "demo", "enabled": true, "email": "demo@example.com", "id": "667b2e1420604df8b67cd8ea57d4ee64", "tenantId": null}, {"name": "nova", "enabled": true, "email": "nova@example.com", "id": "e021dfd758eb44a89f1c57c8ef3be8e2", "tenantId": "ab1da202f5774cceb5da2aeff1f0aa87"}, {"name": "glance", "enabled": true, "email": "glance@example.com", "id": "3f6c1c9ba993495ead7d2eb2192e284f", "tenantId": "ab1da202f5774cceb5da2aeff1f0aa87"}]} \ No newline at end of file diff --git a/labs/openstack-keystone/src/test/resources/user_role_list.json b/labs/openstack-keystone/src/test/resources/user_role_list.json new file mode 100644 index 0000000000..ee90ff90ea --- /dev/null +++ b/labs/openstack-keystone/src/test/resources/user_role_list.json @@ -0,0 +1 @@ +{"roles": [{"id": "79cada5c02814b57a52e0eed4dd388cb", "name": "admin"}]} \ No newline at end of file diff --git a/labs/openstack-keystone/src/test/resources/user_tenant_role_list.json b/labs/openstack-keystone/src/test/resources/user_tenant_role_list.json new file mode 100644 index 0000000000..0a25228c84 --- /dev/null +++ b/labs/openstack-keystone/src/test/resources/user_tenant_role_list.json @@ -0,0 +1 @@ +{"roles": [{"id": "31c451195aac49b386039341e2c92a16", "name": "KeystoneServiceAdmin"}, {"id": "79cada5c02814b57a52e0eed4dd388cb", "name": "admin"}, {"id": "6ea17ddd37a6447794cb0e164d4db894", "name": "KeystoneAdmin"}]} \ No newline at end of file diff --git a/labs/pom.xml b/labs/pom.xml index 737e6602c1..521a07e677 100644 --- a/labs/pom.xml +++ b/labs/pom.xml @@ -46,5 +46,6 @@ cdmi openstack-glance joyent-sdc + openstack-keystone