JCLOUDS-126 - Support and tests for region selection in swift-keystone BlobStore

This commit is contained in:
JoshVote 2013-06-14 16:13:33 +08:00 committed by Andrew Gaul
parent 335f5943f4
commit 8db0218cf7
5 changed files with 183 additions and 4 deletions

View File

@ -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;
}

View File

@ -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

View File

@ -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);
}
}

View File

@ -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>>() {

View File

@ -37,6 +37,15 @@ public class Suppliers2Test {
Suppliers.ofInstance("bar")))).get(), "bar");
}
@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");