http://git-wip-us.apache.org/repos/asf/jclouds/blob/7def8169/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/KeystoneApiMetadata.java
----------------------------------------------------------------------
diff --git 
a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/KeystoneApiMetadata.java
 
b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/KeystoneApiMetadata.java
new file mode 100644
index 0000000..d649563
--- /dev/null
+++ 
b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/KeystoneApiMetadata.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.openstack.keystone.v3;
+
+import static 
org.jclouds.openstack.keystone.config.KeystoneProperties.CREDENTIAL_TYPE;
+import static 
org.jclouds.openstack.keystone.config.KeystoneProperties.KEYSTONE_VERSION;
+import static 
org.jclouds.openstack.keystone.config.KeystoneProperties.SERVICE_TYPE;
+
+import java.net.URI;
+import java.util.Properties;
+
+import org.jclouds.apis.ApiMetadata;
+import org.jclouds.openstack.keystone.auth.config.AuthenticationModule;
+import org.jclouds.openstack.keystone.catalog.config.KeystoneAdminURLModule;
+import org.jclouds.openstack.keystone.catalog.config.ServiceCatalogModule;
+import org.jclouds.openstack.keystone.v2_0.config.KeystoneHttpApiModule;
+import org.jclouds.openstack.keystone.v2_0.config.KeystoneParserModule;
+import org.jclouds.openstack.v2_0.ServiceType;
+import org.jclouds.rest.internal.BaseHttpApiMetadata;
+
+import com.google.auto.service.AutoService;
+import com.google.common.collect.ImmutableSet;
+import com.google.inject.Module;
+
+/**
+ * Implementation of {@link ApiMetadata} for Keystone 3.0 API
+ */
+@AutoService(ApiMetadata.class)
+public class KeystoneApiMetadata extends BaseHttpApiMetadata<KeystoneApi> {
+
+   @Override
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromApiMetadata(this);
+   }
+
+   public KeystoneApiMetadata() {
+      this(new ConcreteBuilder());
+   }
+
+   protected KeystoneApiMetadata(Builder<?> builder) {
+      super(builder);
+   }
+
+   public static Properties defaultProperties() {
+      Properties properties = BaseHttpApiMetadata.defaultProperties();
+      properties.setProperty(CREDENTIAL_TYPE, 
org.jclouds.openstack.keystone.auth.config.CredentialTypes.PASSWORD_CREDENTIALS);
+      properties.setProperty(SERVICE_TYPE, ServiceType.IDENTITY);
+      properties.setProperty(KEYSTONE_VERSION, "2");
+      return properties;
+   }
+
+   public abstract static class Builder<T extends Builder<T>> extends 
BaseHttpApiMetadata.Builder<KeystoneApi, T> {
+
+      protected Builder() {
+          id("openstack-keystone-3")
+         .name("OpenStack Keystone 3.x API")
+         .identityName("${tenantName}:${userName} or ${userName}, if your 
keystone supports a default tenant")
+         .credentialName("${password}")
+         .endpointName("Keystone base url ending in /v${jclouds.api-version}/")
+         .documentation(URI.create("http://api.openstack.org/";))
+         .version("3")
+         .defaultEndpoint("http://localhost:5000/v${jclouds.api-version}/";)
+         .defaultProperties(KeystoneApiMetadata.defaultProperties())
+         .defaultModules(ImmutableSet.<Class<? extends Module>>builder()
+                                     .add(AuthenticationModule.class)
+                                     .add(ServiceCatalogModule.class)
+                                     .add(KeystoneAdminURLModule.class)
+                                     .add(KeystoneParserModule.class)
+                                     
.add(KeystoneHttpApiModule.class).build());
+      }
+
+      @Override
+      public KeystoneApiMetadata build() {
+         return new KeystoneApiMetadata(this);
+      }
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/7def8169/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/auth/V3AuthenticationApi.java
----------------------------------------------------------------------
diff --git 
a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/auth/V3AuthenticationApi.java
 
b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/auth/V3AuthenticationApi.java
new file mode 100644
index 0000000..7a84c21
--- /dev/null
+++ 
b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/auth/V3AuthenticationApi.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.openstack.keystone.v3.auth;
+
+import java.io.Closeable;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.MediaType;
+
+import org.jclouds.openstack.keystone.auth.AuthenticationApi;
+import org.jclouds.openstack.keystone.auth.domain.PasswordCredentials;
+import org.jclouds.openstack.keystone.auth.domain.TenantAndCredentials;
+import org.jclouds.openstack.keystone.auth.domain.TokenCredentials;
+import org.jclouds.openstack.keystone.v3.binders.BindPasswordAuthToJsonPayload;
+import org.jclouds.openstack.keystone.v3.binders.BindTokenAuthToJsonPayload;
+import org.jclouds.openstack.keystone.v3.domain.Token;
+import org.jclouds.openstack.keystone.v3.parsers.ParseToken;
+import org.jclouds.rest.annotations.MapBinder;
+import org.jclouds.rest.annotations.ResponseParser;
+
+import com.google.inject.name.Named;
+
+/**
+ * Provides access to the OpenStack Keystone Authentication API.
+ */
+@Consumes(MediaType.APPLICATION_JSON)
+@Path("/auth/tokens")
+public interface V3AuthenticationApi extends AuthenticationApi, Closeable {
+
+   @Named("token:create")
+   @POST
+   @ResponseParser(ParseToken.class)
+   @MapBinder(BindPasswordAuthToJsonPayload.class)
+   @Override
+   Token authenticatePassword(TenantAndCredentials<PasswordCredentials> 
credentials);
+
+   @Named("token:create")
+   @POST
+   @ResponseParser(ParseToken.class)
+   @MapBinder(BindTokenAuthToJsonPayload.class)
+   @Override
+   Token authenticateToken(TenantAndCredentials<TokenCredentials> credentials);
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/7def8169/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/binders/BindAuthToJsonPayload.java
----------------------------------------------------------------------
diff --git 
a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/binders/BindAuthToJsonPayload.java
 
b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/binders/BindAuthToJsonPayload.java
new file mode 100644
index 0000000..f100988
--- /dev/null
+++ 
b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/binders/BindAuthToJsonPayload.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.openstack.keystone.v3.binders;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Predicates.instanceOf;
+import static com.google.common.collect.Iterables.tryFind;
+
+import java.util.Map;
+
+import org.jclouds.http.HttpRequest;
+import org.jclouds.json.Json;
+import org.jclouds.openstack.keystone.auth.domain.TenantAndCredentials;
+import org.jclouds.openstack.keystone.v3.domain.Auth;
+import org.jclouds.rest.MapBinder;
+import org.jclouds.rest.binders.BindToJsonPayload;
+import org.jclouds.rest.internal.GeneratedHttpRequest;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableMap;
+
+public abstract class BindAuthToJsonPayload<T> extends BindToJsonPayload 
implements MapBinder {
+
+   protected BindAuthToJsonPayload(Json jsonBinder) {
+      super(jsonBinder);
+   }
+
+   protected abstract Auth buildAuth(TenantAndCredentials<T> credentials);
+
+   @Override
+   public <R extends HttpRequest> R bindToRequest(R request, Map<String, 
Object> postParams) {
+      checkArgument(checkNotNull(request, "request") instanceof 
GeneratedHttpRequest,
+            "this binder is only valid for GeneratedHttpRequests!");
+      GeneratedHttpRequest gRequest = (GeneratedHttpRequest) request;
+
+      Optional<Object> authentication = 
tryFind(gRequest.getInvocation().getArgs(),
+            instanceOf(TenantAndCredentials.class));
+      checkArgument(authentication.isPresent(), "no credentials found in the 
api call arguments");
+
+      @SuppressWarnings("unchecked")
+      Auth auth = buildAuth((TenantAndCredentials<T>) authentication.get());
+
+      R authRequest = super.bindToRequest(request, ImmutableMap.of("auth", 
auth));
+      authRequest.getPayload().setSensitive(true);
+
+      return authRequest;
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/7def8169/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/binders/BindPasswordAuthToJsonPayload.java
----------------------------------------------------------------------
diff --git 
a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/binders/BindPasswordAuthToJsonPayload.java
 
b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/binders/BindPasswordAuthToJsonPayload.java
new file mode 100644
index 0000000..41f64bc
--- /dev/null
+++ 
b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/binders/BindPasswordAuthToJsonPayload.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.openstack.keystone.v3.binders;
+
+import static java.util.Collections.singletonList;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.jclouds.json.Json;
+import org.jclouds.openstack.keystone.auth.domain.PasswordCredentials;
+import org.jclouds.openstack.keystone.auth.domain.TenantAndCredentials;
+import org.jclouds.openstack.keystone.v3.domain.Auth;
+import org.jclouds.openstack.keystone.v3.domain.Auth.Identity;
+import org.jclouds.openstack.keystone.v3.domain.Auth.Identity.PasswordAuth;
+import 
org.jclouds.openstack.keystone.v3.domain.Auth.Identity.PasswordAuth.UserAuth;
+import 
org.jclouds.openstack.keystone.v3.domain.Auth.Identity.PasswordAuth.UserAuth.DomainAuth;
+
+@Singleton
+public class BindPasswordAuthToJsonPayload extends 
BindAuthToJsonPayload<PasswordCredentials> {
+
+   @Inject
+   BindPasswordAuthToJsonPayload(Json jsonBinder) {
+      super(jsonBinder);
+   }
+
+   @Override
+   protected Auth buildAuth(TenantAndCredentials<PasswordCredentials> 
credentials) {
+      return Auth.create(Identity.create(
+            singletonList("password"),
+            null,
+            
PasswordAuth.create(UserAuth.create(credentials.credentials().username(),
+                  DomainAuth.create(credentials.tenantName()), 
credentials.credentials().password()))));
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/7def8169/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/binders/BindTokenAuthToJsonPayload.java
----------------------------------------------------------------------
diff --git 
a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/binders/BindTokenAuthToJsonPayload.java
 
b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/binders/BindTokenAuthToJsonPayload.java
new file mode 100644
index 0000000..9d60fad
--- /dev/null
+++ 
b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/binders/BindTokenAuthToJsonPayload.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.openstack.keystone.v3.binders;
+
+import static java.util.Collections.singletonList;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.jclouds.json.Json;
+import org.jclouds.openstack.keystone.auth.domain.TenantAndCredentials;
+import org.jclouds.openstack.keystone.auth.domain.TokenCredentials;
+import org.jclouds.openstack.keystone.v3.domain.Auth;
+import org.jclouds.openstack.keystone.v3.domain.Auth.Identity;
+import org.jclouds.openstack.keystone.v3.domain.Auth.Identity.TokenAuth;
+
+@Singleton
+public class BindTokenAuthToJsonPayload extends 
BindAuthToJsonPayload<TokenCredentials> {
+
+   @Inject
+   BindTokenAuthToJsonPayload(Json jsonBinder) {
+      super(jsonBinder);
+   }
+
+   @Override
+   protected Auth buildAuth(TenantAndCredentials<TokenCredentials> 
credentials) {
+      return Auth
+            .create(Identity.create(singletonList("token"), 
TokenAuth.create(credentials.credentials().id()), null));
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/7def8169/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/catalog/V3ServiceCatalog.java
----------------------------------------------------------------------
diff --git 
a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/catalog/V3ServiceCatalog.java
 
b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/catalog/V3ServiceCatalog.java
new file mode 100644
index 0000000..0e86f2b
--- /dev/null
+++ 
b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/catalog/V3ServiceCatalog.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.openstack.keystone.v3.catalog;
+
+import java.util.List;
+
+import javax.annotation.Resource;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.jclouds.logging.Logger;
+import org.jclouds.openstack.keystone.auth.domain.AuthInfo;
+import org.jclouds.openstack.keystone.catalog.ServiceEndpoint;
+import org.jclouds.openstack.keystone.v3.domain.Catalog;
+import org.jclouds.openstack.keystone.v3.domain.Endpoint;
+import org.jclouds.openstack.keystone.v3.domain.Token;
+
+import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableList;
+
+@Singleton
+public class V3ServiceCatalog implements Supplier<List<ServiceEndpoint>> {
+
+   @Resource
+   private Logger logger = Logger.NULL;
+
+   private final Supplier<AuthInfo> authInfo;
+
+   @Inject
+   V3ServiceCatalog(Supplier<AuthInfo> authInfo) {
+      this.authInfo = authInfo;
+   }
+
+   @Override
+   public List<ServiceEndpoint> get() {
+      Token token = (Token) authInfo.get();
+
+      ImmutableList.Builder<ServiceEndpoint> serviceEndpoints = 
ImmutableList.builder();
+      for (Catalog catalog : token.catalog()) {
+         for (Endpoint endpoint : catalog.endpoints()) {
+            
serviceEndpoints.add(ServiceEndpoint.builder().id(endpoint.id()).iface(endpoint.iface())
+                  
.regionId(endpoint.regionId()).type(catalog.type()).url(endpoint.url()).build());
+         }
+      }
+
+      return serviceEndpoints.build();
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/7def8169/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/domain/Auth.java
----------------------------------------------------------------------
diff --git 
a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/domain/Auth.java
 
b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/domain/Auth.java
new file mode 100644
index 0000000..13c3753
--- /dev/null
+++ 
b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/domain/Auth.java
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.openstack.keystone.v3.domain;
+
+import java.util.List;
+
+import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.json.SerializedNames;
+
+import com.google.auto.value.AutoValue;
+
+@AutoValue
+public abstract class Auth {
+   
+   public abstract Identity identity();
+
+   @SerializedNames({ "identity" })
+   public static Auth create(Identity identity) {
+      return new AutoValue_Auth(identity);
+   }
+
+   @AutoValue
+   public abstract static class Identity {
+      
+      public abstract List<String> methods();
+      public abstract TokenAuth token();
+      public abstract PasswordAuth password();
+
+      @SerializedNames({ "methods", "token", "password" })
+      public static Identity create(List<String> methods, TokenAuth token, 
PasswordAuth password) {
+         return new AutoValue_Auth_Identity(methods, token, password);
+      }
+
+      @AutoValue
+      public abstract static  class TokenAuth {
+         
+         public abstract String id();
+
+         @SerializedNames({ "id" })
+         public static TokenAuth create(String id) {
+            return new AutoValue_Auth_Identity_TokenAuth(id);
+         }
+      }
+
+      @AutoValue
+      public abstract static  class PasswordAuth {
+         
+         public abstract UserAuth user();
+
+         @SerializedNames({ "user" })
+         public static PasswordAuth create(UserAuth user) {
+            return new AutoValue_Auth_Identity_PasswordAuth(user);
+         }
+
+         @AutoValue
+         public abstract static  class UserAuth {
+            
+            public abstract String name();
+            public abstract DomainAuth domain();
+            public abstract String password();
+
+            @SerializedNames({ "name", "domain", "password" })
+            public static UserAuth create(String name, DomainAuth domain, 
String password) {
+               return new AutoValue_Auth_Identity_PasswordAuth_UserAuth(name, 
domain, password);
+            }
+
+            @AutoValue
+            public abstract static  class DomainAuth {
+               
+               @Nullable public abstract String name();
+
+               @SerializedNames({ "name" })
+               public static DomainAuth create(String name) {
+                  return new 
AutoValue_Auth_Identity_PasswordAuth_UserAuth_DomainAuth(name);
+               }
+            }
+         }
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/7def8169/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/domain/Catalog.java
----------------------------------------------------------------------
diff --git 
a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/domain/Catalog.java
 
b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/domain/Catalog.java
new file mode 100644
index 0000000..e5977f9
--- /dev/null
+++ 
b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/domain/Catalog.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.openstack.keystone.v3.domain;
+
+import java.util.List;
+
+import org.jclouds.json.SerializedNames;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableList;
+
+@AutoValue
+public abstract class Catalog {
+
+   public abstract String id();
+
+   public abstract String name();
+
+   public abstract String type();
+
+   public abstract List<Endpoint> endpoints();
+
+   @SerializedNames({"id", "name", "type", "endpoints"})
+   public static Catalog create(String id,
+                                String name,
+                                String type,
+                                List<Endpoint> endpoints
+   ) {
+      return new AutoValue_Catalog(id, name, type, endpoints == null ? 
ImmutableList.<Endpoint>of() : ImmutableList.copyOf(endpoints));
+   }
+
+   Catalog() {
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/7def8169/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/domain/Endpoint.java
----------------------------------------------------------------------
diff --git 
a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/domain/Endpoint.java
 
b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/domain/Endpoint.java
new file mode 100644
index 0000000..5de3d11
--- /dev/null
+++ 
b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/domain/Endpoint.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.openstack.keystone.v3.domain;
+
+import java.net.URI;
+import java.util.List;
+
+import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.json.SerializedNames;
+import org.jclouds.openstack.v2_0.domain.Link;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableList;
+
+@AutoValue
+public abstract class Endpoint {
+
+   public abstract String id();
+   public abstract String region();
+   public abstract String regionId();
+
+   @Nullable public abstract String serviceId();
+   public abstract URI url();
+   @Nullable public abstract Boolean enabled();
+   @Nullable public abstract List<Link> links();
+   public abstract String iface();
+
+   @SerializedNames({ "id", "region", "region_id", "service_id", "url", 
"enabled", "links", "interface" })
+   public static Endpoint create(String id, String region, String regionId, 
String serviceId, URI url, Boolean enabled,
+         List<Link> links, String iface) {
+      return new AutoValue_Endpoint(id, region, regionId, serviceId, url, 
enabled,
+            links == null ? ImmutableList.<Link> of() : 
ImmutableList.copyOf(links), iface);
+   }
+
+   Endpoint() {
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/7def8169/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/domain/Link.java
----------------------------------------------------------------------
diff --git 
a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/domain/Link.java
 
b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/domain/Link.java
new file mode 100644
index 0000000..0d86069
--- /dev/null
+++ 
b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/domain/Link.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.openstack.keystone.v3.domain;
+
+import org.jclouds.json.SerializedNames;
+
+import com.google.auto.value.AutoValue;
+
+@AutoValue
+public abstract class Link {
+
+   public abstract String self();
+
+   @SerializedNames({"self"})
+   public static Link create(String self) {
+      return new AutoValue_Link(self);
+   }
+
+   Link() {
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/7def8169/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/domain/Region.java
----------------------------------------------------------------------
diff --git 
a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/domain/Region.java
 
b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/domain/Region.java
new file mode 100644
index 0000000..22e5d1c
--- /dev/null
+++ 
b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/domain/Region.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.openstack.keystone.v3.domain;
+
+import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.json.SerializedNames;
+
+import com.google.auto.value.AutoValue;
+
+@AutoValue
+public abstract class Region {
+
+   public abstract String id();
+
+   public abstract String name();
+
+   public abstract Link link();
+
+   @Nullable public abstract String parentRegionId();
+
+   @SerializedNames({"id", "description", "links", "parent_region_id"})
+   public static Region create(String id,
+                               String name,
+                               Link link,
+                               String parentRegionId
+   ) {
+      return new AutoValue_Region(id, name, link, parentRegionId);
+   }
+
+   Region() {
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/7def8169/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/domain/Token.java
----------------------------------------------------------------------
diff --git 
a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/domain/Token.java
 
b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/domain/Token.java
new file mode 100644
index 0000000..4c43dd2
--- /dev/null
+++ 
b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/domain/Token.java
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.openstack.keystone.v3.domain;
+
+import java.util.Date;
+import java.util.List;
+
+import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.json.SerializedNames;
+import org.jclouds.openstack.keystone.auth.domain.AuthInfo;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableList;
+
+@AutoValue
+public abstract class Token implements AuthInfo {
+
+   @Nullable
+   public abstract String id();
+   public abstract List<String> methods();
+   @Nullable
+   public abstract Date expiresAt();
+   @Nullable
+   public abstract Object extras();
+   @Nullable
+   public abstract List<Catalog> catalog();
+   public abstract List<String> auditIds();
+   public abstract User user();
+   public abstract Date issuedAt();
+   
+   @Override
+   public String getAuthToken() {
+      return id();
+   }
+   @SerializedNames({"id", "methods", "expires_at", "extras", "catalog", 
"audit_ids", "user", "issued_at"})
+   private static Token create(
+           String id,
+           List<String> methods,
+           Date expiresAt,
+           Object extras,
+           List<Catalog> catalog,
+           List<String> auditIds,
+           User user,
+           Date issuedAt
+   ) {
+      return builder()
+              .id(id)
+              .methods(methods)
+              .expiresAt(expiresAt)
+              .extras(extras)
+              .catalog(catalog)
+              .auditIds(auditIds)
+              .user(user)
+              .issuedAt(issuedAt)
+              .build();
+   }
+
+   public abstract Builder toBuilder();
+
+   public static Builder builder() {
+      return new AutoValue_Token.Builder();
+   }
+
+   @AutoValue.Builder
+   public abstract static class Builder {
+
+      public abstract Builder id(String id);
+      public abstract Builder methods(List<String> methods);
+      public abstract Builder expiresAt(Date expiresAt);
+      public abstract Builder extras(Object extras);
+      public abstract Builder catalog(List<Catalog> catalog);
+      public abstract Builder auditIds(List<String> auditIds);
+      public abstract Builder user(User user);
+      public abstract Builder issuedAt(Date issuedAt);
+
+      abstract List<Catalog> catalog();
+      abstract List<String> methods();
+      abstract List<String> auditIds();
+
+      abstract Token autoBuild();
+
+      public Token build() {
+         return catalog(catalog() != null ? ImmutableList.copyOf(catalog()) : 
ImmutableList.<Catalog>of())
+                 .methods(methods() != null ? ImmutableList.copyOf(methods()) 
: ImmutableList.<String>of())
+                 .auditIds(auditIds() != null ? 
ImmutableList.copyOf(auditIds()) : ImmutableList.<String>of())
+                 .autoBuild();
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/7def8169/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/domain/User.java
----------------------------------------------------------------------
diff --git 
a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/domain/User.java
 
b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/domain/User.java
new file mode 100644
index 0000000..33702a9
--- /dev/null
+++ 
b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/domain/User.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.openstack.keystone.v3.domain;
+
+import java.util.Date;
+
+import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.json.SerializedNames;
+
+import com.google.auto.value.AutoValue;
+
+@AutoValue
+public abstract class User {
+
+   @AutoValue
+   public abstract static class Domain {
+
+      public abstract String id();
+      public abstract String name();
+
+      @SerializedNames({ "id", "name" })
+      public static Domain create(String id, String name) {
+         return new AutoValue_User_Domain(id, name);
+      }
+   }
+
+   public abstract String id();
+   public abstract String name();
+   @Nullable public abstract Date passwordExpiresAt();
+   public abstract Domain domain();
+
+   @SerializedNames({"id", "name", "password_expires_at", "domain"})
+   public static User create(String id,
+                               String name,
+                               Date passwordExpiresAt,
+                               Domain domain
+   ) {
+      return new AutoValue_User(id, name, passwordExpiresAt, domain);
+   }
+
+   User() {
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/7def8169/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/features/TokenApi.java
----------------------------------------------------------------------
diff --git 
a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/features/TokenApi.java
 
b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/features/TokenApi.java
new file mode 100644
index 0000000..bba190a
--- /dev/null
+++ 
b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/features/TokenApi.java
@@ -0,0 +1,132 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.openstack.keystone.v3.features;
+
+import java.util.List;
+import java.util.Set;
+
+import javax.inject.Named;
+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.core.MediaType;
+
+import org.jclouds.Fallbacks.EmptyListOnNotFoundOr404;
+import org.jclouds.Fallbacks.EmptySetOnNotFoundOr404;
+import org.jclouds.Fallbacks.FalseOnNotFoundOr404;
+import org.jclouds.Fallbacks.NullOnNotFoundOr404;
+import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.openstack.keystone.auth.filters.AuthenticateRequest;
+import org.jclouds.openstack.keystone.v2_0.domain.Endpoint;
+import org.jclouds.openstack.keystone.v2_0.domain.User;
+import org.jclouds.openstack.keystone.v3.domain.Region;
+import org.jclouds.openstack.keystone.v3.domain.Token;
+import org.jclouds.openstack.v2_0.services.Identity;
+import org.jclouds.rest.annotations.Fallback;
+import org.jclouds.rest.annotations.RequestFilters;
+import org.jclouds.rest.annotations.SelectJson;
+
+/**
+ * Provides access to the Keystone Admin API.
+ */
+@Consumes(MediaType.APPLICATION_JSON)
+@RequestFilters(AuthenticateRequest.class)
[email protected](Identity.class)
+public interface TokenApi {
+
+   /**
+    * Validate a token and, if it is valid, return access information 
regarding the tenant (though not the service catalog)/
+    *
+    * @return the requested information
+    */
+   @Named("token:get")
+   @GET
+   @SelectJson("token")
+   @Path("/auth/tokens/{token}")
+   @Fallback(NullOnNotFoundOr404.class)
+   @Nullable
+   Token get(@PathParam("token") 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
+    */
+   @Named("token:getuser")
+   @GET
+   @SelectJson("user")
+   @Path("/auth/tokens/{token}")
+   @Fallback(NullOnNotFoundOr404.class)
+   @Nullable
+   User getUserOfToken(@PathParam("token") 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
+    */
+   @Named("token:valid")
+   @HEAD
+   @Path("/auth/tokens/{token}")
+   @Fallback(FalseOnNotFoundOr404.class)
+   boolean isValid(@PathParam("token") String token);
+
+   /**
+    * List all endpoints for a token
+    * <p/>
+    * NOTE: currently not working in openstack ( 
https://bugs.launchpad.net/keystone/+bug/988672 )
+    *
+    * @return the set of endpoints
+    */
+   @Named("token:listEndpoints")
+   @GET
+   @SelectJson("endpoints")
+   @Path("/{token}/endpoints")
+   @Fallback(EmptySetOnNotFoundOr404.class)
+   Set<Endpoint> listEndpointsForToken(@PathParam("token") String token);
+
+
+   /**
+    * List all endpoints for a token
+    * <p/>
+    * NOTE: currently not working in openstack ( 
https://bugs.launchpad.net/keystone/+bug/988672 )
+    *
+    * @return the list of endpoints
+    */
+   @Named("token:endpoints")
+   @GET
+   @SelectJson("endpoints")
+   @Path("/endpoints")
+   @Fallback(EmptyListOnNotFoundOr404.class)
+   List<Endpoint> endpoints();
+
+   /**
+    * List all regions for a token
+    * <p/>
+    *
+    * @return the list of regions
+    */
+   @Named("token:regions")
+   @GET
+   @SelectJson("regions")
+   @Path("/regions")
+   @Fallback(EmptyListOnNotFoundOr404.class)
+   List<Region> regions();
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/7def8169/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/parsers/ParseToken.java
----------------------------------------------------------------------
diff --git 
a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/parsers/ParseToken.java
 
b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/parsers/ParseToken.java
new file mode 100644
index 0000000..1fd633f
--- /dev/null
+++ 
b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/parsers/ParseToken.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.openstack.keystone.v3.parsers;
+
+import org.jclouds.http.HttpResponse;
+import org.jclouds.http.functions.ParseFirstJsonValueNamed;
+import org.jclouds.json.internal.GsonWrapper;
+import org.jclouds.openstack.keystone.v3.domain.Token;
+
+import com.google.common.collect.Iterables;
+import com.google.inject.Inject;
+import com.google.inject.TypeLiteral;
+
+public class ParseToken extends ParseFirstJsonValueNamed<Token> {
+
+   @Inject
+   ParseToken(GsonWrapper json, TypeLiteral<Token> type, String... 
nameChoices) {
+      super(json, type, nameChoices);
+   }
+
+   @Override
+   public Token apply(HttpResponse from) {
+      Token token = super.apply(from);
+      String xSubjectToken = 
Iterables.getOnlyElement(from.getHeaders().get("X-Subject-Token"));
+      return token.toBuilder().id(xSubjectToken).build();
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/7def8169/apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/config/InternalUrlModule.java
----------------------------------------------------------------------
diff --git 
a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/config/InternalUrlModule.java
 
b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/config/InternalUrlModule.java
deleted file mode 100644
index 71b36cb..0000000
--- 
a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/config/InternalUrlModule.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.jclouds.openstack.v2_0.config;
-
-import org.jclouds.openstack.keystone.v2_0.functions.EndpointToSupplierURI;
-import org.jclouds.openstack.keystone.v2_0.functions.InternalURL;
-
-import com.google.inject.AbstractModule;
-import com.google.inject.Singleton;
-
-/**
- * Guice module to configure JClouds in order to use the internal urls to
- * communicate with the services.
- */
-@Singleton
-public class InternalUrlModule extends AbstractModule {
-   @Override
-   protected void configure() {
-      bind(EndpointToSupplierURI.class).to(InternalURL.class);
-   }
-}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/7def8169/apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/features/ExtensionApi.java
----------------------------------------------------------------------
diff --git 
a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/features/ExtensionApi.java
 
b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/features/ExtensionApi.java
index 633b327..466bcc9 100644
--- 
a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/features/ExtensionApi.java
+++ 
b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/features/ExtensionApi.java
@@ -28,7 +28,7 @@ import javax.ws.rs.core.MediaType;
 import org.jclouds.Fallbacks.EmptySetOnNotFoundOr404;
 import org.jclouds.Fallbacks.NullOnNotFoundOr404;
 import org.jclouds.javax.annotation.Nullable;
-import org.jclouds.openstack.keystone.v2_0.filters.AuthenticateRequest;
+import org.jclouds.openstack.keystone.auth.filters.AuthenticateRequest;
 import org.jclouds.openstack.v2_0.domain.Extension;
 import org.jclouds.rest.annotations.Fallback;
 import org.jclouds.rest.annotations.RequestFilters;

http://git-wip-us.apache.org/repos/asf/jclouds/blob/7def8169/apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/reference/AuthHeaders.java
----------------------------------------------------------------------
diff --git 
a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/reference/AuthHeaders.java
 
b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/reference/AuthHeaders.java
deleted file mode 100644
index 88ea817..0000000
--- 
a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/reference/AuthHeaders.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.jclouds.openstack.v2_0.reference;
-
-public final class AuthHeaders {
-
-   public static final String AUTH_USER = "X-Auth-User";
-   public static final String AUTH_KEY = "X-Auth-Key";
-   public static final String AUTH_TOKEN = "X-Auth-Token";
-
-   private AuthHeaders() {
-      throw new AssertionError("intentionally unimplemented");
-   }
-}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/7def8169/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/auth/config/CredentialTypesTest.java
----------------------------------------------------------------------
diff --git 
a/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/auth/config/CredentialTypesTest.java
 
b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/auth/config/CredentialTypesTest.java
new file mode 100644
index 0000000..93af93c
--- /dev/null
+++ 
b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/auth/config/CredentialTypesTest.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.openstack.keystone.auth.config;
+
+import static org.testng.Assert.assertEquals;
+
+import org.jclouds.openstack.keystone.auth.domain.PasswordCredentials;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableSet;
+
+@Test(groups = "unit", testName = "CredentialTypesTest")
+public class CredentialTypesTest {
+
+   public void testCredentialTypeOfWhenValid() {
+      assertEquals(
+            
CredentialTypes.credentialTypeOf(PasswordCredentials.builder().username("username").password("password")
+                  .build()), CredentialTypes.PASSWORD_CREDENTIALS);
+   }
+
+   @Test(expectedExceptions = IllegalArgumentException.class)
+   public void testCredentialTypeOfWithoutAnnotation() {
+      CredentialTypes.credentialTypeOf("");
+   }
+
+   public void testIndexByCredentialTypeWhenValid() {
+      assertEquals(
+            CredentialTypes.indexByCredentialType(
+                  
ImmutableSet.of(PasswordCredentials.builder().username("username").password("password").build()))
+                  .keySet(), 
ImmutableSet.of(CredentialTypes.PASSWORD_CREDENTIALS));
+   }
+
+   @Test(expectedExceptions = IllegalArgumentException.class)
+   public void testIndexByCredentialTypeWithoutAnnotation() {
+      CredentialTypes.indexByCredentialType(ImmutableSet.of(""));
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/7def8169/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/auth/config/ProviderModuleExpectTest.java
----------------------------------------------------------------------
diff --git 
a/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/auth/config/ProviderModuleExpectTest.java
 
b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/auth/config/ProviderModuleExpectTest.java
new file mode 100644
index 0000000..a11c7de
--- /dev/null
+++ 
b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/auth/config/ProviderModuleExpectTest.java
@@ -0,0 +1,180 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.openstack.keystone.auth.config;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import static 
org.jclouds.openstack.keystone.config.KeystoneProperties.CREDENTIAL_TYPE;
+import static 
org.jclouds.openstack.keystone.config.KeystoneProperties.KEYSTONE_VERSION;
+import static 
org.jclouds.openstack.keystone.config.KeystoneProperties.SERVICE_TYPE;
+import static org.jclouds.util.Suppliers2.getLastValueInMap;
+import static org.testng.Assert.assertTrue;
+
+import java.io.Closeable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+import java.net.URI;
+import java.util.Properties;
+
+import javax.inject.Qualifier;
+import javax.inject.Singleton;
+import javax.ws.rs.HEAD;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+
+import org.jclouds.Fallbacks.FalseOnNotFoundOr404;
+import org.jclouds.apis.ApiMetadata;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.json.config.GsonModule.DateAdapter;
+import org.jclouds.json.config.GsonModule.Iso8601DateAdapter;
+import org.jclouds.location.suppliers.RegionIdToURISupplier;
+import org.jclouds.openstack.keystone.catalog.config.ServiceCatalogModule;
+import 
org.jclouds.openstack.keystone.catalog.config.ServiceCatalogModule.ProviderModule;
+import 
org.jclouds.openstack.keystone.catalog.config.ServiceCatalogModule.RegionModule;
+import org.jclouds.openstack.keystone.v2_0.KeystoneApi;
+import 
org.jclouds.openstack.keystone.v2_0.internal.BaseKeystoneRestApiExpectTest;
+import org.jclouds.rest.ConfiguresHttpApi;
+import org.jclouds.rest.annotations.ApiVersion;
+import org.jclouds.rest.annotations.Fallback;
+import org.jclouds.rest.config.HttpApiModule;
+import org.jclouds.rest.internal.BaseHttpApiMetadata;
+import org.jclouds.rest.internal.BaseRestApiExpectTest;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableSet;
+import com.google.inject.Module;
+import com.google.inject.Provides;
+
+/**
+ * Tests configuration via {@link ProviderModule}
+ */
+@Test(groups = "unit", testName = "ProviderModuleExpectTest")
+public class ProviderModuleExpectTest extends 
BaseRestApiExpectTest<ProviderModuleExpectTest.DNSApi> {
+
+   @Retention(RUNTIME)
+   @Target(METHOD)
+   @Qualifier
+   static @interface DNS {
+   }
+
+   @ConfiguresHttpApi
+   public static class DNSHttpApiModule extends HttpApiModule<DNSApi> {
+
+      @Override
+      public void configure() {
+         bind(DateAdapter.class).to(Iso8601DateAdapter.class);
+         super.configure();
+      }
+
+      @Provides
+      @Singleton
+      @DNS
+      protected final Supplier<URI> 
provideCDNUrl(RegionIdToURISupplier.Factory factory, @ApiVersion String 
apiVersion) {
+         return getLastValueInMap(factory.createForApiTypeAndVersion("dns", 
apiVersion));
+      }
+   }
+
+   @org.jclouds.rest.annotations.Endpoint(DNS.class)
+   interface DNSApi extends Closeable {
+      @HEAD
+      @Path("/zones/{zoneName}")
+      @Fallback(FalseOnNotFoundOr404.class)
+      boolean 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 BaseHttpApiMetadata<DNSApi> {
+
+      @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 = BaseHttpApiMetadata.defaultProperties();
+         properties.setProperty(SERVICE_TYPE, "dns");
+         properties.setProperty(KEYSTONE_VERSION, "2");
+         properties.setProperty(CREDENTIAL_TYPE, 
CredentialTypes.PASSWORD_CREDENTIALS);
+         return properties;
+      }
+
+      public static class Builder extends BaseHttpApiMetadata.Builder<DNSApi, 
Builder> {
+
+         protected Builder() {
+            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.<Class<? extends Module>>builder()
+                                        .add(AuthenticationModule.class)
+                                        .add(ServiceCatalogModule.class)
+                                        .add(RegionModule.class)
+                                        .add(DNSHttpApiModule.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<KeystoneApi> {
+      public HttpRequest getKeystoneAuthWithUsernameAndPassword() {
+         return keystoneAuthWithUsernameAndPassword;
+      }
+      
+      public HttpResponse getResponseWithKeystoneAccess() {
+         return responseWithKeystoneAccess;
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/7def8169/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/auth/handlers/RetryOnRenewTest.java
----------------------------------------------------------------------
diff --git 
a/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/auth/handlers/RetryOnRenewTest.java
 
b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/auth/handlers/RetryOnRenewTest.java
new file mode 100644
index 0000000..01611ff
--- /dev/null
+++ 
b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/auth/handlers/RetryOnRenewTest.java
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.openstack.keystone.auth.handlers;
+
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.expectLastCall;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+import org.jclouds.domain.Credentials;
+import org.jclouds.http.HttpCommand;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.http.handlers.BackoffLimitedRetryHandler;
+import org.jclouds.io.Payloads;
+import org.jclouds.openstack.keystone.auth.domain.AuthInfo;
+import org.testng.annotations.Test;
+
+import com.google.common.cache.LoadingCache;
+
+/**
+ * Tests behavior of {@code RetryOnRenew} handler
+ */
+@Test(groups = "unit", testName = "RetryOnRenewTest")
+public class RetryOnRenewTest {
+   @Test
+   public void test401ShouldRetry() {
+      HttpCommand command = createMock(HttpCommand.class);
+      HttpRequest request = createMock(HttpRequest.class);
+      HttpResponse response = createMock(HttpResponse.class);
+      @SuppressWarnings("unchecked")
+      LoadingCache<Credentials, AuthInfo> cache = 
createMock(LoadingCache.class);
+      BackoffLimitedRetryHandler backoffHandler = 
createMock(BackoffLimitedRetryHandler.class);
+
+      expect(command.getCurrentRequest()).andReturn(request);
+
+      cache.invalidateAll();
+      expectLastCall();
+
+      
expect(response.getPayload()).andReturn(Payloads.newStringPayload("")).anyTimes();
+      expect(response.getStatusCode()).andReturn(401).atLeastOnce();
+
+      replay(command);
+      replay(response);
+      replay(cache);
+      replay(backoffHandler);
+
+      RetryOnRenew retry = new RetryOnRenew(cache, backoffHandler);
+
+      assertTrue(retry.shouldRetryRequest(command, response));
+
+      verify(command);
+      verify(response);
+      verify(cache);
+   }
+
+   @Test
+   public void test401ShouldRetry4Times() {
+      HttpCommand command = createMock(HttpCommand.class);
+      HttpRequest request = createMock(HttpRequest.class);
+      HttpResponse response = createMock(HttpResponse.class);
+
+      @SuppressWarnings("unchecked")
+      LoadingCache<Credentials, AuthInfo> cache = 
createMock(LoadingCache.class);
+      BackoffLimitedRetryHandler backoffHandler = 
createMock(BackoffLimitedRetryHandler.class);
+
+      expect(command.getCurrentRequest()).andReturn(request).anyTimes();
+      expect(request.getHeaders()).andStubReturn(null);
+
+      cache.invalidateAll();
+      expectLastCall().anyTimes();
+
+      
expect(response.getPayload()).andReturn(Payloads.newStringPayload("")).anyTimes();
+      expect(response.getStatusCode()).andReturn(401).anyTimes();
+
+      replay(command, request, response, cache);
+
+      RetryOnRenew retry = new RetryOnRenew(cache, backoffHandler);
+
+      for (int i = 0; i < RetryOnRenew.NUM_RETRIES - 1; ++i) {
+         assertTrue(retry.shouldRetryRequest(command, response), "Expected 
retry to succeed");
+      }
+
+      assertFalse(retry.shouldRetryRequest(command, response), "Expected retry 
to fail on attempt " + RetryOnRenew.NUM_RETRIES);
+
+      verify(command, response, cache);
+   }
+
+   @Test
+   public void test408ShouldRetry() {
+      HttpCommand command = createMock(HttpCommand.class);
+      HttpResponse response = createMock(HttpResponse.class);
+      @SuppressWarnings("unchecked")
+      LoadingCache<Credentials, AuthInfo> cache = 
createMock(LoadingCache.class);
+      BackoffLimitedRetryHandler backoffHandler = 
createMock(BackoffLimitedRetryHandler.class);
+
+      expect(backoffHandler.shouldRetryRequest(command, 
response)).andReturn(true).once();
+      expect(response.getStatusCode()).andReturn(408).once();
+
+      replay(command);
+      replay(response);
+      replay(cache);
+      replay(backoffHandler);
+
+      RetryOnRenew retry = new RetryOnRenew(cache, backoffHandler);
+
+      assertTrue(retry.shouldRetryRequest(command, response));
+
+      verify(command);
+      verify(response);
+      verify(cache);
+      verify(backoffHandler);
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/7def8169/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/auth/suppliers/LocationIdToURIFromAccessForTypeAndVersionTest.java
----------------------------------------------------------------------
diff --git 
a/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/auth/suppliers/LocationIdToURIFromAccessForTypeAndVersionTest.java
 
b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/auth/suppliers/LocationIdToURIFromAccessForTypeAndVersionTest.java
new file mode 100644
index 0000000..91146b0
--- /dev/null
+++ 
b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/auth/suppliers/LocationIdToURIFromAccessForTypeAndVersionTest.java
@@ -0,0 +1,139 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.openstack.keystone.auth.suppliers;
+
+import static org.testng.Assert.assertEquals;
+
+import java.net.URI;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+
+import javax.inject.Singleton;
+
+import org.jclouds.location.Provider;
+import org.jclouds.openstack.keystone.auth.domain.AuthInfo;
+import org.jclouds.openstack.keystone.catalog.ServiceEndpoint;
+import 
org.jclouds.openstack.keystone.catalog.suppliers.LocationIdToURIFromAccessForTypeAndVersion;
+import org.jclouds.openstack.keystone.v2_0.catalog.V2ServiceCatalog;
+import org.jclouds.openstack.keystone.v2_0.parse.ParseAccessTest;
+import org.jclouds.openstack.keystone.v2_0.parse.ParseRackspaceAccessTest;
+import org.testng.annotations.Test;
+
+import com.google.common.base.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.Scopes;
+import com.google.inject.TypeLiteral;
+import com.google.inject.assistedinject.FactoryModuleBuilder;
+
+@Test(groups = "unit", testName = 
"LocationIdToURIFromAccessForTypeAndVersionTest")
+public class LocationIdToURIFromAccessForTypeAndVersionTest {
+   private final LocationIdToURIFromAccessForTypeAndVersion.Factory factory = 
Guice.createInjector(
+         new AbstractModule() {
+
+            @Override
+            protected void configure() {
+               
bindConstant().annotatedWith(Provider.class).to("openstack-keystone");
+               bind(new TypeLiteral<Supplier<URI>>() {
+               
}).annotatedWith(Provider.class).toInstance(Suppliers.ofInstance(URI.create("https://identity";)));
+               install(new 
FactoryModuleBuilder().implement(LocationIdToURIFromAccessForTypeAndVersion.class,
+                     LocationIdToURIFromAccessForTypeAndVersion.class).build(
+                     
LocationIdToURIFromAccessForTypeAndVersion.Factory.class));
+               // We test against a 2.0 service catalog but it is OK for the 
purpose of this test
+               bind(new TypeLiteral<Supplier<List<ServiceEndpoint>>>() {
+               }).to(V2ServiceCatalog.class).in(Scopes.SINGLETON);
+            }
+
+            @Provides
+            @Singleton
+            public Supplier<AuthInfo> provide() {
+               return Suppliers.<AuthInfo> ofInstance(new 
ParseAccessTest().expected());
+            }
+
+         
}).getInstance(LocationIdToURIFromAccessForTypeAndVersion.Factory.class);
+
+   public void testRegionUnmatchesOkWhenNoVersionIdSet() {
+      assertEquals(
+            Maps.transformValues(factory.createForApiTypeAndVersion("compute", 
"2").get(),
+                  Suppliers.<URI> supplierFunction()),
+            ImmutableMap.of("az-1.region-a.geo-1",
+                  
URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v2/3456";), 
"az-2.region-a.geo-1",
+                  
URI.create("https://az-2.region-a.geo-1.compute.hpcloudsvc.com/v2/3456";), 
"az-3.region-a.geo-1",
+                  
URI.create("https://az-3.region-a.geo-1.compute.hpcloudsvc.com/v2/3456";)));
+   }
+
+   public void testRegionMatches() {
+      assertEquals(
+            Maps.transformValues(factory.createForApiTypeAndVersion("compute", 
"2").get(),
+                  Suppliers.<URI> supplierFunction()),
+            ImmutableMap.of("az-1.region-a.geo-1",
+                  
URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v2/3456";), 
"az-2.region-a.geo-1",
+                  
URI.create("https://az-2.region-a.geo-1.compute.hpcloudsvc.com/v2/3456";), 
"az-3.region-a.geo-1",
+                  
URI.create("https://az-3.region-a.geo-1.compute.hpcloudsvc.com/v2/3456";)));
+   }
+
+   private final LocationIdToURIFromAccessForTypeAndVersion.Factory raxFactory 
= Guice.createInjector(
+         new AbstractModule() {
+
+            @Override
+            protected void configure() {
+               bindConstant().annotatedWith(Provider.class).to("rackspace");
+               bind(new TypeLiteral<Supplier<URI>>() {
+               
}).annotatedWith(Provider.class).toInstance(Suppliers.ofInstance(URI.create("https://identity";)));
+               install(new 
FactoryModuleBuilder().implement(LocationIdToURIFromAccessForTypeAndVersion.class,
+                     LocationIdToURIFromAccessForTypeAndVersion.class).build(
+                     
LocationIdToURIFromAccessForTypeAndVersion.Factory.class));
+               // We test against a 2.0 service catalog but it is OK for the 
purpose of this test
+               bind(new TypeLiteral<Supplier<List<ServiceEndpoint>>>() {
+               }).to(V2ServiceCatalog.class).in(Scopes.SINGLETON);
+            }
+
+            @Provides
+            @Singleton
+            public Supplier<AuthInfo> provide() {
+               return Suppliers.<AuthInfo> ofInstance(new 
ParseRackspaceAccessTest().expected());
+            }
+         
}).getInstance(LocationIdToURIFromAccessForTypeAndVersion.Factory.class);
+
+   @Test(expectedExceptions = NoSuchElementException.class)
+   public void testWhenNotInList() {
+      assertEquals(
+            Maps.transformValues(raxFactory.createForApiTypeAndVersion("goo", 
"1.0").get(),
+                  Suppliers.<URI> supplierFunction()),
+            ImmutableMap.of("rackspace", 
URI.create("https://servers.api.rackspacecloud.com/v1.0/40806637803162";)));
+   }
+
+   public void testProviderWhenNoRegions() {
+      Map<String, URI> withNoRegions = 
Maps.transformValues(raxFactory.createForApiTypeAndVersion("compute", "1.0")
+            .get(), Suppliers.<URI> supplierFunction());
+      assertEquals(withNoRegions,
+            ImmutableMap.of("rackspace", 
URI.create("https://servers.api.rackspacecloud.com/v1.0/40806637803162";)));
+   }
+
+   public void testOkWithNoVersions() {
+      
assertEquals(Maps.transformValues(raxFactory.createForApiTypeAndVersion("rax:database",
 null).get(),
+            Suppliers.<URI> supplierFunction()), ImmutableMap.of("DFW",
+            
URI.create("https://dfw.databases.api.rackspacecloud.com/v1.0/40806637803162";), 
"ORD",
+            
URI.create("https://ord.databases.api.rackspacecloud.com/v1.0/40806637803162";)));
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/7def8169/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/auth/suppliers/RegionIdToAdminURIFromAccessForTypeAndVersionTest.java
----------------------------------------------------------------------
diff --git 
a/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/auth/suppliers/RegionIdToAdminURIFromAccessForTypeAndVersionTest.java
 
b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/auth/suppliers/RegionIdToAdminURIFromAccessForTypeAndVersionTest.java
new file mode 100644
index 0000000..0ed35a4
--- /dev/null
+++ 
b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/auth/suppliers/RegionIdToAdminURIFromAccessForTypeAndVersionTest.java
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.openstack.keystone.auth.suppliers;
+
+import static org.testng.Assert.assertEquals;
+
+import java.net.URI;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+
+import javax.inject.Singleton;
+
+import org.jclouds.location.Provider;
+import org.jclouds.openstack.keystone.auth.domain.AuthInfo;
+import org.jclouds.openstack.keystone.catalog.ServiceEndpoint;
+import 
org.jclouds.openstack.keystone.catalog.suppliers.RegionIdToAdminURIFromAccessForTypeAndVersion;
+import 
org.jclouds.openstack.keystone.catalog.suppliers.RegionIdToAdminURISupplier;
+import org.jclouds.openstack.keystone.v2_0.catalog.V2ServiceCatalog;
+import org.jclouds.openstack.keystone.v2_0.parse.ParseAccessTest;
+import org.jclouds.openstack.keystone.v2_0.parse.ParseRackspaceAccessTest;
+import org.testng.annotations.Test;
+
+import com.google.common.base.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.Scopes;
+import com.google.inject.TypeLiteral;
+import com.google.inject.assistedinject.FactoryModuleBuilder;
+
+@Test(groups = "unit", testName = 
"RegionIdToAdminURIFromAccessForTypeAndVersionTest")
+public class RegionIdToAdminURIFromAccessForTypeAndVersionTest {
+   private final RegionIdToAdminURISupplier.Factory factory = 
Guice.createInjector(new AbstractModule() {
+
+      @Override
+      protected void configure() {
+         bindConstant().annotatedWith(Provider.class).to("openstack-keystone");
+         bind(new TypeLiteral<Supplier<URI>>() {
+         
}).annotatedWith(Provider.class).toInstance(Suppliers.ofInstance(URI.create("https://identity";)));
+         install(new 
FactoryModuleBuilder().implement(RegionIdToAdminURISupplier.class,
+                  
RegionIdToAdminURIFromAccessForTypeAndVersion.class).build(RegionIdToAdminURISupplier.Factory.class));
+         // We test against a 2.0 service catalog but it is OK for the purpose 
of this test
+         bind(new TypeLiteral<Supplier<List<ServiceEndpoint>>>() {
+         }).to(V2ServiceCatalog.class).in(Scopes.SINGLETON);
+      }
+
+      @Provides
+      @Singleton
+      public Supplier<AuthInfo> provide() {
+         return Suppliers.<AuthInfo> ofInstance(new 
ParseAccessTest().expected());
+      }
+   }).getInstance(RegionIdToAdminURISupplier.Factory.class);
+
+   public void testRegionMatches() {
+      
assertEquals(Maps.transformValues(factory.createForApiTypeAndVersion("identity",
 "2.0").get(), Suppliers
+               .<URI> supplierFunction()), ImmutableMap.of("region-a.geo-1", 
URI.create("https://csnode.jclouds.org:35357/v2.0/";)));
+      Map<String, URI> map = Maps.newLinkedHashMap();
+      map.put("region-a.geo-1", null);
+      map.put("region-b.geo-1", null);
+      map.put("region-c.geo-1", null);
+      
assertEquals(Maps.transformValues(factory.createForApiTypeAndVersion("compute", 
"2").get(), Suppliers
+               .<URI> supplierFunction()), map);
+   }
+   
+   private final RegionIdToAdminURISupplier.Factory raxFactory = 
Guice.createInjector(new AbstractModule() {
+
+      @Override
+      protected void configure() {
+         bindConstant().annotatedWith(Provider.class).to("rackspace");
+         bind(new TypeLiteral<Supplier<URI>>() {
+         
}).annotatedWith(Provider.class).toInstance(Suppliers.ofInstance(URI.create("https://identity";)));
+         install(new 
FactoryModuleBuilder().implement(RegionIdToAdminURISupplier.class,
+                  
RegionIdToAdminURIFromAccessForTypeAndVersion.class).build(RegionIdToAdminURISupplier.Factory.class));
+         // We test against a 2.0 service catalog but it is OK for the purpose 
of this test
+         bind(new TypeLiteral<Supplier<List<ServiceEndpoint>>>() {
+         }).to(V2ServiceCatalog.class).in(Scopes.SINGLETON);
+      }
+
+      @Provides
+      @Singleton
+      public Supplier<AuthInfo> provide() {
+         return Suppliers.<AuthInfo> ofInstance(new 
ParseRackspaceAccessTest().expected());
+      }
+   }).getInstance(RegionIdToAdminURISupplier.Factory.class);
+
+   @Test(expectedExceptions = NoSuchElementException.class)
+   public void testWhenNotInList() {
+      
assertEquals(Maps.transformValues(raxFactory.createForApiTypeAndVersion("goo", 
"1.0").get(), Suppliers
+               .<URI> supplierFunction()), ImmutableMap.of("rackspace", URI
+               
.create("https://servers.api.rackspacecloud.com/v1.0/40806637803162";)));
+   }
+   
+   public void testProviderWhenNoRegions() {
+      Map<String, URI> map = Maps.newLinkedHashMap();
+      map.put("rackspace", null);
+      
assertEquals(Maps.transformValues(raxFactory.createForApiTypeAndVersion("compute",
 "1.0").get(), Suppliers
+               .<URI> supplierFunction()), map);
+   }
+   
+   public void testOkWithNoVersions() {
+      Map<String, URI> map = Maps.newLinkedHashMap();
+      map.put("DFW", null);
+      map.put("ORD", null);
+      
assertEquals(Maps.transformValues(raxFactory.createForApiTypeAndVersion("rax:database",
 null).get(), Suppliers
+               .<URI> supplierFunction()), map);
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/7def8169/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/auth/suppliers/RegionIdToURIFromAccessForTypeAndVersionTest.java
----------------------------------------------------------------------
diff --git 
a/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/auth/suppliers/RegionIdToURIFromAccessForTypeAndVersionTest.java
 
b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/auth/suppliers/RegionIdToURIFromAccessForTypeAndVersionTest.java
new file mode 100644
index 0000000..cbdaf21
--- /dev/null
+++ 
b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/auth/suppliers/RegionIdToURIFromAccessForTypeAndVersionTest.java
@@ -0,0 +1,128 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.openstack.keystone.auth.suppliers;
+
+import static org.testng.Assert.assertEquals;
+
+import java.net.URI;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+import javax.inject.Singleton;
+
+import org.jclouds.location.Provider;
+import org.jclouds.location.suppliers.RegionIdToURISupplier;
+import org.jclouds.openstack.keystone.auth.domain.AuthInfo;
+import org.jclouds.openstack.keystone.catalog.ServiceEndpoint;
+import 
org.jclouds.openstack.keystone.catalog.suppliers.RegionIdToURIFromAccessForTypeAndVersion;
+import org.jclouds.openstack.keystone.v2_0.catalog.V2ServiceCatalog;
+import org.jclouds.openstack.keystone.v2_0.parse.ParseAccessTest;
+import org.jclouds.openstack.keystone.v2_0.parse.ParseRackspaceAccessTest;
+import org.testng.annotations.Test;
+
+import com.google.common.base.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.Scopes;
+import com.google.inject.TypeLiteral;
+import com.google.inject.assistedinject.FactoryModuleBuilder;
+
+@Test(groups = "unit", testName = 
"RegionIdToURIFromAccessForTypeAndVersionTest")
+public class RegionIdToURIFromAccessForTypeAndVersionTest {
+   private final RegionIdToURISupplier.Factory factory = 
Guice.createInjector(new AbstractModule() {
+
+      @Override
+      protected void configure() {
+         bindConstant().annotatedWith(Provider.class).to("openstack-keystone");
+         bind(new TypeLiteral<Supplier<URI>>() {
+         
}).annotatedWith(Provider.class).toInstance(Suppliers.ofInstance(URI.create("https://identity";)));
+         install(new 
FactoryModuleBuilder().implement(RegionIdToURISupplier.class,
+                  RegionIdToURIFromAccessForTypeAndVersion.class).build(
+                  RegionIdToURISupplier.Factory.class));
+         // We test against a 2.0 service catalog but it is OK for the purpose 
of this test
+         bind(new TypeLiteral<Supplier<List<ServiceEndpoint>>>() {
+         }).to(V2ServiceCatalog.class).in(Scopes.SINGLETON);
+      }
+
+      @Provides
+      @Singleton
+      public Supplier<AuthInfo> provide() {
+         return Suppliers.<AuthInfo> ofInstance(new 
ParseAccessTest().expected());
+      }
+   }).getInstance(RegionIdToURISupplier.Factory.class);
+
+   @Test(expectedExceptions = NoSuchElementException.class)
+   public void testRegionUnmatches() {
+      Maps.transformValues(factory.createForApiTypeAndVersion("compute", 
"1.0").get(),
+               Suppliers.<URI> supplierFunction());
+   }
+   
+   public void testRegionMatches() {
+      
assertEquals(Maps.transformValues(factory.createForApiTypeAndVersion("compute", 
"2").get(), Suppliers
+               .<URI> supplierFunction()), 
ImmutableMap.of("az-1.region-a.geo-1", URI
+               
.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v2/3456";), 
"az-2.region-a.geo-1", URI
+               
.create("https://az-2.region-a.geo-1.compute.hpcloudsvc.com/v2/3456";), 
"az-3.region-a.geo-1", URI
+               
.create("https://az-3.region-a.geo-1.compute.hpcloudsvc.com/v2/3456";)));
+   }
+
+   private final RegionIdToURISupplier.Factory raxFactory = 
Guice.createInjector(new AbstractModule() {
+
+      @Override
+      protected void configure() {
+         bindConstant().annotatedWith(Provider.class).to("rackspace");
+         bind(new TypeLiteral<Supplier<URI>>() {
+         
}).annotatedWith(Provider.class).toInstance(Suppliers.ofInstance(URI.create("https://identity";)));
+         install(new 
FactoryModuleBuilder().implement(RegionIdToURISupplier.class,
+                  RegionIdToURIFromAccessForTypeAndVersion.class).build(
+                  RegionIdToURISupplier.Factory.class));
+         // We test against a 2.0 service catalog but it is OK for the purpose 
of this test
+         bind(new TypeLiteral<Supplier<List<ServiceEndpoint>>>() {
+         }).to(V2ServiceCatalog.class).in(Scopes.SINGLETON);
+      }
+
+      @Provides
+      @Singleton
+      public Supplier<AuthInfo> provide() {
+         return Suppliers.<AuthInfo> ofInstance(new 
ParseRackspaceAccessTest().expected());
+      }
+   }).getInstance(RegionIdToURISupplier.Factory.class);
+
+   @Test(expectedExceptions = NoSuchElementException.class)
+   public void testWhenNotInList() {
+      
assertEquals(Maps.transformValues(raxFactory.createForApiTypeAndVersion("goo", 
"1.0").get(), Suppliers
+               .<URI> supplierFunction()), ImmutableMap.of("rackspace", URI
+               
.create("https://servers.api.rackspacecloud.com/v1.0/40806637803162";)));
+   }
+
+   public void testProviderWhenNoRegions() {
+      
assertEquals(Maps.transformValues(raxFactory.createForApiTypeAndVersion("compute",
 "1.0").get(), Suppliers
+               .<URI> supplierFunction()), ImmutableMap.of("rackspace", URI
+               
.create("https://servers.api.rackspacecloud.com/v1.0/40806637803162";)));
+   }
+
+   public void testOkWithNoVersions() {
+      
assertEquals(Maps.transformValues(raxFactory.createForApiTypeAndVersion("rax:database",
 null).get(), Suppliers
+               .<URI> supplierFunction()), ImmutableMap.of("DFW", URI
+               
.create("https://dfw.databases.api.rackspacecloud.com/v1.0/40806637803162";), 
"ORD", URI
+               
.create("https://ord.databases.api.rackspacecloud.com/v1.0/40806637803162";)));
+   }
+
+}

Reply via email to