Updated Branches: refs/heads/master 335f5943f -> 8db0218cf
JCLOUDS-126 - Support and tests for region selection in swift-keystone BlobStore Project: http://git-wip-us.apache.org/repos/asf/incubator-jclouds/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-jclouds/commit/8db0218c Tree: http://git-wip-us.apache.org/repos/asf/incubator-jclouds/tree/8db0218c Diff: http://git-wip-us.apache.org/repos/asf/incubator-jclouds/diff/8db0218c Branch: refs/heads/master Commit: 8db0218cf76e59d3a662339bf3fc89a87d70f1e5 Parents: 335f594 Author: JoshVote <[email protected]> Authored: Fri Jun 14 16:13:33 2013 +0800 Committer: Andrew Gaul <[email protected]> Committed: Wed Jul 3 13:44:46 2013 -0700 ---------------------------------------------------------------------- .../swift/SwiftKeystoneApiMetadata.java | 2 + .../swift/config/SwiftRestClientModule.java | 30 ++++- .../KeystoneStorageEndpointModuleTest.java | 131 +++++++++++++++++++ .../main/java/org/jclouds/util/Suppliers2.java | 15 +++ .../java/org/jclouds/util/Suppliers2Test.java | 9 ++ 5 files changed, 183 insertions(+), 4 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/8db0218c/apis/swift/src/main/java/org/jclouds/openstack/swift/SwiftKeystoneApiMetadata.java ---------------------------------------------------------------------- diff --git a/apis/swift/src/main/java/org/jclouds/openstack/swift/SwiftKeystoneApiMetadata.java b/apis/swift/src/main/java/org/jclouds/openstack/swift/SwiftKeystoneApiMetadata.java index b2ad934..77c43f4 100644 --- a/apis/swift/src/main/java/org/jclouds/openstack/swift/SwiftKeystoneApiMetadata.java +++ b/apis/swift/src/main/java/org/jclouds/openstack/swift/SwiftKeystoneApiMetadata.java @@ -17,6 +17,7 @@ package org.jclouds.openstack.swift; import static org.jclouds.location.reference.LocationConstants.PROPERTY_REGIONS; +import static org.jclouds.location.reference.LocationConstants.PROPERTY_REGION; import static org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties.CREDENTIAL_TYPE; import static org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties.SERVICE_TYPE; @@ -69,6 +70,7 @@ public class SwiftKeystoneApiMetadata extends SwiftApiMetadata { Properties properties = SwiftApiMetadata.defaultProperties(); properties.setProperty(SERVICE_TYPE, ServiceType.OBJECT_STORE); properties.setProperty(CREDENTIAL_TYPE, CredentialTypes.PASSWORD_CREDENTIALS); + properties.setProperty(PROPERTY_REGION, ""); properties.remove(PROPERTY_REGIONS); return properties; } http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/8db0218c/apis/swift/src/main/java/org/jclouds/openstack/swift/config/SwiftRestClientModule.java ---------------------------------------------------------------------- diff --git a/apis/swift/src/main/java/org/jclouds/openstack/swift/config/SwiftRestClientModule.java b/apis/swift/src/main/java/org/jclouds/openstack/swift/config/SwiftRestClientModule.java index 0ff2422..7b81edf 100644 --- a/apis/swift/src/main/java/org/jclouds/openstack/swift/config/SwiftRestClientModule.java +++ b/apis/swift/src/main/java/org/jclouds/openstack/swift/config/SwiftRestClientModule.java @@ -17,18 +17,23 @@ package org.jclouds.openstack.swift.config; import static org.jclouds.reflect.Reflection2.typeToken; import static org.jclouds.util.Suppliers2.getLastValueInMap; +import static org.jclouds.util.Suppliers2.getValueInMapOrNull; import java.net.URI; import java.util.Map; +import javax.inject.Inject; +import javax.inject.Named; import javax.inject.Singleton; import org.jclouds.http.HttpErrorHandler; import org.jclouds.http.annotation.ClientError; import org.jclouds.http.annotation.Redirection; import org.jclouds.http.annotation.ServerError; +import org.jclouds.javax.annotation.Nullable; import org.jclouds.json.config.GsonModule.DateAdapter; import org.jclouds.json.config.GsonModule.Iso8601DateAdapter; +import org.jclouds.location.reference.LocationConstants; import org.jclouds.location.suppliers.RegionIdToURISupplier; import org.jclouds.openstack.config.OpenStackAuthenticationModule; import org.jclouds.openstack.functions.URIFromAuthenticationResponseForService; @@ -45,8 +50,14 @@ import org.jclouds.rest.ConfiguresRestClient; import org.jclouds.rest.annotations.ApiVersion; import org.jclouds.rest.config.RestClientModule; +import com.google.common.base.Function; +import com.google.common.base.Functions; +import com.google.common.base.Predicates; +import com.google.common.base.Strings; 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.common.reflect.TypeToken; import com.google.inject.Provides; import com.google.inject.Scopes; @@ -80,14 +91,25 @@ public class SwiftRestClientModule<S extends CommonSwiftClient, A extends Common } public static class KeystoneStorageEndpointModule extends KeystoneAuthenticationModule { - @Provides @Singleton @Storage - protected Supplier<URI> provideStorageUrl(RegionIdToURISupplier.Factory factory, @ApiVersion String apiVersion) { - return getLastValueInMap(factory.createForApiTypeAndVersion(ServiceType.OBJECT_STORE, apiVersion)); - } + protected Supplier<URI> provideStorageUrl(RegionIdToURISupplier.Factory factory, + @ApiVersion String apiVersion, + @Named(LocationConstants.PROPERTY_REGION) String region) { + //Get the URI's keyed by their region name + Supplier<Map<String, Supplier<URI>>> endpointsSupplier = factory.createForApiTypeAndVersion(ServiceType.OBJECT_STORE, apiVersion); + + //Pick the matching region name (if any) otherwise just return an arbitrary URL if no region name is set + //NOTE: The region string should never be null (it can be empty) if this object was instantiated via guice + // as it pulls these named strings from a Properties object. + if (region.isEmpty()) { + return getLastValueInMap(endpointsSupplier); + } else { + return getValueInMapOrNull(endpointsSupplier, region); + } + } } @Override http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/8db0218c/apis/swift/src/test/java/org/jclouds/openstack/swift/config/KeystoneStorageEndpointModuleTest.java ---------------------------------------------------------------------- diff --git a/apis/swift/src/test/java/org/jclouds/openstack/swift/config/KeystoneStorageEndpointModuleTest.java b/apis/swift/src/test/java/org/jclouds/openstack/swift/config/KeystoneStorageEndpointModuleTest.java new file mode 100644 index 0000000..43d4e66 --- /dev/null +++ b/apis/swift/src/test/java/org/jclouds/openstack/swift/config/KeystoneStorageEndpointModuleTest.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.swift.config; + +import static org.easymock.EasyMock.createStrictMock; +import static org.easymock.EasyMock.replay; +import static org.easymock.EasyMock.expect; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertNull; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.HashMap; +import java.util.Map; + +import org.jclouds.location.suppliers.RegionIdToURISupplier; +import org.jclouds.openstack.services.ServiceType; +import org.jclouds.openstack.swift.config.SwiftRestClientModule.KeystoneStorageEndpointModule; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; + +@Test(groups = "unit") +public class KeystoneStorageEndpointModuleTest { + + private final String apiVersion = "9.8.7"; + private final RegionIdToURISupplier.Factory mockFactory = createStrictMock(RegionIdToURISupplier.Factory.class); + private final RegionIdToURISupplier mockSupplier = createStrictMock(RegionIdToURISupplier.class); + + /** + * Setup the expectations for our mock factory to return 3 region urls keyed + * by test region names + */ + @BeforeTest + public void setup() { + Map<String, Supplier<URI>> endpoints = new HashMap<String, Supplier<URI>>(); + + try { + endpoints.put("region1", Suppliers.ofInstance(new URI("http://region1.example.org/"))); + endpoints.put("region2", Suppliers.ofInstance(new URI("http://region2.example.org/"))); + endpoints.put("region3", Suppliers.ofInstance(new URI("http://region3.example.org/"))); + } catch (URISyntaxException ex) { + fail("static test Strings do not parse to URI: " + ex.getMessage()); + } + + expect(mockSupplier.get()) + .andReturn(endpoints) + .anyTimes(); + expect(mockFactory.createForApiTypeAndVersion(ServiceType.OBJECT_STORE,apiVersion)) + .andReturn(mockSupplier) + .anyTimes(); + + replay(mockSupplier); + replay(mockFactory); + } + + /** + * Test that specifying an empty region will return an arbitrary URL + */ + @Test + public void testEmptyRegion() { + final KeystoneStorageEndpointModule moduleToTest = new KeystoneStorageEndpointModule(); + + // Test with an empty Region - just ensure we get either a region 1,2 or 3 + // URI + Supplier<URI> resultingSupplier = moduleToTest.provideStorageUrl(mockFactory, apiVersion, ""); + assertNotNull(resultingSupplier); + URI resultingUri = resultingSupplier.get(); + assertNotNull(resultingUri); + + // Without a region our choice is arbitrary. We can't enforce an ordering + // on the map + // as that varies from JVM to JVM - easier to just assume its one of the + // possible values + assertTrue(resultingUri.toString().equals("http://region1.example.org/") + || resultingUri.toString().equals("http://region2.example.org/") + || resultingUri.toString().equals("http://region3.example.org/")); + } + + /** + * Test that specifying a region will return the correct URL + */ + @Test + public void testSpecificRegion() { + final KeystoneStorageEndpointModule moduleToTest = new KeystoneStorageEndpointModule(); + + // Iterate through our region names + for (int i = 1; i <= 3; i++) { + Supplier<URI> resultingSupplier = moduleToTest.provideStorageUrl(mockFactory, apiVersion, String.format("region%1$s", i)); + assertNotNull(resultingSupplier); + URI resultingUri = resultingSupplier.get(); + assertNotNull(resultingUri); + + assertEquals(resultingUri.toString(), + String.format("http://region%1$s.example.org/", i)); + } + } + + /** + * Test that specifying an undefined region will return null + */ + @Test + public void testUndefinedRegion() { + final KeystoneStorageEndpointModule moduleToTest = new KeystoneStorageEndpointModule(); + + Supplier<URI> resultingSupplier = moduleToTest.provideStorageUrl(mockFactory, apiVersion, "region-that-dne"); + assertNotNull(resultingSupplier); + URI resultingUri = resultingSupplier.get(); + assertNull(resultingUri); + } +} http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/8db0218c/core/src/main/java/org/jclouds/util/Suppliers2.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/jclouds/util/Suppliers2.java b/core/src/main/java/org/jclouds/util/Suppliers2.java index 94f1a75..6b89e80 100644 --- a/core/src/main/java/org/jclouds/util/Suppliers2.java +++ b/core/src/main/java/org/jclouds/util/Suppliers2.java @@ -47,6 +47,21 @@ public class Suppliers2 { }; } + public static <K, V> Supplier<V> getValueInMapOrNull(final Supplier<Map<K, Supplier<V>>> input, final K keyValue) { + return new Supplier<V>() { + @Override + public V get() { + Map<K, Supplier<V>> map = input.get(); + return map.containsKey(keyValue) ? map.get(keyValue).get() : null; + } + + @Override + public String toString() { + return String.format("getValueInMapOrNull('%1$s')", keyValue); + } + }; + } + public static <X> Function<X, Supplier<X>> ofInstanceFunction() { return new Function<X, Supplier<X>>() { http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/8db0218c/core/src/test/java/org/jclouds/util/Suppliers2Test.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/jclouds/util/Suppliers2Test.java b/core/src/test/java/org/jclouds/util/Suppliers2Test.java index 43effcf..579c2ee 100644 --- a/core/src/test/java/org/jclouds/util/Suppliers2Test.java +++ b/core/src/test/java/org/jclouds/util/Suppliers2Test.java @@ -38,6 +38,15 @@ public class Suppliers2Test { } @Test + public void testGetSpecificValueInMap() { + Supplier<Map<String, Supplier<String>>> testMap = Suppliers.<Map<String, Supplier<String>>> ofInstance( + ImmutableMap.of("foo", Suppliers.ofInstance("bar"))); + + assertEquals(Suppliers2.<String, String> getValueInMapOrNull(testMap, "foo").get(), "bar"); + assertEquals(Suppliers2.<String, String> getValueInMapOrNull(testMap, "baz").get(), null); + } + + @Test public void testOfInstanceFunction() { assertEquals(Suppliers2.ofInstanceFunction().apply("foo").get(), "foo"); }
