diff --git a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/config/KeystoneAuthenticationModule.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/config/KeystoneAuthenticationModule.java
index 2432e346f0..c1e201211f 100644
--- a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/config/KeystoneAuthenticationModule.java
+++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/config/KeystoneAuthenticationModule.java
@@ -20,7 +20,9 @@ package org.jclouds.openstack.keystone.v2_0.config;
import static com.google.common.base.Preconditions.checkArgument;
import static org.jclouds.rest.config.BinderUtils.bindHttpApi;
+import static org.jclouds.util.Suppliers2.getLastValueInMap;
+import java.net.URI;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
@@ -48,14 +50,17 @@ import org.jclouds.location.suppliers.implicit.FirstZone;
import org.jclouds.openstack.keystone.v2_0.AuthenticationApi;
import org.jclouds.openstack.keystone.v2_0.AuthenticationAsyncApi;
import org.jclouds.openstack.keystone.v2_0.domain.Access;
+import org.jclouds.openstack.keystone.v2_0.domain.Endpoint;
import org.jclouds.openstack.keystone.v2_0.functions.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.LocationIdToURIFromAccessForTypeAndVersion;
import org.jclouds.openstack.keystone.v2_0.suppliers.RegionIdToAdminURIFromAccessForTypeAndVersion;
import org.jclouds.openstack.keystone.v2_0.suppliers.RegionIdToAdminURISupplier;
import org.jclouds.openstack.keystone.v2_0.suppliers.RegionIdToURIFromAccessForTypeAndVersion;
import org.jclouds.openstack.keystone.v2_0.suppliers.ZoneIdToURIFromAccessForTypeAndVersion;
import org.jclouds.rest.annotations.ApiVersion;
+import org.jclouds.rest.config.RestClientModule;
import com.google.common.base.Function;
import com.google.common.base.Supplier;
@@ -75,14 +80,55 @@ import com.google.inject.assistedinject.FactoryModuleBuilder;
* @author Adrian Cole
*/
public class KeystoneAuthenticationModule extends AbstractModule {
-
+
+ /**
+ * For global services who have no regions, such as DNS. To use, do the following
+ *
+ * - add this module to your {@link org.jclouds.apis.ApiMetadata#getDefaultModules()}
+ * - create a service-specific annotation, such as {@code @CloudDNS}, and make sure that has the meta-annotation
+ * {@link javax.inject.Qualifier}
+ * - add the above annotation to any {@link AsyncApi} classes by placing it on the type. ex.
+ * {@code @Endpoint(CloudDNS.class)}
+ * - add the following to your {@link RestClientModule}
+ *
+ *
+ * bind(new TypeLiteral<Supplier<URI>>() {
+ * }).annotatedWith(CloudDNS.class).to(new TypeLiteral<Supplier<URI>>() {
+ * });
+ *
+ */
+ public static class ProviderModule extends AbstractModule {
+ @Override
+ protected void configure() {
+ install(new FactoryModuleBuilder().build(LocationIdToURIFromAccessForTypeAndVersion.Factory.class));
+ }
+
+ @Provides
+ @Singleton
+ protected Supplier provideZoneIdToURISupplierForApiVersion(
+ @Named(KeystoneProperties.SERVICE_TYPE) String serviceType, @ApiVersion String apiVersion,
+ LocationIdToURIFromAccessForTypeAndVersion.Factory factory) {
+ return getLastValueInMap(factory.createForApiTypeAndVersion(serviceType, apiVersion));
+ }
+
+ @Provides
+ @Singleton
+ Function provideProvider(@Provider final String provider) {
+ return new Function() {
+ public String apply(Endpoint in) {
+ return provider;
+ }
+ };
+ }
+ }
+
public static class RegionModule extends AbstractModule {
@Override
protected void configure() {
install(new FactoryModuleBuilder().implement(RegionIdToURISupplier.class,
- RegionIdToURIFromAccessForTypeAndVersion.class).build(RegionIdToURISupplier.Factory.class));
+ RegionIdToURIFromAccessForTypeAndVersion.class).build(RegionIdToURISupplier.Factory.class));
install(new FactoryModuleBuilder().implement(RegionIdToAdminURISupplier.class,
- RegionIdToAdminURIFromAccessForTypeAndVersion.class).build(RegionIdToAdminURISupplier.Factory.class));
+ RegionIdToAdminURIFromAccessForTypeAndVersion.class).build(RegionIdToAdminURISupplier.Factory.class));
// dynamically build the region list as opposed to from properties
bind(RegionIdsSupplier.class).to(RegionIdsFromRegionIdToURIKeySet.class);
bind(ImplicitLocationSupplier.class).to(FirstRegion.class).in(Scopes.SINGLETON);
@@ -94,8 +140,8 @@ public class KeystoneAuthenticationModule extends AbstractModule {
@Provides
@Singleton
protected RegionIdToURISupplier provideRegionIdToURISupplierForApiVersion(
- @Named(KeystoneProperties.SERVICE_TYPE) String serviceType, @ApiVersion String apiVersion,
- RegionIdToURISupplier.Factory factory) {
+ @Named(KeystoneProperties.SERVICE_TYPE) String serviceType, @ApiVersion String apiVersion,
+ RegionIdToURISupplier.Factory factory) {
return factory.createForApiTypeAndVersion(serviceType, apiVersion);
}
@@ -105,8 +151,8 @@ public class KeystoneAuthenticationModule extends AbstractModule {
@Provides
@Singleton
protected RegionIdToAdminURISupplier provideRegionIdToAdminURISupplierForApiVersion(
- @Named(KeystoneProperties.SERVICE_TYPE) String serviceType, @ApiVersion String apiVersion,
- RegionIdToAdminURISupplier.Factory factory) {
+ @Named(KeystoneProperties.SERVICE_TYPE) String serviceType, @ApiVersion String apiVersion,
+ RegionIdToAdminURISupplier.Factory factory) {
return factory.createForApiTypeAndVersion(serviceType, apiVersion);
}
@@ -116,7 +162,7 @@ public class KeystoneAuthenticationModule extends AbstractModule {
@Override
protected void configure() {
install(new FactoryModuleBuilder().implement(ZoneIdToURISupplier.class,
- ZoneIdToURIFromAccessForTypeAndVersion.class).build(ZoneIdToURISupplier.Factory.class));
+ ZoneIdToURIFromAccessForTypeAndVersion.class).build(ZoneIdToURISupplier.Factory.class));
// dynamically build the zone list as opposed to from properties
bind(ZoneIdsSupplier.class).to(ZoneIdsFromZoneIdToURIKeySet.class);
bind(ImplicitLocationSupplier.class).to(FirstZone.class).in(Scopes.SINGLETON);
@@ -128,8 +174,8 @@ public class KeystoneAuthenticationModule extends AbstractModule {
@Provides
@Singleton
protected ZoneIdToURISupplier provideZoneIdToURISupplierForApiVersion(
- @Named(KeystoneProperties.SERVICE_TYPE) String serviceType, @ApiVersion String apiVersion,
- ZoneIdToURISupplier.Factory factory) {
+ @Named(KeystoneProperties.SERVICE_TYPE) String serviceType, @ApiVersion String apiVersion,
+ ZoneIdToURISupplier.Factory factory) {
return factory.createForApiTypeAndVersion(serviceType, apiVersion);
}
@@ -153,7 +199,7 @@ public class KeystoneAuthenticationModule extends AbstractModule {
@Singleton
@Authentication
protected Supplier provideAuthenticationTokenCache(final Supplier supplier)
- throws InterruptedException, ExecutionException, TimeoutException {
+ throws InterruptedException, ExecutionException, TimeoutException {
return new Supplier() {
public String get() {
return supplier.get().getToken().getId();
@@ -173,10 +219,10 @@ public class KeystoneAuthenticationModule extends AbstractModule {
@Provides
@Singleton
protected Function authenticationMethodForCredentialType(
- @Named(KeystoneProperties.CREDENTIAL_TYPE) String credentialType,
- Map> authenticationMethods) {
+ @Named(KeystoneProperties.CREDENTIAL_TYPE) String credentialType,
+ Map> authenticationMethods) {
checkArgument(authenticationMethods.containsKey(credentialType), "credential type %s not in supported list: %s",
- credentialType, authenticationMethods.keySet());
+ credentialType, authenticationMethods.keySet());
return authenticationMethods.get(credentialType);
}
diff --git a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/domain/Limit.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/domain/Limit.java
new file mode 100644
index 0000000000..16d852f4d1
--- /dev/null
+++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/domain/Limit.java
@@ -0,0 +1,96 @@
+/**
+ * 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.v2_0.domain;
+
+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 java.beans.ConstructorProperties;
+import java.util.Date;
+
+import javax.inject.Named;
+
+import org.jclouds.javax.annotation.Nullable;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Optional;
+
+/**
+ * @author Everett Toews
+ */
+public final class Limit {
+
+ private final String verb;
+ private final String unit;
+ private final int value;
+ private final Optional remaining;
+ @Named("next-available")
+ private final Optional nextAvailable;
+
+ @ConstructorProperties({ "verb", "unit", "value", "remaining", "next-available" })
+ private Limit(String verb, String unit, int value, @Nullable Integer remaining, @Nullable Date nextAvailable) {
+ this.verb = checkNotNull(verb, "verb");
+ this.unit = checkNotNull(unit, "unit", unit);
+ this.value = value;
+ this.remaining = Optional.fromNullable(remaining);
+ this.nextAvailable = Optional.fromNullable(nextAvailable);
+ }
+
+ public String getVerb() {
+ return verb;
+ }
+
+ public String getUnit() {
+ return unit;
+ }
+
+ public int getValue() {
+ return value;
+ }
+
+ public Optional getRemaining() {
+ return remaining;
+ }
+
+ public Optional getNextAvailable() {
+ return nextAvailable;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(verb, unit);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null || getClass() != obj.getClass())
+ return false;
+ Limit that = Limit.class.cast(obj);
+ return equal(this.verb, that.verb) && equal(this.unit, that.unit);
+ }
+
+ @Override
+ public String toString() {
+ return toStringHelper(this).omitNullValues().add("verb", verb).add("unit", unit).add("value", value)
+ .add("remaining", remaining.orNull()).add("nextAvailable", nextAvailable.orNull()).toString();
+ }
+}
diff --git a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/domain/Limits.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/domain/Limits.java
new file mode 100644
index 0000000000..23f47472c6
--- /dev/null
+++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/domain/Limits.java
@@ -0,0 +1,75 @@
+/**
+ * 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.v2_0.domain;
+
+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 java.beans.ConstructorProperties;
+import java.util.Map;
+
+import javax.inject.Named;
+
+import com.google.common.base.Objects;
+
+/**
+ * @author Everett Toews
+ */
+public final class Limits {
+
+ @Named("rate")
+ private final Iterable rateLimits;
+ @Named("absolute")
+ private final Map absoluteLimits;
+
+ @ConstructorProperties({ "rate", "absolute" })
+ private Limits(Iterable rateLimits, Map absoluteLimits) {
+ this.rateLimits = checkNotNull(rateLimits, "rateLimits");
+ this.absoluteLimits = checkNotNull(absoluteLimits, "absoluteLimits");
+ }
+
+ public Iterable getRateLimits() {
+ return rateLimits;
+ }
+
+ public Map getAbsoluteLimits() {
+ return absoluteLimits;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(rateLimits, absoluteLimits);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null || getClass() != obj.getClass())
+ return false;
+ Limits that = Limits.class.cast(obj);
+ return equal(this.rateLimits, that.rateLimits) && equal(this.absoluteLimits, that.absoluteLimits);
+ }
+
+ @Override
+ public String toString() {
+ return toStringHelper(this).add("rateLimits", rateLimits).add("absoluteLimits", absoluteLimits).toString();
+ }
+}
diff --git a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/domain/RateLimit.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/domain/RateLimit.java
new file mode 100644
index 0000000000..5a3ef1c657
--- /dev/null
+++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/domain/RateLimit.java
@@ -0,0 +1,79 @@
+/**
+ * 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.v2_0.domain;
+
+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 java.beans.ConstructorProperties;
+
+import javax.inject.Named;
+
+import com.google.common.base.Objects;
+
+/**
+ * @author Everett Toews
+ */
+public final class RateLimit {
+
+ private final String uri;
+ private final String regex;
+ @Named("limit")
+ private final Iterable limits;
+
+ @ConstructorProperties({ "uri", "regex", "limit" })
+ private RateLimit(String uri, String regex, Iterable limits) {
+ this.uri = checkNotNull(uri, "uri");
+ this.regex = checkNotNull(regex, "regex");
+ this.limits = checkNotNull(limits, "limit");
+ }
+
+ public String getUri() {
+ return uri;
+ }
+
+ public String getRegex() {
+ return regex;
+ }
+
+ public Iterable getLimits() {
+ return limits;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(uri);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null || getClass() != obj.getClass())
+ return false;
+ RateLimit that = RateLimit.class.cast(obj);
+ return equal(this.uri, that.uri);
+ }
+
+ @Override
+ public String toString() {
+ return toStringHelper(this).add("uri", uri).add("regex", regex).add("limits", limits).toString();
+ }
+}
diff --git a/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/KeystoneApiExpectTest.java b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/KeystoneApiExpectTest.java
index a1e6b2fd43..37f23372a7 100644
--- a/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/KeystoneApiExpectTest.java
+++ b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/KeystoneApiExpectTest.java
@@ -38,7 +38,7 @@ public class KeystoneApiExpectTest extends BaseKeystoneRestApiExpectTest {
+
+ @Retention(RUNTIME)
+ @Target(TYPE)
+ @Qualifier
+ static @interface DNS {
+ }
+
+ @ConfiguresRestClient
+ public static class DNSRestClientModule extends RestClientModule {
+ @Override
+ public void configure() {
+ bind(new TypeLiteral>() {}).annotatedWith(DNS.class).to(new TypeLiteral>() {});
+ bind(DateAdapter.class).to(Iso8601DateAdapter.class);
+ super.configure();
+ }
+ }
+
+ static interface DNSApi {
+ boolean zoneExists(@PathParam("zoneName") String zoneName);
+ }
+
+ @org.jclouds.rest.annotations.Endpoint(DNS.class)
+ static interface DNSAsyncApi {
+ @HEAD
+ @Path("/zones/{zoneName}")
+ @Fallback(FalseOnNotFoundOr404.class)
+ public ListenableFuture zoneExists(@PathParam("zoneName") String zoneName);
+ }
+
+ public void testDNSEndpointApplied() {
+ KeystoneAuth keystoneAuth = new KeystoneAuth();
+
+ DNSApi api = requestsSendResponses(
+ keystoneAuth.getKeystoneAuthWithUsernameAndPassword(),
+ keystoneAuth.getResponseWithKeystoneAccess(),
+ HttpRequest.builder().method("HEAD").endpoint("http://172.16.0.1:8776/v1/3456/zones/foo.com").build(),
+ HttpResponse.builder().statusCode(200).build());
+ assertTrue(api.zoneExists("foo.com"));
+ }
+
+ private static class DNSApiMetadata extends BaseRestApiMetadata {
+
+ @Override
+ public Builder toBuilder() {
+ return new Builder().fromApiMetadata(this);
+ }
+
+ public DNSApiMetadata() {
+ this(new Builder());
+ }
+
+ protected DNSApiMetadata(Builder builder) {
+ super(builder);
+ }
+
+ public static Properties defaultProperties() {
+ Properties properties = BaseRestApiMetadata.defaultProperties();
+ properties.setProperty(SERVICE_TYPE, "dns");
+ properties.setProperty(CREDENTIAL_TYPE, CredentialTypes.PASSWORD_CREDENTIALS);
+ return properties;
+ }
+
+ public static class Builder extends BaseRestApiMetadata.Builder {
+
+ protected Builder() {
+ super(DNSApi.class, DNSAsyncApi.class);
+ id("dns")
+ .name("DNS API")
+ .identityName("${tenantName}:${userName} or ${userName}, if your keystone supports a default tenant")
+ .credentialName("${password}")
+ .endpointName("Keystone base url ending in /v2.0/")
+ .documentation(URI.create("http://dns"))
+ .version("1.0")
+ .defaultEndpoint("http://localhost:5000/v2.0/")
+ .defaultProperties(DNSApiMetadata.defaultProperties())
+ .defaultModules(
+ ImmutableSet.> builder()
+ .add(KeystoneAuthenticationModule.class)
+ .add(ProviderModule.class)
+ .add(DNSRestClientModule.class)
+ .build());
+ }
+
+ @Override
+ public DNSApiMetadata build() {
+ return new DNSApiMetadata(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+ }
+
+ @Override
+ public ApiMetadata createApiMetadata() {
+ return new DNSApiMetadata();
+ }
+
+ public static class KeystoneAuth extends BaseKeystoneRestApiExpectTest {
+ public HttpRequest getKeystoneAuthWithUsernameAndPassword() {
+ return keystoneAuthWithUsernameAndPassword;
+ }
+
+ public HttpResponse getResponseWithKeystoneAccess() {
+ return responseWithKeystoneAccess;
+ }
+ }
+}
\ No newline at end of file
diff --git a/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/features/ServiceApiExpectTest.java b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/features/ServiceApiExpectTest.java
index f5e63a22af..0ac255c649 100644
--- a/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/features/ServiceApiExpectTest.java
+++ b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/features/ServiceApiExpectTest.java
@@ -44,7 +44,7 @@ public class ServiceApiExpectTest extends BaseKeystoneRestApiExpectTest extends BaseRestApiExpectTest {
+ protected HttpRequest keystoneAuthWithUsernameAndPasswordAndTenantName;
protected HttpRequest keystoneAuthWithUsernameAndPassword;
protected HttpRequest keystoneAuthWithAccessKeyAndSecretKey;
protected String authToken;
@@ -47,10 +48,12 @@ public class BaseKeystoneRestApiExpectTest extends BaseRestApiExpectTest {
public BaseKeystoneRestApiExpectTest() {
provider = "openstack-keystone";
- keystoneAuthWithUsernameAndPassword = KeystoneFixture.INSTANCE.initialAuthWithUsernameAndPasswordAndTenantName(identity,
- credential);
- keystoneAuthWithAccessKeyAndSecretKey = KeystoneFixture.INSTANCE.initialAuthWithAccessKeyAndSecretKeyAndTenantName(identity,
- credential);
+ keystoneAuthWithUsernameAndPasswordAndTenantName = KeystoneFixture.INSTANCE
+ .initialAuthWithUsernameAndPasswordAndTenantName(identity, credential);
+ keystoneAuthWithUsernameAndPassword = KeystoneFixture.INSTANCE
+ .initialAuthWithUsernameAndPassword(identity, credential);
+ keystoneAuthWithAccessKeyAndSecretKey = KeystoneFixture.INSTANCE
+ .initialAuthWithAccessKeyAndSecretKeyAndTenantName(identity, credential);
authToken = KeystoneFixture.INSTANCE.getAuthToken();
responseWithKeystoneAccess = KeystoneFixture.INSTANCE.responseWithAccess();
@@ -59,10 +62,8 @@ public class BaseKeystoneRestApiExpectTest extends BaseRestApiExpectTest {
}
protected HttpRequest.Builder> authenticatedGET() {
- return HttpRequest.builder()
- .method("GET")
- .addHeader("Accept", MediaType.APPLICATION_JSON)
- .addHeader("X-Auth-Token", authToken);
+ return HttpRequest.builder().method("GET").addHeader("Accept", MediaType.APPLICATION_JSON)
+ .addHeader("X-Auth-Token", authToken);
}
@Override
@@ -75,7 +76,7 @@ public class BaseKeystoneRestApiExpectTest extends BaseRestApiExpectTest {
@Override
protected HttpRequestComparisonType compareHttpRequestAsType(HttpRequest input) {
return Objects.equal("HEAD", input.getMethod()) ? HttpRequestComparisonType.DEFAULT
- : HttpRequestComparisonType.JSON;
+ : HttpRequestComparisonType.JSON;
}
@Override
diff --git a/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/parse/ParseAccessTest.java b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/parse/ParseAccessTest.java
index b6d2f0af29..d337e96c85 100644
--- a/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/parse/ParseAccessTest.java
+++ b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/parse/ParseAccessTest.java
@@ -115,7 +115,12 @@ public class ParseAccessTest extends BaseItemParserTest {
.publicURL("http://172.16.0.1:8776/v1/50cdb4c60374463198695d9f798fa34d")
.internalURL("http://10.0.2.15:8776/v1/50cdb4c60374463198695d9f798fa34d")
.adminURL("http://10.0.2.15:8776/v1/50cdb4c60374463198695d9f798fa34d")
- .region("RegionOne").build()).build()).build();
+ .region("RegionOne").build()).build())
+ .service(Service.builder().name("dns").type("dns")
+ .endpoint(Endpoint.builder()
+ .publicURL("http://172.16.0.1:8776/v1/3456")
+ .tenantId("3456")
+ .build()).build()).build();
}
}
diff --git a/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/parse/ParseRackspaceAccessTest.java b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/parse/ParseRackspaceAccessTest.java
index c18fa27e70..6474127042 100644
--- a/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/parse/ParseRackspaceAccessTest.java
+++ b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/parse/ParseRackspaceAccessTest.java
@@ -112,7 +112,7 @@ public class ParseRackspaceAccessTest extends BaseItemParserTest {
.endpoint(Endpoint.builder()
.tenantId("40806637803162")
.publicURL("https://monitoring.api.rackspacecloud.com/v1.0/40806637803162").build()).build())
- .service(Service.builder().name("cloudDNS").type("dnsextension:dns")
+ .service(Service.builder().name("cloudDNS").type("rax:dns")
.endpoint(Endpoint.builder()
.tenantId("40806637803162")
.publicURL("https://dns.api.rackspacecloud.com/v1.0/40806637803162").build()).build())
diff --git a/apis/openstack-keystone/src/test/resources/keystoneAuthResponse.json b/apis/openstack-keystone/src/test/resources/keystoneAuthResponse.json
index a35f9f90a8..3798ff59a0 100644
--- a/apis/openstack-keystone/src/test/resources/keystoneAuthResponse.json
+++ b/apis/openstack-keystone/src/test/resources/keystoneAuthResponse.json
@@ -138,6 +138,16 @@
}
],
"endpoints_links": []
+ },
+ {
+ "type": "dns",
+ "name": "dns",
+ "endpoints": [
+ {
+ "publicURL": "http://172.16.0.1:8776/v1/3456",
+ "tenantId": "3456"
+ }
+ ]
}
]
}
diff --git a/apis/openstack-keystone/src/test/resources/raxAuth.json b/apis/openstack-keystone/src/test/resources/raxAuth.json
index 1044effa3f..aeb96dd5c6 100644
--- a/apis/openstack-keystone/src/test/resources/raxAuth.json
+++ b/apis/openstack-keystone/src/test/resources/raxAuth.json
@@ -87,7 +87,7 @@
"publicURL": "https:\/\/dns.api.rackspacecloud.com\/v1.0\/40806637803162"
}],
"name": "cloudDNS",
- "type": "dnsextension:dns"
+ "type": "rax:dns"
}, {
"endpoints": [{
"region": "DFW",
diff --git a/apis/rackspace-cloudidentity/src/main/java/org/jclouds/rackspace/cloudidentity/v2_0/ServiceType.java b/apis/rackspace-cloudidentity/src/main/java/org/jclouds/rackspace/cloudidentity/v2_0/ServiceType.java
index b79d631bad..1b91aa6f48 100644
--- a/apis/rackspace-cloudidentity/src/main/java/org/jclouds/rackspace/cloudidentity/v2_0/ServiceType.java
+++ b/apis/rackspace-cloudidentity/src/main/java/org/jclouds/rackspace/cloudidentity/v2_0/ServiceType.java
@@ -30,4 +30,9 @@ public interface ServiceType {
* Cloud Load Balancers
*/
public static final String LOAD_BALANCERS = "rax:load-balancer";
+
+ /**
+ * Cloud Load DNS
+ */
+ public static final String DNS = "rax:dns";
}
diff --git a/core/src/main/java/org/jclouds/rest/internal/RestAnnotationProcessor.java b/core/src/main/java/org/jclouds/rest/internal/RestAnnotationProcessor.java
index f5d629724b..05b3677101 100644
--- a/core/src/main/java/org/jclouds/rest/internal/RestAnnotationProcessor.java
+++ b/core/src/main/java/org/jclouds/rest/internal/RestAnnotationProcessor.java
@@ -818,8 +818,8 @@ public class RestAnnotationProcessor implements Function
+
+
+ 4.0.0
+
+ org.jclouds
+ jclouds-project
+ 1.6.0-SNAPSHOT
+ ../../project/pom.xml
+
+ org.jclouds.labs
+ rackspace-clouddns
+ jclouds rackspace clouddns api
+ jclouds components for Rackspace Cloud DNS
+ bundle
+
+
+ https://identity.api.rackspacecloud.com/v2.0/
+ 1.0
+
+ ${test.rackspace.identity}
+ ${test.rackspace.credential}
+
+ org.jclouds.rackspace.clouddns.v1*;version="${project.version}"
+
+ org.jclouds.rest.internal;version="${project.version}",
+ org.jclouds*;version="${project.version}",
+ *
+
+
+
+
+
+ org.jclouds
+ jclouds-core
+ ${project.version}
+
+
+ org.jclouds.api
+ openstack-keystone
+ ${project.version}
+
+
+ org.jclouds.api
+ rackspace-cloudidentity
+ ${project.version}
+
+
+ org.jclouds
+ jclouds-core
+ ${project.version}
+ test-jar
+ test
+
+
+ org.jclouds.api
+ openstack-keystone
+ ${project.version}
+ test-jar
+ test
+
+
+ org.jclouds.api
+ rackspace-cloudidentity
+ ${project.version}
+ test-jar
+ test
+
+
+ org.jclouds.driver
+ jclouds-slf4j
+ ${project.version}
+ test
+
+
+ ch.qos.logback
+ logback-classic
+ test
+
+
+
+
+
+ live
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+
+ integration
+ integration-test
+
+ test
+
+
+
+ 1
+
+ ${test.rackspace-clouddns.endpoint}
+ ${test.rackspace-clouddns.api-version}
+ ${test.rackspace-clouddns.build-version}
+ ${test.rackspace-clouddns.identity}
+ ${test.rackspace-clouddns.credential}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/labs/rackspace-clouddns/src/main/java/org/jclouds/rackspace/clouddns/v1/CloudDNSApi.java b/labs/rackspace-clouddns/src/main/java/org/jclouds/rackspace/clouddns/v1/CloudDNSApi.java
new file mode 100644
index 0000000000..c347ffe7e2
--- /dev/null
+++ b/labs/rackspace-clouddns/src/main/java/org/jclouds/rackspace/clouddns/v1/CloudDNSApi.java
@@ -0,0 +1,37 @@
+/**
+ * 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.rackspace.clouddns.v1;
+
+import org.jclouds.rackspace.clouddns.v1.features.LimitApi;
+import org.jclouds.rest.annotations.Delegate;
+
+/**
+ * Provides access to the Rackspace Cloud DNS API.
+ *
+ * See Cloud DNS Developer Guide
+ * @see CloudDNSAsyncApi
+ * @author Everett Toews
+ */
+public interface CloudDNSApi {
+ /**
+ * Provides synchronous access to Limit features.
+ */
+ @Delegate
+ LimitApi getLimitApi();
+}
diff --git a/labs/rackspace-clouddns/src/main/java/org/jclouds/rackspace/clouddns/v1/CloudDNSApiMetadata.java b/labs/rackspace-clouddns/src/main/java/org/jclouds/rackspace/clouddns/v1/CloudDNSApiMetadata.java
new file mode 100644
index 0000000000..e2c0f6f5aa
--- /dev/null
+++ b/labs/rackspace-clouddns/src/main/java/org/jclouds/rackspace/clouddns/v1/CloudDNSApiMetadata.java
@@ -0,0 +1,101 @@
+/**
+ * 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.rackspace.clouddns.v1;
+
+import static org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties.CREDENTIAL_TYPE;
+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.KeystoneAuthenticationModule.ProviderModule;
+import org.jclouds.rackspace.clouddns.v1.config.CloudDNSRestClientModule;
+import org.jclouds.rackspace.cloudidentity.v2_0.ServiceType;
+import org.jclouds.rackspace.cloudidentity.v2_0.config.CloudIdentityAuthenticationModule;
+import org.jclouds.rackspace.cloudidentity.v2_0.config.CloudIdentityCredentialTypes;
+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 ApiMetadata} for DynECT 1.0 API
+ *
+ * @author Everett Toews
+ */
+public class CloudDNSApiMetadata extends BaseRestApiMetadata {
+
+ public static final TypeToken> CONTEXT_TOKEN = new TypeToken>() {
+ private static final long serialVersionUID = 1L;
+ };
+
+ @Override
+ public Builder toBuilder() {
+ return new Builder().fromApiMetadata(this);
+ }
+
+ public CloudDNSApiMetadata() {
+ this(new Builder());
+ }
+
+ protected CloudDNSApiMetadata(Builder builder) {
+ super(builder);
+ }
+
+ public static Properties defaultProperties() {
+ Properties properties = BaseRestApiMetadata.defaultProperties();
+ properties.setProperty(SERVICE_TYPE, ServiceType.DNS);
+ properties.setProperty(CREDENTIAL_TYPE, CloudIdentityCredentialTypes.API_KEY_CREDENTIALS);
+ return properties;
+ }
+
+ public static class Builder extends BaseRestApiMetadata.Builder {
+
+ protected Builder() {
+ super(CloudDNSApi.class, CloudDNSAsyncApi.class);
+ id("rackspace-clouddns")
+ .name("Rackspace Cloud DNS API")
+ .identityName("Username")
+ .credentialName("API Key")
+ .documentation(URI.create("http://docs.rackspace.com/cdns/api/v1.0/cdns-devguide/content/index.html"))
+ .version("1.0")
+ .defaultEndpoint("https://identity.api.rackspacecloud.com/v2.0/")
+ .defaultProperties(CloudDNSApiMetadata.defaultProperties())
+ .defaultModules(
+ ImmutableSet.> builder()
+ .add(CloudIdentityAuthenticationModule.class)
+ .add(ProviderModule.class)
+ .add(CloudDNSRestClientModule.class)
+ .build());
+ }
+
+ @Override
+ public CloudDNSApiMetadata build() {
+ return new CloudDNSApiMetadata(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
diff --git a/labs/rackspace-clouddns/src/main/java/org/jclouds/rackspace/clouddns/v1/CloudDNSAsyncApi.java b/labs/rackspace-clouddns/src/main/java/org/jclouds/rackspace/clouddns/v1/CloudDNSAsyncApi.java
new file mode 100644
index 0000000000..0992a7a984
--- /dev/null
+++ b/labs/rackspace-clouddns/src/main/java/org/jclouds/rackspace/clouddns/v1/CloudDNSAsyncApi.java
@@ -0,0 +1,37 @@
+/**
+ * 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.rackspace.clouddns.v1;
+
+import org.jclouds.rackspace.clouddns.v1.features.LimitAsyncApi;
+import org.jclouds.rest.annotations.Delegate;
+
+/**
+ * Provides access to the Rackspace Cloud DNS API.
+ *
+ *
+ * @see CloudDNSApi
+ * @author Everett Toews
+ */
+public interface CloudDNSAsyncApi {
+ /**
+ * Provides asynchronous access to Limit features.
+ */
+ @Delegate
+ LimitAsyncApi getLimitApi();
+}
diff --git a/labs/rackspace-clouddns/src/main/java/org/jclouds/rackspace/clouddns/v1/config/CloudDNS.java b/labs/rackspace-clouddns/src/main/java/org/jclouds/rackspace/clouddns/v1/config/CloudDNS.java
new file mode 100644
index 0000000000..764e0c9aa6
--- /dev/null
+++ b/labs/rackspace-clouddns/src/main/java/org/jclouds/rackspace/clouddns/v1/config/CloudDNS.java
@@ -0,0 +1,38 @@
+/**
+ * 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.rackspace.clouddns.v1.config;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import javax.inject.Qualifier;
+
+/**
+ * Represents a component related to Rackspace Cloud DNS.
+ *
+ * @author Everett Toews
+ */
+@Retention(value = RetentionPolicy.RUNTIME)
+@Target(value = { ElementType.TYPE, ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
+@Qualifier
+public @interface CloudDNS {
+
+}
diff --git a/labs/rackspace-clouddns/src/main/java/org/jclouds/rackspace/clouddns/v1/config/CloudDNSRestClientModule.java b/labs/rackspace-clouddns/src/main/java/org/jclouds/rackspace/clouddns/v1/config/CloudDNSRestClientModule.java
new file mode 100644
index 0000000000..1e99a7c29b
--- /dev/null
+++ b/labs/rackspace-clouddns/src/main/java/org/jclouds/rackspace/clouddns/v1/config/CloudDNSRestClientModule.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.rackspace.clouddns.v1.config;
+
+import java.net.URI;
+import java.util.Map;
+
+import org.jclouds.json.config.GsonModule.DateAdapter;
+import org.jclouds.json.config.GsonModule.Iso8601DateAdapter;
+import org.jclouds.rackspace.clouddns.v1.CloudDNSApi;
+import org.jclouds.rackspace.clouddns.v1.CloudDNSAsyncApi;
+import org.jclouds.rackspace.clouddns.v1.features.LimitApi;
+import org.jclouds.rackspace.clouddns.v1.features.LimitAsyncApi;
+import org.jclouds.rest.ConfiguresRestClient;
+import org.jclouds.rest.config.RestClientModule;
+
+import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableMap;
+import com.google.inject.TypeLiteral;
+
+/**
+ * Configures Rackspace Cloud DNS.
+ *
+ * @author Everett Toews
+ */
+@ConfiguresRestClient
+public class CloudDNSRestClientModule extends RestClientModule {
+
+ public static final Map, Class>> DELEGATE_MAP = ImmutableMap., Class>> builder()
+ .put(LimitApi.class, LimitAsyncApi.class)
+ .build();
+
+ public CloudDNSRestClientModule() {
+ super(DELEGATE_MAP);
+ }
+
+ @Override
+ protected void configure() {
+ bind(new TypeLiteral>() {}).annotatedWith(CloudDNS.class).to(new TypeLiteral>() {});
+ bind(DateAdapter.class).to(Iso8601DateAdapter.class);
+ super.configure();
+ }
+}
diff --git a/labs/rackspace-clouddns/src/main/java/org/jclouds/rackspace/clouddns/v1/features/LimitApi.java b/labs/rackspace-clouddns/src/main/java/org/jclouds/rackspace/clouddns/v1/features/LimitApi.java
new file mode 100644
index 0000000000..c749dc10fa
--- /dev/null
+++ b/labs/rackspace-clouddns/src/main/java/org/jclouds/rackspace/clouddns/v1/features/LimitApi.java
@@ -0,0 +1,44 @@
+/**
+ * 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, String 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.rackspace.clouddns.v1.features;
+
+import org.jclouds.openstack.keystone.v2_0.filters.AuthenticateRequest;
+import org.jclouds.openstack.v2_0.domain.Limits;
+import org.jclouds.rest.annotations.RequestFilters;
+
+/**
+ * All accounts, by default, have a preconfigured set of thresholds (or limits) to manage capacity and prevent abuse
+ * of the system. The system recognizes two kinds of limits: rate limits and absolute limits. Rate limits are
+ * thresholds that are reset after a certain amount of time passes. Absolute limits are fixed.
+ *
+ * @see LimitApi
+ * @author Everett Toews
+ */
+@RequestFilters(AuthenticateRequest.class)
+public interface LimitApi {
+ /**
+ * Provides a list of all applicable limits.
+ */
+ Limits list();
+
+ /**
+ * All applicable limit types.
+ */
+ Iterable listTypes();
+}
diff --git a/labs/rackspace-clouddns/src/main/java/org/jclouds/rackspace/clouddns/v1/features/LimitAsyncApi.java b/labs/rackspace-clouddns/src/main/java/org/jclouds/rackspace/clouddns/v1/features/LimitAsyncApi.java
new file mode 100644
index 0000000000..210466db0c
--- /dev/null
+++ b/labs/rackspace-clouddns/src/main/java/org/jclouds/rackspace/clouddns/v1/features/LimitAsyncApi.java
@@ -0,0 +1,67 @@
+/**
+ * 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, String 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.rackspace.clouddns.v1.features;
+
+import javax.inject.Named;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.MediaType;
+
+import org.jclouds.Fallbacks.EmptyListOnNotFoundOr404;
+import org.jclouds.Fallbacks.NullOnNotFoundOr404;
+import org.jclouds.openstack.keystone.v2_0.filters.AuthenticateRequest;
+import org.jclouds.openstack.v2_0.domain.Limits;
+import org.jclouds.rackspace.clouddns.v1.config.CloudDNS;
+import org.jclouds.rest.annotations.Endpoint;
+import org.jclouds.rest.annotations.Fallback;
+import org.jclouds.rest.annotations.RequestFilters;
+import org.jclouds.rest.annotations.SelectJson;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * @see LimitAsyncApi
+ * @author Everett Toews
+ */
+@Endpoint(CloudDNS.class)
+@RequestFilters(AuthenticateRequest.class)
+public interface LimitAsyncApi {
+ /**
+ * @see LimitApi#list()
+ */
+ @Named("limits:list")
+ @GET
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Fallback(NullOnNotFoundOr404.class)
+ @SelectJson("limits")
+ @Path("/limits")
+ ListenableFuture list();
+
+ /**
+ * @see LimitApi#listTypes()
+ */
+ @Named("limits:list")
+ @GET
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Fallback(EmptyListOnNotFoundOr404.class)
+ @SelectJson("limitTypes")
+ @Path("/limits/types")
+ ListenableFuture> listTypes();
+}
diff --git a/labs/rackspace-clouddns/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata b/labs/rackspace-clouddns/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
new file mode 100644
index 0000000000..eae6b81b84
--- /dev/null
+++ b/labs/rackspace-clouddns/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
@@ -0,0 +1 @@
+org.jclouds.rackspace.clouddns.v1.CloudDNSApiMetadata
\ No newline at end of file
diff --git a/labs/rackspace-clouddns/src/test/java/org/jclouds/rackspace/clouddns/v1/features/LimitApiExpectTest.java b/labs/rackspace-clouddns/src/test/java/org/jclouds/rackspace/clouddns/v1/features/LimitApiExpectTest.java
new file mode 100644
index 0000000000..1ff83f61be
--- /dev/null
+++ b/labs/rackspace-clouddns/src/test/java/org/jclouds/rackspace/clouddns/v1/features/LimitApiExpectTest.java
@@ -0,0 +1,83 @@
+/**
+ * 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.rackspace.clouddns.v1.features;
+
+import static org.testng.Assert.assertEquals;
+
+import java.net.URI;
+
+import org.jclouds.http.HttpResponse;
+import org.jclouds.openstack.v2_0.domain.Limits;
+import org.jclouds.openstack.v2_0.domain.RateLimit;
+import org.jclouds.rackspace.clouddns.v1.CloudDNSApi;
+import org.jclouds.rackspace.clouddns.v1.internal.BaseCloudDNSApiExpectTest;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+
+/**
+ * @author Everett Toews
+ */
+@Test(groups = "unit")
+public class LimitApiExpectTest extends BaseCloudDNSApiExpectTest {
+ public void testListLimits() {
+ URI endpoint = URI.create("https://dns.api.rackspacecloud.com/v1.0/123123/limits");
+ LimitApi api = requestsSendResponses(
+ rackspaceAuthWithUsernameAndApiKey,
+ responseWithAccess,
+ authenticatedGET().endpoint(endpoint).build(),
+ HttpResponse.builder().statusCode(200).payload(payloadFromResource("/limit-list.json")).build()
+ ).getLimitApi();
+
+ Limits limits = api.list();
+ assertEquals(limits.getAbsoluteLimits().get("records per domain"), Integer.valueOf(500));
+ assertEquals(limits.getAbsoluteLimits().get("domains"), Integer.valueOf(500));
+ assertEquals(Iterables.size(limits.getRateLimits()), 2);
+
+ RateLimit statusRateLimit = Iterables.tryFind(limits.getRateLimits(), isStatusRateLimit()).orNull();
+ assertEquals(statusRateLimit.getRegex(), ".*/v\\d+\\.\\d+/(\\d+/status).*");
+ assertEquals(Iterables.get(statusRateLimit.getLimits(), 0).getVerb(), "GET");
+ assertEquals(Iterables.get(statusRateLimit.getLimits(), 0).getValue(), 5);
+ assertEquals(Iterables.get(statusRateLimit.getLimits(), 0).getRemaining().get(), Integer.valueOf(5));
+ assertEquals(Iterables.get(statusRateLimit.getLimits(), 0).getUnit(), "SECOND");
+ }
+
+ public void testListLimitTypes() {
+ URI endpoint = URI.create("https://dns.api.rackspacecloud.com/v1.0/123123/limits/types");
+ LimitApi api = requestsSendResponses(
+ rackspaceAuthWithUsernameAndApiKey,
+ responseWithAccess,
+ authenticatedGET().endpoint(endpoint).build(),
+ HttpResponse.builder().statusCode(200).payload(payloadFromResource("/limit-types-list.json")).build()
+ ).getLimitApi();
+
+ Iterable limitTypes = api.listTypes();
+ assertEquals(Iterables.size(limitTypes), 3);
+ }
+
+ private static Predicate isStatusRateLimit() {
+ return new Predicate() {
+ @Override
+ public boolean apply(RateLimit rateLimit) {
+ return rateLimit.getUri().contains("status");
+ }
+ };
+ }
+}
diff --git a/labs/rackspace-clouddns/src/test/java/org/jclouds/rackspace/clouddns/v1/features/LimitApiLiveTest.java b/labs/rackspace-clouddns/src/test/java/org/jclouds/rackspace/clouddns/v1/features/LimitApiLiveTest.java
new file mode 100644
index 0000000000..27d338acee
--- /dev/null
+++ b/labs/rackspace-clouddns/src/test/java/org/jclouds/rackspace/clouddns/v1/features/LimitApiLiveTest.java
@@ -0,0 +1,64 @@
+/**
+ * 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.rackspace.clouddns.v1.features;
+
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+import org.jclouds.openstack.v2_0.domain.Limits;
+import org.jclouds.rackspace.clouddns.v1.internal.BaseCloudDNSApiLiveTest;
+import org.testng.annotations.AfterGroups;
+import org.testng.annotations.BeforeGroups;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.Iterables;
+
+/**
+ * @author Everett Toews
+ */
+@Test(groups = "live", singleThreaded = true, testName = "LimitApiLiveTest")
+public class LimitApiLiveTest extends BaseCloudDNSApiLiveTest {
+ @Override
+ @BeforeGroups(groups = { "live" })
+ public void setupContext() {
+ super.setupContext();
+ }
+
+ @Test
+ public void testList() throws Exception {
+ Limits limits = cloudDNSApi.getLimitApi().list();
+ assertNotNull(limits.getAbsoluteLimits());
+ assertNotNull(limits.getRateLimits());
+ assertTrue(limits.getAbsoluteLimits().size() > 1);
+ assertTrue(Iterables.size(limits.getRateLimits()) > 1);
+ }
+
+ @Test
+ public void testListTypes() throws Exception {
+ Iterable limitTypes = cloudDNSApi.getLimitApi().listTypes();
+ assertNotNull(limitTypes);
+ assertTrue(Iterables.size(limitTypes) > 1);
+ }
+
+ @Override
+ @AfterGroups(groups = "live")
+ protected void tearDownContext() {
+ super.tearDownContext();
+ }
+}
diff --git a/labs/rackspace-clouddns/src/test/java/org/jclouds/rackspace/clouddns/v1/internal/BaseCloudDNSApiExpectTest.java b/labs/rackspace-clouddns/src/test/java/org/jclouds/rackspace/clouddns/v1/internal/BaseCloudDNSApiExpectTest.java
new file mode 100644
index 0000000000..6d02a4706c
--- /dev/null
+++ b/labs/rackspace-clouddns/src/test/java/org/jclouds/rackspace/clouddns/v1/internal/BaseCloudDNSApiExpectTest.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.rackspace.clouddns.v1.internal;
+
+import javax.ws.rs.core.MediaType;
+
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.rackspace.cloudidentity.v2_0.internal.RackspaceFixture;
+import org.jclouds.rest.internal.BaseRestApiExpectTest;
+
+/**
+ * @author Everett Toews
+ */
+public class BaseCloudDNSApiExpectTest extends BaseRestApiExpectTest {
+ protected HttpRequest rackspaceAuthWithUsernameAndApiKey;
+
+ protected String authToken;
+ protected HttpResponse responseWithAccess;
+
+ public BaseCloudDNSApiExpectTest() {
+ provider = "rackspace-clouddns";
+
+ rackspaceAuthWithUsernameAndApiKey = RackspaceFixture.INSTANCE
+ .initialAuthWithUsernameAndApiKey(identity, credential);
+
+ authToken = RackspaceFixture.INSTANCE.getAuthToken();
+ responseWithAccess = RackspaceFixture.INSTANCE.responseWithAccess();
+ }
+
+ @Override
+ protected HttpRequestComparisonType compareHttpRequestAsType(HttpRequest input) {
+ return HttpRequestComparisonType.JSON;
+ }
+
+ protected HttpRequest.Builder> authenticatedGET() {
+ return HttpRequest.builder()
+ .method("GET")
+ .addHeader("Accept", MediaType.APPLICATION_JSON)
+ .addHeader("X-Auth-Token", authToken);
+ }
+}
diff --git a/labs/rackspace-clouddns/src/test/java/org/jclouds/rackspace/clouddns/v1/internal/BaseCloudDNSApiLiveTest.java b/labs/rackspace-clouddns/src/test/java/org/jclouds/rackspace/clouddns/v1/internal/BaseCloudDNSApiLiveTest.java
new file mode 100644
index 0000000000..c0c7469f77
--- /dev/null
+++ b/labs/rackspace-clouddns/src/test/java/org/jclouds/rackspace/clouddns/v1/internal/BaseCloudDNSApiLiveTest.java
@@ -0,0 +1,62 @@
+/**
+ * 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.rackspace.clouddns.v1.internal;
+
+import java.util.Properties;
+
+import org.jclouds.apis.BaseContextLiveTest;
+import org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties;
+import org.jclouds.rackspace.clouddns.v1.CloudDNSApi;
+import org.jclouds.rackspace.clouddns.v1.CloudDNSApiMetadata;
+import org.jclouds.rackspace.clouddns.v1.CloudDNSAsyncApi;
+import org.jclouds.rest.RestContext;
+import org.testng.annotations.BeforeGroups;
+
+import com.google.common.reflect.TypeToken;
+
+/**
+ * @author Everett Toews
+ */
+public class BaseCloudDNSApiLiveTest extends BaseContextLiveTest> {
+ protected CloudDNSApi cloudDNSApi;
+
+ public BaseCloudDNSApiLiveTest() {
+ provider = "rackspace-clouddns";
+ }
+
+ @BeforeGroups(groups = { "integration", "live" })
+ @Override
+ public void setupContext() {
+ super.setupContext();
+
+ cloudDNSApi = context.getApi();
+ }
+
+ @Override
+ protected Properties setupProperties() {
+ Properties props = super.setupProperties();
+ setIfTestSystemPropertyPresent(props, KeystoneProperties.CREDENTIAL_TYPE);
+ return props;
+ }
+
+ @Override
+ protected TypeToken> contextType() {
+ return CloudDNSApiMetadata.CONTEXT_TOKEN;
+ }
+}
diff --git a/labs/rackspace-clouddns/src/test/resources/limit-list.json b/labs/rackspace-clouddns/src/test/resources/limit-list.json
new file mode 100644
index 0000000000..27896a3d49
--- /dev/null
+++ b/labs/rackspace-clouddns/src/test/resources/limit-list.json
@@ -0,0 +1,57 @@
+{
+ "limits": {
+ "absolute": {
+ "records per domain": 500,
+ "domains": 500
+ },
+ "rate": [
+ {
+ "limit": [
+ {
+ "verb": "GET",
+ "value": 5,
+ "remaining": 5,
+ "unit": "SECOND",
+ "next-available": "2013-03-06T20:11:14.000Z"
+ }
+ ],
+ "uri": "*/status/*",
+ "regex": ".*/v\\d+\\.\\d+/(\\d+/status).*"
+ },
+ {
+ "limit": [
+ {
+ "verb": "GET",
+ "value": 100,
+ "remaining": 100,
+ "unit": "MINUTE",
+ "next-available": "2013-03-06T20:11:14.000Z"
+ },
+ {
+ "verb": "POST",
+ "value": 25,
+ "remaining": 25,
+ "unit": "MINUTE",
+ "next-available": "2013-03-06T20:11:14.000Z"
+ },
+ {
+ "verb": "PUT",
+ "value": 50,
+ "remaining": 50,
+ "unit": "MINUTE",
+ "next-available": "2013-03-06T20:11:14.000Z"
+ },
+ {
+ "verb": "DELETE",
+ "value": 50,
+ "remaining": 50,
+ "unit": "MINUTE",
+ "next-available": "2013-03-06T20:11:14.000Z"
+ }
+ ],
+ "uri": "*/domains*",
+ "regex": ".*/v\\d+\\.\\d+/(\\d+/domains).*"
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/labs/rackspace-clouddns/src/test/resources/limit-types-list.json b/labs/rackspace-clouddns/src/test/resources/limit-types-list.json
new file mode 100644
index 0000000000..8952e2c628
--- /dev/null
+++ b/labs/rackspace-clouddns/src/test/resources/limit-types-list.json
@@ -0,0 +1,7 @@
+{
+ "limitTypes": [
+ "RATE_LIMIT",
+ "DOMAIN_LIMIT",
+ "DOMAIN_RECORD_LIMIT"
+ ]
+}
\ No newline at end of file
diff --git a/labs/rackspace-clouddns/src/test/resources/logback.xml b/labs/rackspace-clouddns/src/test/resources/logback.xml
new file mode 100644
index 0000000000..9679b2e03a
--- /dev/null
+++ b/labs/rackspace-clouddns/src/test/resources/logback.xml
@@ -0,0 +1,38 @@
+
+
+
+ target/test-data/jclouds.log
+
+
+ %d %-5p [%c] [%thread] %m%n
+
+
+
+
+ target/test-data/jclouds-wire.log
+
+
+ %d %-5p [%c] [%thread] %m%n
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+