> result = json.apply(from);
+ if(result.get("user") == null)
+ return null;
+ return result.get("user").get("password");
+ }
+}
diff --git a/apis/openstack-trove/src/main/java/org/jclouds/openstack/trove/v1/handlers/TroveErrorHandler.java b/apis/openstack-trove/src/main/java/org/jclouds/openstack/trove/v1/handlers/TroveErrorHandler.java
new file mode 100644
index 0000000000..887c5413eb
--- /dev/null
+++ b/apis/openstack-trove/src/main/java/org/jclouds/openstack/trove/v1/handlers/TroveErrorHandler.java
@@ -0,0 +1,69 @@
+/*
+ * 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.trove.v1.handlers;
+
+import static org.jclouds.http.HttpUtils.closeClientButKeepContentStream;
+
+import javax.inject.Singleton;
+
+import org.jclouds.http.HttpCommand;
+import org.jclouds.http.HttpErrorHandler;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.http.HttpResponseException;
+import org.jclouds.rest.AuthorizationException;
+import org.jclouds.rest.InsufficientResourcesException;
+import org.jclouds.rest.ResourceNotFoundException;
+
+/**
+ * This will parse and set an appropriate exception on the command object.
+ *
+ * @author Zack Shoylev
+ *
+ */
+@Singleton
+public class TroveErrorHandler implements HttpErrorHandler {
+
+ public void handleError(HttpCommand command, HttpResponse response) {
+ // it is important to always read fully and close streams
+ byte[] data = closeClientButKeepContentStream(response);
+ String message = data != null ? new String(data) : null;
+
+ Exception exception = message != null ? new HttpResponseException(command, response, message)
+ : new HttpResponseException(command, response);
+ message = message != null ? message : String.format("%s -> %s", command.getCurrentRequest().getRequestLine(),
+ response.getStatusLine());
+ switch (response.getStatusCode()) {
+ case 400:
+ if (message.contains("quota exceeded"))
+ exception = new InsufficientResourcesException(message, exception);
+ break;
+ case 401:
+ case 403:
+ exception = new AuthorizationException(message, exception);
+ break;
+ case 404:
+ if (!command.getCurrentRequest().getMethod().equals("DELETE")) {
+ exception = new ResourceNotFoundException(message, exception);
+ }
+ break;
+ case 413:
+ exception = new InsufficientResourcesException(message, exception);
+ break;
+ }
+ command.setException(exception);
+ }
+}
diff --git a/apis/openstack-trove/src/main/java/org/jclouds/openstack/trove/v1/internal/Volume.java b/apis/openstack-trove/src/main/java/org/jclouds/openstack/trove/v1/internal/Volume.java
new file mode 100644
index 0000000000..74c16cfb2a
--- /dev/null
+++ b/apis/openstack-trove/src/main/java/org/jclouds/openstack/trove/v1/internal/Volume.java
@@ -0,0 +1,32 @@
+/*
+ * 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.trove.v1.internal;
+
+public class Volume{
+ private final int size;
+
+ public Volume(int size){
+ this.size = size;
+ }
+
+ /**
+ * @return the size
+ */
+ public int getSize() {
+ return size;
+ }
+}
diff --git a/apis/openstack-trove/src/main/java/org/jclouds/openstack/trove/v1/predicates/InstancePredicates.java b/apis/openstack-trove/src/main/java/org/jclouds/openstack/trove/v1/predicates/InstancePredicates.java
new file mode 100644
index 0000000000..6149eff270
--- /dev/null
+++ b/apis/openstack-trove/src/main/java/org/jclouds/openstack/trove/v1/predicates/InstancePredicates.java
@@ -0,0 +1,144 @@
+/*
+ * 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.trove.v1.predicates;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.jclouds.util.Predicates2.retry;
+import org.jclouds.openstack.trove.v1.domain.Instance;
+import org.jclouds.openstack.trove.v1.domain.Instance.Status;
+import org.jclouds.openstack.trove.v1.features.InstanceApi;
+
+import com.google.common.base.Predicate;
+
+/**
+ * Tests to see if instance has reached status. This class is most useful when paired with a RetryablePredicate as
+ * in the code below. This class can be used to block execution until the Instance status has reached a desired state.
+ * This is useful when your Instance needs to be 100% ready before you can continue with execution.
+ *
+ *
+ * {@code
+ * Instance instance = instanceApi.create(100);
+ *
+ * RetryablePredicate awaitAvailable = RetryablePredicate.create(
+ * InstancePredicates.available(instanceApi), 600, 10, 10, TimeUnit.SECONDS);
+ *
+ * if (!awaitAvailable.apply(instance.getId())) {
+ * throw new TimeoutException("Timeout on instance: " + instance);
+ * }
+ * }
+ *
+ *
+ * You can also use the static convenience methods as follows.
+ *
+ *
+ * {@code
+ * Instance instance = instanceApi.create(100);
+ *
+ * if (!InstancePredicates.awaitAvailable(instanceApi).apply(instance.getId())) {
+ * throw new TimeoutException("Timeout on instance: " + instance);
+ * }
+ * }
+ *
+ *
+ * @author Zack Shoylev
+ */
+public class InstancePredicates {
+ /**
+ * Wait until an Instance is Available.
+ *
+ * @param instanceApi The InstanceApi in the zone where your Instance resides.
+ * @return RetryablePredicate That will check the status every 5 seconds for a maxiumum of 10 minutes.
+ */
+ public static Predicate awaitAvailable(InstanceApi instanceApi) {
+ StatusUpdatedPredicate statusPredicate = new StatusUpdatedPredicate(instanceApi, Instance.Status.ACTIVE);
+ return retry(statusPredicate, 600, 5, 5, SECONDS);
+ }
+
+ /**
+ * Wait until an Instance no longer exists.
+ *
+ * @param instanceApi The InstanceApi in the zone where your Instance resides.
+ * @return RetryablePredicate That will check whether the Instance exists.
+ * every 5 seconds for a maxiumum of 10 minutes.
+ */
+ public static Predicate awaitDeleted(InstanceApi instanceApi) {
+ DeletedPredicate deletedPredicate = new DeletedPredicate(instanceApi);
+ return retry(deletedPredicate, 600, 5, 5, SECONDS);
+ }
+
+ /**
+ * Wait until instance is in the status specified.
+ *
+ * @param instanceApi The InstanceApi in the zone where your Instance resides.
+ * @param status Wait until instance in in this status.
+ * @param maxWaitInSec Maximum time to wait.
+ * @param periodInSec Interval between retries.
+ * @return RetryablePredicate That will check whether the Instance exists.
+ */
+ public static Predicate awaitStatus(
+ InstanceApi instanceApi, Instance.Status status, long maxWaitInSec, long periodInSec) {
+ StatusUpdatedPredicate statusPredicate = new StatusUpdatedPredicate(instanceApi, status);
+ return retry(statusPredicate, maxWaitInSec, periodInSec, periodInSec, SECONDS);
+ }
+
+ private static class StatusUpdatedPredicate implements Predicate {
+ private InstanceApi instanceApi;
+ private Status status;
+
+ public StatusUpdatedPredicate(InstanceApi instanceApi, Instance.Status status) {
+ this.instanceApi = checkNotNull(instanceApi, "instanceApi must be defined");
+ this.status = checkNotNull(status, "status must be defined");
+ }
+
+ /**
+ * @return boolean Return true when the instance reaches status, false otherwise.
+ */
+ @Override
+ public boolean apply(Instance instance) {
+ checkNotNull(instance, "instance must be defined");
+
+ if (status.equals(instance.getStatus())) {
+ return true;
+ }
+ else {
+ Instance instanceUpdated = instanceApi.get(instance.getId());
+ checkNotNull(instanceUpdated, "Instance %s not found.", instance.getId());
+
+ return status.equals(instanceUpdated.getStatus());
+ }
+ }
+ }
+
+ private static class DeletedPredicate implements Predicate {
+ private InstanceApi instanceApi;
+
+ public DeletedPredicate(InstanceApi instanceApi) {
+ this.instanceApi = checkNotNull(instanceApi, "instanceApi must be defined");
+ }
+
+ /**
+ * @return boolean Return true when the snapshot is deleted, false otherwise.
+ */
+ @Override
+ public boolean apply(Instance instance) {
+ checkNotNull(instance, "instance must be defined");
+
+ return instanceApi.get(instance.getId()) == null;
+ }
+ }
+}
diff --git a/apis/openstack-trove/src/main/java/org/jclouds/openstack/trove/v1/utils/TroveUtils.java b/apis/openstack-trove/src/main/java/org/jclouds/openstack/trove/v1/utils/TroveUtils.java
new file mode 100644
index 0000000000..dbc17f2647
--- /dev/null
+++ b/apis/openstack-trove/src/main/java/org/jclouds/openstack/trove/v1/utils/TroveUtils.java
@@ -0,0 +1,107 @@
+/*
+ * 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.trove.v1.utils;
+
+import java.util.UUID;
+import java.util.concurrent.TimeUnit;
+
+import javax.annotation.Resource;
+
+import org.jclouds.openstack.trove.v1.TroveApi;
+import org.jclouds.openstack.trove.v1.domain.Instance;
+import org.jclouds.openstack.trove.v1.features.InstanceApi;
+import org.jclouds.openstack.trove.v1.predicates.InstancePredicates;
+import org.jclouds.logging.Logger;
+
+import com.google.common.util.concurrent.Uninterruptibles;
+
+/**
+ * @author Zack Shoylev
+ *
+ * Helper methods for dealing with instances that get created with errors.
+ */
+public class TroveUtils {
+ private final TroveApi api;
+ @Resource
+ protected Logger logger = Logger.NULL;
+
+ public TroveUtils(TroveApi api) {
+ this.api = api;
+ }
+
+ /**
+ * Create an ACTIVE operational instance.
+ *
+ * @see InstanceApi#create(String, int, String)
+ *
+ * @param zone
+ * The instance zone or region.
+ * @param name
+ * Instance name.
+ * @param flavorId
+ * Id of the flavor to be used when creating the instance.
+ * @param size
+ * Size of the instance.
+ * @return Instance object in active state or NULL.
+ */
+ public Instance getWorkingInstance(String zone, String name, String flavorId, int size) {
+ InstanceApi instanceApi = api.getInstanceApiForZone(zone);
+ for (int retries = 0; retries < 10; retries++) {
+ Instance instance = null;
+ try {
+ instance = instanceApi.create(flavorId, size, name);
+ } catch (Exception e) {
+
+ Uninterruptibles.sleepUninterruptibly(15, TimeUnit.SECONDS);
+
+ logger.error(e.getStackTrace().toString());
+ continue;
+ }
+
+ Instance updatedInstance = awaitAvailable(instance, instanceApi);
+ if (updatedInstance != null) {
+ return updatedInstance;
+ }
+ instanceApi.delete(instance.getId());
+ InstancePredicates.awaitDeleted(instanceApi).apply(instance);
+
+ }
+ return null;
+ }
+
+ /**
+ * This will return a small working instance.
+ *
+ * @param zone The zone where the instance should be created.
+ * @return A working database instance.
+ */
+ public Instance getWorkingInstance(String zone) {
+ return getWorkingInstance(zone, UUID.randomUUID().toString(), "1", 1);
+ }
+
+ private Instance awaitAvailable(Instance instance, InstanceApi iapi) {
+ for (int n = 0; n < 100; n = n + 1) {
+ Instance updatedInstance = iapi.get(instance.getId());
+ if (updatedInstance.getStatus() == Instance.Status.ACTIVE)
+ return updatedInstance;
+ if (updatedInstance.getStatus() == Instance.Status.UNRECOGNIZED)
+ return null; // fast fail
+ Uninterruptibles.sleepUninterruptibly(15, TimeUnit.SECONDS);
+ }
+ return null;
+ }
+}
diff --git a/apis/openstack-trove/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata b/apis/openstack-trove/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
new file mode 100644
index 0000000000..558658ca91
--- /dev/null
+++ b/apis/openstack-trove/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
@@ -0,0 +1,18 @@
+#
+# 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.
+#
+
+org.jclouds.openstack.trove.v1.TroveApiMetadata
diff --git a/apis/openstack-trove/src/test/java/org/jclouds/openstack/trove/v1/domain/FlavorTest.java b/apis/openstack-trove/src/test/java/org/jclouds/openstack/trove/v1/domain/FlavorTest.java
new file mode 100644
index 0000000000..0b9d1b18eb
--- /dev/null
+++ b/apis/openstack-trove/src/test/java/org/jclouds/openstack/trove/v1/domain/FlavorTest.java
@@ -0,0 +1,55 @@
+/*
+ * 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.trove.v1.domain;
+
+import org.jclouds.http.Uris;
+import org.jclouds.openstack.v2_0.domain.Link;
+import org.jclouds.openstack.v2_0.domain.Link.Relation;
+import org.testng.annotations.Test;
+import com.google.common.collect.ImmutableList;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+
+@Test(groups = "unit", testName = "FlavorTest")
+public class FlavorTest {
+ public void testFlavorForId() {
+ Flavor flavor1 = forId(1);
+ Flavor flavor2 = forId(2);
+ assertEquals(flavor1.getId(), 1);
+ assertEquals(flavor1.getName(), "small");
+ assertFalse(flavor1.equals(flavor2));
+ }
+
+ /**
+ * Creates a dummy Flavor when you need a Flavor with just the flavorId.
+ *
+ * 1. name = small
+ * 2. ram = 512
+ * 3. links = self, bookmark
+ */
+ public static Flavor forId(int flavorId) {
+ return Flavor.builder()
+ .id(flavorId)
+ .name("small")
+ .ram(512)
+ .links(
+ ImmutableList.of(
+ Link.create(Relation.SELF, Uris.uriBuilder("http://test1").build() ),
+ Link.create(Relation.BOOKMARK, Uris.uriBuilder("http://test2").build() )
+ ) ).build();
+ }
+}
diff --git a/apis/openstack-trove/src/test/java/org/jclouds/openstack/trove/v1/domain/InstanceTest.java b/apis/openstack-trove/src/test/java/org/jclouds/openstack/trove/v1/domain/InstanceTest.java
new file mode 100644
index 0000000000..d0cf6f24f6
--- /dev/null
+++ b/apis/openstack-trove/src/test/java/org/jclouds/openstack/trove/v1/domain/InstanceTest.java
@@ -0,0 +1,53 @@
+/*
+ * 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.trove.v1.domain;
+
+import org.jclouds.http.Uris;
+import org.jclouds.openstack.v2_0.domain.Link;
+import org.jclouds.openstack.v2_0.domain.Link.Relation;
+import org.testng.annotations.Test;
+import com.google.common.collect.ImmutableList;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+
+@Test(groups = "unit", testName = "InstanceTest")
+public class InstanceTest {
+ public void testInstanceForId() {
+ Instance instance1 = forId("1");
+ Instance instance2 = forId("2");
+ assertEquals(instance1.getId(), "1");
+ assertEquals(instance1.getName(), "json");
+ assertFalse(instance1.equals(instance2));
+ }
+
+ /**
+ * Creates a dummy Instance when you need an Instance with just the instanceId.
+ */
+ public static Instance forId(String instanceId) {
+ return Instance.builder()
+ .id(instanceId)
+ .name("json")
+ .status(Instance.Status.ACTIVE)
+ .size(2)
+ .flavor( FlavorTest.forId(1) )
+ .links(
+ ImmutableList.of(
+ Link.create(Relation.SELF, Uris.uriBuilder("http://test1").build() ),
+ Link.create(Relation.BOOKMARK, Uris.uriBuilder("http://test2").build() )
+ ) ).build();
+ }
+}
diff --git a/apis/openstack-trove/src/test/java/org/jclouds/openstack/trove/v1/domain/UserTest.java b/apis/openstack-trove/src/test/java/org/jclouds/openstack/trove/v1/domain/UserTest.java
new file mode 100644
index 0000000000..9edd6f8201
--- /dev/null
+++ b/apis/openstack-trove/src/test/java/org/jclouds/openstack/trove/v1/domain/UserTest.java
@@ -0,0 +1,47 @@
+/*
+ * 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.trove.v1.domain;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import org.testng.annotations.Test;
+import com.google.common.collect.ImmutableSet;
+
+@Test(groups = "unit", testName = "UserTest")
+public class UserTest {
+ public void testUserForName() {
+ User user1 = forName("1");
+ User user2 = forName("2");
+ assertEquals(user1.getName(), "1");
+ assertEquals(user2.getName(), "2");
+ assertFalse(user1.equals(user2));
+ }
+
+ /**
+ * Creates a dummy User when you need an User with just the userName.
+ */
+ public static User forName(String userName) {
+ return User.builder()
+ .name(userName)
+ .password("password")
+ .databases(
+ ImmutableSet.of(
+ "db1",
+ "db2"
+ ) ).build();
+ }
+}
diff --git a/apis/openstack-trove/src/test/java/org/jclouds/openstack/trove/v1/features/DatabaseApiExpectTest.java b/apis/openstack-trove/src/test/java/org/jclouds/openstack/trove/v1/features/DatabaseApiExpectTest.java
new file mode 100644
index 0000000000..d422e600bf
--- /dev/null
+++ b/apis/openstack-trove/src/test/java/org/jclouds/openstack/trove/v1/features/DatabaseApiExpectTest.java
@@ -0,0 +1,161 @@
+/*
+ * 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.trove.v1.features;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+import java.net.URI;
+import java.util.List;
+import java.util.Set;
+
+import javax.ws.rs.core.MediaType;
+
+import org.jclouds.http.HttpResponse;
+import org.jclouds.openstack.trove.v1.internal.BaseTroveApiExpectTest;
+import org.testng.annotations.Test;
+
+/**
+ * Tests DatabaseApi Guice wiring and parsing
+ *
+ * @author Zack Shoylev
+ */
+@Test(groups = "unit", testName = "DatabaseApiExpectTest")
+public class DatabaseApiExpectTest extends BaseTroveApiExpectTest {
+
+ public void testCreateDatabaseSimple() {
+ URI endpoint = URI.create("http://172.16.0.1:8776/v1/3456/instances/instanceId-1234-5678/databases");
+ DatabaseApi api = requestsSendResponses(
+ keystoneAuthWithUsernameAndPasswordAndTenantName,
+ responseWithKeystoneAccess,
+ authenticatedGET().endpoint(endpoint)
+ .method("POST")
+ .payload(payloadFromResourceWithContentType("/database_create_simple_request.json", MediaType.APPLICATION_JSON))
+ .build(),
+ HttpResponse.builder().statusCode(202).build() // response
+ ).getDatabaseApiForZoneAndInstance("RegionOne", "instanceId-1234-5678");
+
+ boolean result = api.create("testingdb");
+ assertTrue(result);
+ }
+
+ public void testCreateDatabaseSimpleFail() {
+ URI endpoint = URI.create("http://172.16.0.1:8776/v1/3456/instances/instanceId-1234-5678/databases");
+ DatabaseApi api = requestsSendResponses(
+ keystoneAuthWithUsernameAndPasswordAndTenantName,
+ responseWithKeystoneAccess,
+ authenticatedGET().endpoint(endpoint)
+ .method("POST")
+ .payload(payloadFromResourceWithContentType("/database_create_simple_request.json", MediaType.APPLICATION_JSON))
+ .build(),
+ HttpResponse.builder().statusCode(404).build() // response
+ ).getDatabaseApiForZoneAndInstance("RegionOne", "instanceId-1234-5678");
+
+ boolean result = api.create("testingdb");
+ assertFalse(result);
+ }
+
+ public void testCreateDatabase() {
+ URI endpoint = URI.create("http://172.16.0.1:8776/v1/3456/instances/instanceId-1234-5678/databases");
+ DatabaseApi api = requestsSendResponses(
+ keystoneAuthWithUsernameAndPasswordAndTenantName,
+ responseWithKeystoneAccess,
+ authenticatedGET().endpoint(endpoint)
+ .method("POST")
+ .payload(payloadFromResourceWithContentType("/database_create_request.json", MediaType.APPLICATION_JSON))
+ .build(),
+ HttpResponse.builder().statusCode(202).build() // response
+ ).getDatabaseApiForZoneAndInstance("RegionOne", "instanceId-1234-5678");
+
+ boolean result = api.create("testingdb", "utf8", "utf8_general_ci");
+ assertTrue(result);
+ }
+
+ public void testCreateDatabaseFail() {
+ URI endpoint = URI.create("http://172.16.0.1:8776/v1/3456/instances/instanceId-1234-5678/databases");
+ DatabaseApi api = requestsSendResponses(
+ keystoneAuthWithUsernameAndPasswordAndTenantName,
+ responseWithKeystoneAccess,
+ authenticatedGET().endpoint(endpoint)
+ .method("POST")
+ .payload(payloadFromResourceWithContentType("/database_create_request.json", MediaType.APPLICATION_JSON))
+ .build(),
+ HttpResponse.builder().statusCode(404).build() // response
+ ).getDatabaseApiForZoneAndInstance("RegionOne", "instanceId-1234-5678");
+
+ boolean result = api.create("testingdb", "utf8", "utf8_general_ci");
+ assertFalse(result);
+ }
+
+ public void testDeleteDatabase() {
+ URI endpoint = URI.create("http://172.16.0.1:8776/v1/3456/instances/instanceId-1234-5678/databases/db1");
+ DatabaseApi api = requestsSendResponses(
+ keystoneAuthWithUsernameAndPasswordAndTenantName,
+ responseWithKeystoneAccess,
+ authenticatedGET().endpoint(endpoint)
+ .method("DELETE")
+ .build(),
+ HttpResponse.builder().statusCode(202).build() // response
+ ).getDatabaseApiForZoneAndInstance("RegionOne", "instanceId-1234-5678");
+
+ boolean result = api.delete("db1");
+ assertTrue(result);
+ }
+
+ public void testDeleteDatabaseFail() {
+ URI endpoint = URI.create("http://172.16.0.1:8776/v1/3456/instances/instanceId-1234-5678/databases/db1");
+ DatabaseApi api = requestsSendResponses(
+ keystoneAuthWithUsernameAndPasswordAndTenantName,
+ responseWithKeystoneAccess,
+ authenticatedGET().endpoint(endpoint)
+ .method("DELETE")
+ .build(),
+ HttpResponse.builder().statusCode(404).build() // response
+ ).getDatabaseApiForZoneAndInstance("RegionOne", "instanceId-1234-5678");
+
+ boolean result = api.delete("db1");
+ assertFalse(result);
+ }
+
+ public void testListDatabases() {
+ URI endpoint = URI.create("http://172.16.0.1:8776/v1/3456/instances/instanceId-1234-5678/databases");
+ DatabaseApi api = requestsSendResponses(
+ keystoneAuthWithUsernameAndPasswordAndTenantName,
+ responseWithKeystoneAccess,
+ authenticatedGET().endpoint(endpoint).build(),
+ HttpResponse.builder().statusCode(200).payload(payloadFromResource("/database_list.json")).build()
+ ).getDatabaseApiForZoneAndInstance("RegionOne", "instanceId-1234-5678");
+
+ List databases = api.list().toList();
+ assertEquals(databases.size(), 5);
+ assertEquals(databases.iterator().next(), "anotherdb");
+ }
+
+ public void testListDatabasesFail() {
+ URI endpoint = URI.create("http://172.16.0.1:8776/v1/3456/instances/instanceId-1234-5678/databases");
+ DatabaseApi api = requestsSendResponses(
+ keystoneAuthWithUsernameAndPasswordAndTenantName,
+ responseWithKeystoneAccess,
+ authenticatedGET().endpoint(endpoint).build(),
+ HttpResponse.builder().statusCode(404).payload(payloadFromResource("/database_list.json")).build()
+ ).getDatabaseApiForZoneAndInstance("RegionOne", "instanceId-1234-5678");
+
+ Set databases = api.list().toSet();
+ assertTrue(databases.isEmpty());
+ }
+}
diff --git a/apis/openstack-trove/src/test/java/org/jclouds/openstack/trove/v1/features/DatabaseApiLiveTest.java b/apis/openstack-trove/src/test/java/org/jclouds/openstack/trove/v1/features/DatabaseApiLiveTest.java
new file mode 100644
index 0000000000..c60ee7ca15
--- /dev/null
+++ b/apis/openstack-trove/src/test/java/org/jclouds/openstack/trove/v1/features/DatabaseApiLiveTest.java
@@ -0,0 +1,114 @@
+/*
+ * 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.trove.v1.features;
+
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+import java.util.List;
+import java.util.Map;
+
+import org.jclouds.openstack.trove.v1.domain.Instance;
+import org.jclouds.openstack.trove.v1.internal.BaseTroveApiLiveTest;
+import org.jclouds.openstack.trove.v1.utils.TroveUtils;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+
+/**
+ * @author Zack Shoylev
+ */
+@Test(groups = "live", testName = "DatabaseApiLiveTest")
+public class DatabaseApiLiveTest extends BaseTroveApiLiveTest {
+
+ // zone to instance
+ private static Map> instancesToDelete = Maps.newHashMap();
+ // not deleting databases. they will be deleted when instances are deleted
+
+ @Override
+ @BeforeClass(groups = { "integration", "live" })
+ public void setup() {
+ super.setup();
+ TroveUtils utils = new TroveUtils(api);
+ for (String zone : api.getConfiguredZones()) {
+ // create instances
+ List instanceList = Lists.newArrayList();
+ Instance first = utils.getWorkingInstance(zone, "first_database_testing_" + zone, "1", 1);
+ Instance second = utils.getWorkingInstance(zone, "second_database_testing_" + zone, "1", 1);
+ instanceList.add(first);
+ instanceList.add(second);
+ instancesToDelete.put(zone, instanceList);
+
+ DatabaseApi databaseApiFirst = api.getDatabaseApiForZoneAndInstance(zone, first.getId());
+ DatabaseApi databaseApiSecond = api.getDatabaseApiForZoneAndInstance(zone, second.getId());
+ databaseApiFirst.create("livetest_db1");
+ databaseApiFirst.create("livetest_db2");
+ databaseApiSecond.create("livetest_db3");
+ }
+ }
+
+ @Override
+ @AfterClass(groups = { "integration", "live" })
+ public void tearDown(){
+ for (String zone : api.getConfiguredZones()) {
+ InstanceApi instanceApi = api.getInstanceApiForZone(zone);
+ for(Instance instance : instancesToDelete.get(zone)){
+ if( !instanceApi.delete(instance.getId() ) )
+ throw new RuntimeException("Could not delete a database instance after tests!");
+ }
+ }
+ super.tearDown();
+ }
+
+ @Test
+ public void testListDatabases() {
+ for (String zone : api.getConfiguredZones()) {
+ InstanceApi instanceApi = api.getInstanceApiForZone(zone);
+ assertTrue(instanceApi.list().size() >= 2);
+ for(Instance instance : instancesToDelete.get(zone)) {
+ DatabaseApi databaseApi = api.getDatabaseApiForZoneAndInstance(zone, instance.getId());
+ if(!instance.getName().contains("database_testing"))continue;
+ assertTrue(databaseApi.list().size() >=1);
+ for(String database : databaseApi.list()){
+ assertNotNull(database);
+ }
+ }
+ }
+ }
+
+ @Test
+ public void testDeleteDatabases() {
+ for (String zone : api.getConfiguredZones()) {
+ InstanceApi instanceApi = api.getInstanceApiForZone(zone);
+ assertTrue(instanceApi.list().size() >= 2);
+ for(Instance instance : instancesToDelete.get(zone)) {
+ DatabaseApi databaseApi = api.getDatabaseApiForZoneAndInstance(zone, instance.getId());
+ if(!instance.getName().contains("database_testing"))continue;
+ assertTrue(databaseApi.list().size() >=1);
+ for(String database : databaseApi.list()){
+ assertNotNull(database);
+ assertTrue(database.equals("livetest_db1") || database.equals("livetest_db2") || database.equals("livetest_db3") );
+ assertTrue(databaseApi.delete(database));
+ assertTrue(databaseApi.create(database));
+ }
+ }
+ }
+ }
+}
diff --git a/apis/openstack-trove/src/test/java/org/jclouds/openstack/trove/v1/features/FlavorApiExpectTest.java b/apis/openstack-trove/src/test/java/org/jclouds/openstack/trove/v1/features/FlavorApiExpectTest.java
new file mode 100644
index 0000000000..30c4b982c1
--- /dev/null
+++ b/apis/openstack-trove/src/test/java/org/jclouds/openstack/trove/v1/features/FlavorApiExpectTest.java
@@ -0,0 +1,111 @@
+/*
+ * 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.trove.v1.features;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+
+import java.net.URI;
+import java.util.Set;
+
+import org.jclouds.http.HttpResponse;
+import org.jclouds.openstack.trove.v1.TroveApi;
+import org.jclouds.openstack.trove.v1.domain.Flavor;
+import org.jclouds.openstack.trove.v1.internal.BaseTroveApiExpectTest;
+import org.testng.annotations.Test;
+
+/**
+ * Tests FlavorApi Guice wiring and parsing
+ *
+ * @author Zack Shoylev
+ */
+@Test(groups = "unit", testName = "FlavorApiExpectTest")
+public class FlavorApiExpectTest extends BaseTroveApiExpectTest {
+
+ public void testListFlavors() {
+ URI endpoint = URI.create("http://172.16.0.1:8776/v1/3456/flavors");
+ FlavorApi api = requestsSendResponses(
+ keystoneAuthWithUsernameAndPasswordAndTenantName,
+ responseWithKeystoneAccess,
+ authenticatedGET().endpoint(endpoint).build(),
+ HttpResponse.builder().statusCode(200).payload(payloadFromResource("/flavor_list.json")).build()
+ ).getFlavorApiForZone("RegionOne");
+
+ Set extends Flavor> flavors = api.list().toSet();
+ assertEquals(flavors.size(),6);
+ assertEquals(flavors.iterator().next().getRam(), 512);
+ }
+
+ public void testListFlavorsFail() {
+ URI endpoint = URI.create("http://172.16.0.1:8776/v1/3456/flavors");
+ FlavorApi api = requestsSendResponses(
+ keystoneAuthWithUsernameAndPasswordAndTenantName,
+ responseWithKeystoneAccess,
+ authenticatedGET().endpoint(endpoint).build(),
+ HttpResponse.builder().statusCode(404).build()
+ ).getFlavorApiForZone("RegionOne");
+
+ Set extends Flavor> flavors = api.list().toSet();
+ assertTrue(flavors.isEmpty());
+ }
+
+ public void testGetFlavor() {
+ URI endpoint = URI.create("http://172.16.0.1:8776/v1/3456/flavors/1");
+ FlavorApi api = requestsSendResponses(
+ keystoneAuthWithUsernameAndPasswordAndTenantName,
+ responseWithKeystoneAccess,
+ authenticatedGET().endpoint(endpoint).build(),
+ HttpResponse.builder().statusCode(200).payload(payloadFromResource("/flavor_get.json")).build()
+ ).getFlavorApiForZone("RegionOne");
+
+ Flavor flavor = api.get(1);
+ assertEquals(flavor.getName(), "512MB Instance");
+ assertEquals(flavor.getId(), 1);
+ assertEquals(flavor.getRam(), 512);
+ assertEquals(flavor.getLinks().size(), 2);
+ }
+
+ public void testGetFlavorByAccountId() {
+ URI endpoint = URI.create("http://172.16.0.1:8776/v1/3456/flavors/40806637803162");
+ TroveApi redDwarfApi = requestsSendResponses(
+ keystoneAuthWithUsernameAndPasswordAndTenantName,
+ responseWithKeystoneAccess,
+ authenticatedGET().endpoint(endpoint).build(),
+ HttpResponse.builder().statusCode(200).payload(payloadFromResource("/flavor_list.json")).build() );
+ FlavorApi api = redDwarfApi.getFlavorApiForZone("RegionOne");
+
+ Set extends Flavor> flavors = api.list( redDwarfApi.getCurrentTenantId().get().getId() ).toSet();
+ Flavor flavor = flavors.iterator().next();
+ assertEquals(flavor.getName(), "512MB Instance");
+ assertEquals(flavor.getId(), 1);
+ assertEquals(flavor.getRam(), 512);
+ assertEquals(flavor.getLinks().size(), 2);
+ }
+
+ public void testGetFlavorFail() {
+ URI endpoint = URI.create("http://172.16.0.1:8776/v1/3456/flavors/12312");
+ FlavorApi api = requestsSendResponses(
+ keystoneAuthWithUsernameAndPasswordAndTenantName,
+ responseWithKeystoneAccess,
+ authenticatedGET().endpoint(endpoint).build(),
+ HttpResponse.builder().statusCode(404).build()
+ ).getFlavorApiForZone("RegionOne");
+
+ assertNull(api.get(12312));
+ }
+}
diff --git a/apis/openstack-trove/src/test/java/org/jclouds/openstack/trove/v1/features/FlavorApiLiveTest.java b/apis/openstack-trove/src/test/java/org/jclouds/openstack/trove/v1/features/FlavorApiLiveTest.java
new file mode 100644
index 0000000000..7d0eebed64
--- /dev/null
+++ b/apis/openstack-trove/src/test/java/org/jclouds/openstack/trove/v1/features/FlavorApiLiveTest.java
@@ -0,0 +1,87 @@
+/*
+ * 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.trove.v1.features;
+
+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 org.jclouds.openstack.trove.v1.domain.Flavor;
+import org.jclouds.openstack.trove.v1.internal.BaseTroveApiLiveTest;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+import com.google.common.collect.FluentIterable;
+
+/**
+ * @author Zack Shoylev
+ */
+@Test(groups = "live", testName = "FlavorApiLiveTest")
+public class FlavorApiLiveTest extends BaseTroveApiLiveTest {
+
+ @Override
+ @BeforeClass(groups = { "integration", "live" })
+ public void setup() {
+ super.setup();
+ }
+
+ private void checkFlavor(Flavor flavor) {
+ assertNotNull(flavor.getId(), "Id cannot be null for " + flavor);
+ assertNotNull(flavor.getName(), "Name cannot be null for " + flavor);
+ }
+
+ @Test
+ public void testListFlavorsByAccount() {
+ for (String zone : api.getConfiguredZones()) {
+ FlavorApi flavorApi = api.getFlavorApiForZone(zone);
+
+ FluentIterable response = flavorApi.list( api.getCurrentTenantId().get().getId() ); // tenant id, but referred to as account id.
+ for (Flavor flavor : response) {
+ checkFlavor(flavor);
+ }
+ }
+ }
+
+ @Test
+ public void testListFlavorsByAccountWhenAccountIdNotFound() {
+ for (String zone : api.getConfiguredZones()) {
+ FlavorApi flavorApi = api.getFlavorApiForZone(zone);
+ assertTrue(flavorApi.list("9999").isEmpty());
+ }
+ }
+
+ @Test
+ public void testGetFlavor() {
+ for (String zone : api.getConfiguredZones()) {
+ FlavorApi flavorApi = api.getFlavorApiForZone(zone);
+ for (Flavor flavor : flavorApi.list()) {
+ Flavor flavorFromGet = flavorApi.get(flavor.getId());
+ assertEquals(flavorFromGet.getId(), flavor.getId());
+ assertEquals(flavorFromGet.getRam(), flavor.getRam());
+ assertEquals(flavorFromGet.getName(), flavor.getName());
+ assertEquals(flavorFromGet.getLinks(), flavor.getLinks());
+ }
+ }
+ }
+
+ @Test
+ public void testGetFlavorWhenNotFound() {
+ for (String zone : api.getConfiguredZones()) {
+ FlavorApi flavorApi = api.getFlavorApiForZone(zone);
+ assertNull(flavorApi.get(9999));
+ }
+ }
+}
diff --git a/apis/openstack-trove/src/test/java/org/jclouds/openstack/trove/v1/features/InstanceApiExpectTest.java b/apis/openstack-trove/src/test/java/org/jclouds/openstack/trove/v1/features/InstanceApiExpectTest.java
new file mode 100644
index 0000000000..369c4690eb
--- /dev/null
+++ b/apis/openstack-trove/src/test/java/org/jclouds/openstack/trove/v1/features/InstanceApiExpectTest.java
@@ -0,0 +1,217 @@
+/*
+ * 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.trove.v1.features;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+import java.net.URI;
+import java.util.Set;
+import javax.ws.rs.core.MediaType;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.openstack.trove.v1.domain.Instance;
+import org.jclouds.openstack.trove.v1.internal.BaseTroveApiExpectTest;
+import org.jclouds.rest.ResourceNotFoundException;
+import org.testng.annotations.Test;
+
+/**
+ * Tests InstanceApi Guice wiring and parsing
+ *
+ * @author Zack Shoylev
+ */
+@Test(groups = "unit", testName = "InstanceApiExpectTest")
+public class InstanceApiExpectTest extends BaseTroveApiExpectTest {
+
+ public void testCreateInstance() {
+ URI endpoint = URI.create("http://172.16.0.1:8776/v1/3456/instances");
+ InstanceApi api = requestsSendResponses(
+ keystoneAuthWithUsernameAndPasswordAndTenantName,
+ responseWithKeystoneAccess,
+ authenticatedGET().endpoint(endpoint) // bad naming convention, you should not be able to change the method to POST
+ .method("POST")
+ .payload(payloadFromResourceWithContentType("/instance_create_request.json", MediaType.APPLICATION_JSON))
+ .build(),
+ HttpResponse.builder().statusCode(200).payload(payloadFromResource("/instance_create.json")).build() // response
+ ).getInstanceApiForZone("RegionOne");
+
+ Instance instance = api.create("1", 2, "json_rack_instance");
+ assertEquals(instance.getSize(),2);
+ assertEquals(instance.getName(), "json_rack_instance");
+ }
+
+ @Test(expectedExceptions = ResourceNotFoundException.class)
+ public void testCreateInstanceFail() {
+ URI endpoint = URI.create("http://172.16.0.1:8776/v1/3456/instances");
+ InstanceApi api = requestsSendResponses(
+ keystoneAuthWithUsernameAndPasswordAndTenantName,
+ responseWithKeystoneAccess,
+ authenticatedGET().endpoint(endpoint) // bad naming convention, you should not be able to change the method to POST
+ .method("POST")
+ .payload(payloadFromResourceWithContentType("/instance_create_request.json", MediaType.APPLICATION_JSON))
+ .build(),
+ HttpResponse.builder().statusCode(404).payload(payloadFromResource("/instance_create.json")).build() // response
+ ).getInstanceApiForZone("RegionOne");
+
+ api.create("1", 2, "json_rack_instance");
+ }
+
+ public void testDeleteInstance() {
+ URI endpoint = URI.create("http://172.16.0.1:8776/v1/3456/instances/098653ba-218b-47ce-936a-e0b749101f81");
+ InstanceApi api = requestsSendResponses(
+ keystoneAuthWithUsernameAndPasswordAndTenantName,
+ responseWithKeystoneAccess,
+ authenticatedGET().endpoint(endpoint).method("DELETE").build(),
+ HttpResponse.builder().statusCode(202).build() // response
+ ).getInstanceApiForZone("RegionOne");
+
+ assertTrue( api.delete("098653ba-218b-47ce-936a-e0b749101f81") );
+ }
+
+ public void testDeleteInstanceFail() {
+ URI endpoint = URI.create("http://172.16.0.1:8776/v1/3456/instances/098653ba-218b-47ce-936a-e0b749101f81");
+ InstanceApi api = requestsSendResponses(
+ keystoneAuthWithUsernameAndPasswordAndTenantName,
+ responseWithKeystoneAccess,
+ authenticatedGET().endpoint(endpoint).method("DELETE").build(),
+ HttpResponse.builder().statusCode(404).build() // response
+ ).getInstanceApiForZone("RegionOne");
+
+ assertTrue( !api.delete("098653ba-218b-47ce-936a-e0b749101f81") );
+ }
+
+
+ public void testListInstances() {
+ URI endpoint = URI.create("http://172.16.0.1:8776/v1/3456/instances");
+ InstanceApi api = requestsSendResponses(
+ keystoneAuthWithUsernameAndPasswordAndTenantName,
+ responseWithKeystoneAccess,
+ authenticatedGET().endpoint(endpoint).build(),
+ HttpResponse.builder().statusCode(200).payload(payloadFromResource("/instance_list.json")).build()
+ ).getInstanceApiForZone("RegionOne");
+
+ Set extends Instance> instances = api.list().toSet();
+ assertEquals(instances.size(),2);
+ assertEquals(instances.iterator().next().getSize(), 2);
+ }
+
+ public void testListInstancesFail() {
+ URI endpoint = URI.create("http://172.16.0.1:8776/v1/3456/instances");
+ InstanceApi api = requestsSendResponses(
+ keystoneAuthWithUsernameAndPasswordAndTenantName,
+ responseWithKeystoneAccess,
+ authenticatedGET().endpoint(endpoint).build(),
+ HttpResponse.builder().statusCode(404).build()
+ ).getInstanceApiForZone("RegionOne");
+
+ Set extends Instance> instances = api.list().toSet();
+ assertTrue(instances.isEmpty());
+ }
+
+ public void testGetInstance() {
+ URI endpoint = URI.create("http://172.16.0.1:8776/v1/3456/instances/44b277eb-39be-4921-be31-3d61b43651d7");
+ InstanceApi api = requestsSendResponses(
+ keystoneAuthWithUsernameAndPasswordAndTenantName,
+ responseWithKeystoneAccess,
+ authenticatedGET().endpoint(endpoint).build(),
+ HttpResponse.builder().statusCode(200).payload(payloadFromResource("/instance_get.json")).build()
+ ).getInstanceApiForZone("RegionOne");
+
+ Instance instance = api.get("44b277eb-39be-4921-be31-3d61b43651d7");
+ assertEquals(instance.getName(), "json_rack_instance");
+ assertEquals(instance.getId(), "44b277eb-39be-4921-be31-3d61b43651d7");
+ assertEquals(instance.getLinks().size(), 2);
+ assertEquals(instance.getHostname(), "e09ad9a3f73309469cf1f43d11e79549caf9acf2.rackspaceclouddb.com");
+ }
+
+ public void testGetInstanceFail() {
+ URI endpoint = URI.create("http://172.16.0.1:8776/v1/3456/instances/12312");
+ InstanceApi api = requestsSendResponses(
+ keystoneAuthWithUsernameAndPasswordAndTenantName,
+ responseWithKeystoneAccess,
+ authenticatedGET().endpoint(endpoint).build(),
+ HttpResponse.builder().statusCode(404).build()
+ ).getInstanceApiForZone("RegionOne");
+
+ assertNull(api.get("12312"));
+ }
+
+ public void testEnableRootOnInstance() {
+ URI endpoint = URI.create("http://172.16.0.1:8776/v1/3456/instances/44b277eb-39be-4921-be31-3d61b43651d7/root");
+ InstanceApi api = requestsSendResponses(
+ keystoneAuthWithUsernameAndPasswordAndTenantName,
+ responseWithKeystoneAccess,
+ authenticatedGET().method("POST").endpoint(endpoint).build(),
+ HttpResponse.builder().statusCode(200).payload(payloadFromResource("/instance_root.json")).build()
+ ).getInstanceApiForZone("RegionOne");
+
+ String password = api.enableRoot("44b277eb-39be-4921-be31-3d61b43651d7");
+ assertEquals(password, "12345");
+ }
+
+ public void testEnableRootOnInstanceFail() {
+ URI endpoint = URI.create("http://172.16.0.1:8776/v1/3456/instances/44b277eb-39be-4921-be31-3d61b43651d7/root");
+ InstanceApi api = requestsSendResponses(
+ keystoneAuthWithUsernameAndPasswordAndTenantName,
+ responseWithKeystoneAccess,
+ authenticatedGET().method("POST").endpoint(endpoint).build(),
+ HttpResponse.builder().statusCode(404).payload(payloadFromResource("/instance_root.json")).build()
+ ).getInstanceApiForZone("RegionOne");
+
+ String password = api.enableRoot("44b277eb-39be-4921-be31-3d61b43651d7");
+ assertEquals(password, null);
+ }
+
+ public void testIsRootInstance() {
+ URI endpoint = URI.create("http://172.16.0.1:8776/v1/3456/instances/44b277eb-39be-4921-be31-3d61b43651d7/root");
+ InstanceApi api = requestsSendResponses(
+ keystoneAuthWithUsernameAndPasswordAndTenantName,
+ responseWithKeystoneAccess,
+ authenticatedGET().endpoint(endpoint).build(),
+ HttpResponse.builder().statusCode(200).payload(payloadFromResource("/instance_is_rooted.json")).build()
+ ).getInstanceApiForZone("RegionOne");
+
+ boolean rooted = api.isRooted("44b277eb-39be-4921-be31-3d61b43651d7");
+ assertEquals(rooted, true);
+ }
+
+ public void testIsRootInstanceFalse() {
+ URI endpoint = URI.create("http://172.16.0.1:8776/v1/3456/instances/44b277eb-39be-4921-be31-3d61b43651d7/root");
+ InstanceApi api = requestsSendResponses(
+ keystoneAuthWithUsernameAndPasswordAndTenantName,
+ responseWithKeystoneAccess,
+ authenticatedGET().endpoint(endpoint).build(),
+ HttpResponse.builder().statusCode(200).payload(payloadFromResource("/instance_is_rooted_false.json")).build()
+ ).getInstanceApiForZone("RegionOne");
+
+ Boolean rooted = api.isRooted("44b277eb-39be-4921-be31-3d61b43651d7");
+ assertEquals(rooted.booleanValue(), false);
+ }
+
+ @Test(expectedExceptions = ResourceNotFoundException.class)
+ public void testIsRootInstanceFail() {
+ URI endpoint = URI.create("http://172.16.0.1:8776/v1/3456/instances/44b277eb-39be-4921-be31-3d61b43651d7/root");
+ InstanceApi api = requestsSendResponses(
+ keystoneAuthWithUsernameAndPasswordAndTenantName,
+ responseWithKeystoneAccess,
+ authenticatedGET().endpoint(endpoint).build(),
+ HttpResponse.builder().statusCode(404).payload(payloadFromResource("/instance_is_rooted.json")).build()
+ ).getInstanceApiForZone("RegionOne");
+
+ Boolean rooted = api.isRooted("44b277eb-39be-4921-be31-3d61b43651d7");
+ assertNull(rooted);
+ }
+}
diff --git a/apis/openstack-trove/src/test/java/org/jclouds/openstack/trove/v1/features/InstanceApiLiveTest.java b/apis/openstack-trove/src/test/java/org/jclouds/openstack/trove/v1/features/InstanceApiLiveTest.java
new file mode 100644
index 0000000000..6de832a5fa
--- /dev/null
+++ b/apis/openstack-trove/src/test/java/org/jclouds/openstack/trove/v1/features/InstanceApiLiveTest.java
@@ -0,0 +1,137 @@
+/*
+ * 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.trove.v1.features;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.jclouds.openstack.trove.v1.domain.Instance;
+import org.jclouds.openstack.trove.v1.internal.BaseTroveApiLiveTest;
+import org.jclouds.openstack.trove.v1.utils.TroveUtils;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+
+/**
+ * @author Zack Shoylev
+ */
+@Test(groups = "live", testName = "InstanceApiLiveTest")
+public class InstanceApiLiveTest extends BaseTroveApiLiveTest {
+
+ private static Map> created = Maps.newHashMap();
+
+ @Override
+ @BeforeClass(groups = { "integration", "live" })
+ public void setup() {
+ super.setup();
+ TroveUtils utils= new TroveUtils(api);
+ for (String zone : api.getConfiguredZones()) {
+ List zoneList = Lists.newArrayList();
+ InstanceApi instanceApi = api.getInstanceApiForZone(zone);
+ zoneList.add(utils.getWorkingInstance(zone, "first_instance_testing_" + zone, "1", 1));
+ Instance second = utils.getWorkingInstance(zone, "second_instance_testing_" + zone, "1", 1);
+ instanceApi.enableRoot(second.getId());
+ zoneList.add(second);
+ created.put(zone, zoneList);
+ }
+ }
+
+ @Override
+ @AfterClass(groups = { "integration", "live" })
+ public void tearDown(){
+ for (String zone : api.getConfiguredZones()) {
+ InstanceApi instanceApi = api.getInstanceApiForZone(zone);
+ for(Instance instance : created.get(zone)){
+ if( !instanceApi.delete(instance.getId() ) )
+ throw new RuntimeException("Could not delete a database instance after tests!");
+ }
+ }
+ super.tearDown();
+ }
+
+ private void checkInstance(Instance instance) {
+ assertNotNull(instance.getId(), "Id cannot be null for " + instance);
+ checkArgument(instance.getSize() > 0, "Size must not be 0");
+ }
+
+ @Test
+ public void testListInstances() {
+ for (String zone : api.getConfiguredZones()) {
+ InstanceApi instanceApi = api.getInstanceApiForZone(zone);
+ FluentIterable response = instanceApi.list();
+ assertFalse(response.isEmpty());
+ for (Instance instance : response) {
+ checkInstance(instance);
+ }
+ }
+ }
+
+ @Test
+ public void testGetInstance() {
+ for (String zone : api.getConfiguredZones()) {
+ InstanceApi instanceApi = api.getInstanceApiForZone(zone);
+ for (Instance instance : instanceApi.list()) {
+ Instance instanceFromGet = instanceApi.get(instance.getId());
+ assertNotNull(instanceFromGet.getHostname());
+ assertNull(instance.getHostname());
+ assertEquals(instanceFromGet.getId(), instance.getId());
+ assertEquals(instanceFromGet.getName(), instance.getName());
+ assertEquals(instanceFromGet.getStatus(), instance.getStatus());
+ assertEquals(instanceFromGet.getFlavor(), instance.getFlavor());
+ assertEquals(instanceFromGet.getSize(), instance.getSize());
+ assertEquals(instanceFromGet.getLinks(), instance.getLinks());
+ }
+ }
+ }
+
+ @Test
+ public void testGetInstanceWhenNotFound() {
+ for (String zone : api.getConfiguredZones()) {
+ InstanceApi instanceApi = api.getInstanceApiForZone(zone);
+ assertNull(instanceApi.get("9999"));
+ }
+ }
+
+ @Test
+ public void testGetRootStatus() {
+ for (String zone : api.getConfiguredZones()) {
+ InstanceApi instanceApi = api.getInstanceApiForZone(zone);
+ Iterator iterator = instanceApi.list().iterator();
+ Instance first;
+ Instance second;
+ do {
+ first = iterator.next();
+ } while(!first.getName().contains("instance_testing"));
+ do {
+ second = iterator.next();
+ } while(!second.getName().contains("instance_testing"));
+ assertTrue(instanceApi.isRooted(first.getId()) || instanceApi.isRooted(second.getId()));
+ }
+ }
+}
diff --git a/apis/openstack-trove/src/test/java/org/jclouds/openstack/trove/v1/features/UserApiExpectTest.java b/apis/openstack-trove/src/test/java/org/jclouds/openstack/trove/v1/features/UserApiExpectTest.java
new file mode 100644
index 0000000000..b1f9208e3d
--- /dev/null
+++ b/apis/openstack-trove/src/test/java/org/jclouds/openstack/trove/v1/features/UserApiExpectTest.java
@@ -0,0 +1,433 @@
+/*
+ * 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.trove.v1.features;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.assertFalse;
+
+import java.net.URI;
+import java.util.List;
+import java.util.Set;
+
+import javax.ws.rs.core.MediaType;
+
+import org.jclouds.http.HttpResponse;
+import org.jclouds.openstack.trove.v1.domain.User;
+import org.jclouds.openstack.trove.v1.internal.BaseTroveApiExpectTest;
+import org.testng.annotations.Test;
+import org.testng.collections.Lists;
+import org.testng.internal.annotations.Sets;
+
+import com.google.common.collect.ImmutableSortedSet;
+import com.google.common.collect.ImmutableSortedSet.Builder;
+
+/**
+ * Tests UserApi Guice wiring and parsing
+ *
+ * @author Zack Shoylev
+ */
+@Test(groups = "unit", testName = "UserApiExpectTest")
+public class UserApiExpectTest extends BaseTroveApiExpectTest {
+
+ public void testCreateUserSimple() {
+ URI endpoint = URI.create("http://172.16.0.1:8776/v1/3456/instances/instanceId-1234-5678/users");
+ UserApi api = requestsSendResponses(
+ keystoneAuthWithUsernameAndPasswordAndTenantName,
+ responseWithKeystoneAccess,
+ authenticatedGET().endpoint(endpoint) // bad naming convention, you should not be able to change the method to POST
+ .method("POST")
+ .payload(payloadFromResourceWithContentType("/user_create_simple_request.json", MediaType.APPLICATION_JSON))
+ .build(),
+ HttpResponse.builder().statusCode(202).build() // response
+ ).getUserApiForZoneAndInstance("RegionOne", "instanceId-1234-5678");
+
+ boolean result = api.create("dbuser1", "password", "databaseA");
+ assertTrue(result);
+ }
+
+ public void testCreateUserSimpleFail() {
+ URI endpoint = URI.create("http://172.16.0.1:8776/v1/3456/instances/instanceId-1234-5678/users");
+ UserApi api = requestsSendResponses(
+ keystoneAuthWithUsernameAndPasswordAndTenantName,
+ responseWithKeystoneAccess,
+ authenticatedGET().endpoint(endpoint) // bad naming convention, you should not be able to change the method to POST
+ .method("POST")
+ .payload(payloadFromResourceWithContentType("/user_create_simple_request.json", MediaType.APPLICATION_JSON))
+ .build(),
+ HttpResponse.builder().statusCode(404).build() // response
+ ).getUserApiForZoneAndInstance("RegionOne", "instanceId-1234-5678");
+
+ boolean result = api.create("dbuser1", "password", "databaseA");
+ assertFalse(result);
+ }
+
+ public void testCreateUserSimpleWithHost() {
+ URI endpoint = URI.create("http://172.16.0.1:8776/v1/3456/instances/instanceId-1234-5678/users");
+ UserApi api = requestsSendResponses(
+ keystoneAuthWithUsernameAndPasswordAndTenantName,
+ responseWithKeystoneAccess,
+ authenticatedGET().endpoint(endpoint) // bad naming convention, you should not be able to change the method to POST
+ .method("POST")
+ .payload(payloadFromResourceWithContentType("/user_create_with_host_simple_request.json", MediaType.APPLICATION_JSON))
+ .build(),
+ HttpResponse.builder().statusCode(202).build() // response
+ ).getUserApiForZoneAndInstance("RegionOne", "instanceId-1234-5678");
+
+ boolean result = api.create("dbuser1", "password", "192.168.64.64", "databaseA");
+ assertTrue(result);
+ }
+
+ public void testCreateUserSimpleWithHostFail() {
+ URI endpoint = URI.create("http://172.16.0.1:8776/v1/3456/instances/instanceId-1234-5678/users");
+ UserApi api = requestsSendResponses(
+ keystoneAuthWithUsernameAndPasswordAndTenantName,
+ responseWithKeystoneAccess,
+ authenticatedGET().endpoint(endpoint) // bad naming convention, you should not be able to change the method to POST
+ .method("POST")
+ .payload(payloadFromResourceWithContentType("/user_create_with_host_simple_request.json", MediaType.APPLICATION_JSON))
+ .build(),
+ HttpResponse.builder().statusCode(404).build() // response
+ ).getUserApiForZoneAndInstance("RegionOne", "instanceId-1234-5678");
+
+ boolean result = api.create("dbuser1", "password", "192.168.64.64", "databaseA");
+ assertFalse(result);
+ }
+
+ public void testCreateUser() {
+ URI endpoint = URI.create("http://172.16.0.1:8776/v1/3456/instances/instanceId-1234-5678/users");
+ UserApi api = requestsSendResponses(
+ keystoneAuthWithUsernameAndPasswordAndTenantName,
+ responseWithKeystoneAccess,
+ authenticatedGET().endpoint(endpoint) // bad naming convention, you should not be able to change the method to POST
+ .method("POST")
+ .payload(payloadFromResourceWithContentType("/user_create_request.json", MediaType.APPLICATION_JSON))
+ .build(),
+ HttpResponse.builder().statusCode(202).build() // response
+ ).getUserApiForZoneAndInstance("RegionOne", "instanceId-1234-5678");
+
+ Set databases1 = Sets.newHashSet();
+ databases1.add( "databaseA" );
+ Builder databases2builder = ImmutableSortedSet.naturalOrder();
+ databases2builder.add( "databaseB" );
+ databases2builder.add( "databaseC" );
+ Set databases2 = databases2builder.build();
+ Set databases3 = Sets.newHashSet();
+ databases3.add( "databaseD" );
+ User user1 = User.builder().databases( databases1 ).name("dbuser1").password("password").build();
+ User user2 = User.builder().databases( databases2 ).name("dbuser2").password("password").build();
+ User user3 = User.builder().databases( databases3 ).name("dbuser3").password("password").host("192.168.64.64").build();
+ Set users = Sets.newHashSet();
+ users.add(user1);
+ users.add(user2);
+ users.add(user3);
+
+ boolean result = api.create(ImmutableSortedSet.naturalOrder().addAll(users).build());
+ assertTrue(result);
+ }
+
+ public void testCreateUserFail() {
+ URI endpoint = URI.create("http://172.16.0.1:8776/v1/3456/instances/instanceId-1234-5678/users");
+ UserApi api = requestsSendResponses(
+ keystoneAuthWithUsernameAndPasswordAndTenantName,
+ responseWithKeystoneAccess,
+ authenticatedGET().endpoint(endpoint) // bad naming convention, you should not be able to change the method to POST
+ .method("POST")
+ .payload(payloadFromResourceWithContentType("/user_create_request.json", MediaType.APPLICATION_JSON))
+ .build(),
+ HttpResponse.builder().statusCode(404).build() // response
+ ).getUserApiForZoneAndInstance("RegionOne", "instanceId-1234-5678");
+
+ Set databases1 = Sets.newHashSet();
+ databases1.add( "databaseA" );
+ Builder databases2builder = ImmutableSortedSet.naturalOrder();
+ databases2builder.add( "databaseB" );
+ databases2builder.add( "databaseC" );
+ Set databases2 = databases2builder.build();
+ Set databases3 = Sets.newHashSet();
+ databases3.add( "databaseD" );
+ User user1 = User.builder().databases( databases1 ).name("dbuser1").password("password").build();
+ User user2 = User.builder().databases( databases2 ).name("dbuser2").password("password").build();
+ User user3 = User.builder().databases( databases3 ).name("dbuser3").password("password").host("192.168.64.64").build();
+ Set users = Sets.newHashSet();
+ users.add(user1);
+ users.add(user2);
+ users.add(user3);
+
+ boolean result = api.create( ImmutableSortedSet.naturalOrder().addAll(users).build());
+ assertFalse(result);
+ }
+
+ public void testGrantUserSimple() {
+ URI endpoint = URI.create("http://172.16.0.1:8776/v1/3456/instances/instanceId-1234-5678/users/dbuser1/databases");
+ UserApi api = requestsSendResponses(
+ keystoneAuthWithUsernameAndPasswordAndTenantName,
+ responseWithKeystoneAccess,
+ authenticatedGET().endpoint(endpoint) // bad naming convention, you should not be able to change the method to POST
+ .method("PUT")
+ .payload(payloadFromResourceWithContentType("/user_grant_simple_request.json", MediaType.APPLICATION_JSON))
+ .build(),
+ HttpResponse.builder().statusCode(202).build() // response
+ ).getUserApiForZoneAndInstance("RegionOne", "instanceId-1234-5678");
+
+ boolean result = api.grant("dbuser1", "databaseZ");
+ assertTrue(result);
+ }
+
+ public void testGrantUserSimpleFail() {
+ URI endpoint = URI.create("http://172.16.0.1:8776/v1/3456/instances/instanceId-1234-5678/users/dbuser1/databases");
+ UserApi api = requestsSendResponses(
+ keystoneAuthWithUsernameAndPasswordAndTenantName,
+ responseWithKeystoneAccess,
+ authenticatedGET().endpoint(endpoint) // bad naming convention, you should not be able to change the method to POST
+ .method("PUT")
+ .payload(payloadFromResourceWithContentType("/user_grant_simple_request.json", MediaType.APPLICATION_JSON))
+ .build(),
+ HttpResponse.builder().statusCode(404).build() // response
+ ).getUserApiForZoneAndInstance("RegionOne", "instanceId-1234-5678");
+
+ boolean result = api.grant("dbuser1", "databaseZ");
+ assertFalse(result);
+ }
+
+ public void testGrantUser() {
+ URI endpoint = URI.create("http://172.16.0.1:8776/v1/3456/instances/instanceId-1234-5678/users/dbuser1/databases");
+ UserApi api = requestsSendResponses(
+ keystoneAuthWithUsernameAndPasswordAndTenantName,
+ responseWithKeystoneAccess,
+ authenticatedGET().endpoint(endpoint) // bad naming convention, you should not be able to change the method to POST
+ .method("PUT")
+ .payload(payloadFromResourceWithContentType("/user_grant_request.json", MediaType.APPLICATION_JSON))
+ .build(),
+ HttpResponse.builder().statusCode(202).build() // response
+ ).getUserApiForZoneAndInstance("RegionOne", "instanceId-1234-5678");
+
+ List databases = Lists.newArrayList();
+ databases.add( "databaseC" );
+ databases.add( "databaseD" );
+
+ boolean result = api.grant("dbuser1", databases);
+ assertTrue(result);
+ }
+
+ public void testGrantUserFail() {
+ URI endpoint = URI.create("http://172.16.0.1:8776/v1/3456/instances/instanceId-1234-5678/users/dbuser1/databases");
+ UserApi api = requestsSendResponses(
+ keystoneAuthWithUsernameAndPasswordAndTenantName,
+ responseWithKeystoneAccess,
+ authenticatedGET().endpoint(endpoint) // bad naming convention, you should not be able to change the method to POST
+ .method("PUT")
+ .payload(payloadFromResourceWithContentType("/user_grant_request.json", MediaType.APPLICATION_JSON))
+ .build(),
+ HttpResponse.builder().statusCode(404).build() // response
+ ).getUserApiForZoneAndInstance("RegionOne", "instanceId-1234-5678");
+
+ List databases = Lists.newArrayList();
+ databases.add( "databaseC" );
+ databases.add( "databaseD" );
+
+ boolean result = api.grant("dbuser1", databases);
+ assertFalse(result);
+ }
+
+ public void testRevokeUser() {
+ URI endpoint = URI.create("http://172.16.0.1:8776/v1/3456/instances/instanceId-1234-5678/users/dbuser1/databases/databaseA");
+ UserApi api = requestsSendResponses(
+ keystoneAuthWithUsernameAndPasswordAndTenantName,
+ responseWithKeystoneAccess,
+ authenticatedGET().endpoint(endpoint) // bad naming convention, you should not be able to change the method to POST
+ .method("DELETE")
+ .build(),
+ HttpResponse.builder().statusCode(202).build() // response
+ ).getUserApiForZoneAndInstance("RegionOne", "instanceId-1234-5678");
+
+ Set databases = Sets.newHashSet();
+ databases.add( "database" );
+ databases.add( "database" );
+ boolean result = api.revoke("dbuser1", "databaseA");
+ assertTrue(result);
+ }
+
+ public void testRevokeUserFail() {
+ URI endpoint = URI.create("http://172.16.0.1:8776/v1/3456/instances/instanceId-1234-5678/users/dbuser1/databases/databaseA");
+ UserApi api = requestsSendResponses(
+ keystoneAuthWithUsernameAndPasswordAndTenantName,
+ responseWithKeystoneAccess,
+ authenticatedGET().endpoint(endpoint) // bad naming convention, you should not be able to change the method to POST
+ .method("DELETE")
+ .build(),
+ HttpResponse.builder().statusCode(404).build() // response
+ ).getUserApiForZoneAndInstance("RegionOne", "instanceId-1234-5678");
+
+ Set databases = Sets.newHashSet();
+ databases.add( "database" );
+ databases.add( "database" );
+ boolean result = api.revoke("dbuser1", "databaseA");
+ assertFalse(result);
+ }
+
+ public void testDeleteUser() {
+ URI endpoint = URI.create("http://172.16.0.1:8776/v1/3456/instances/instanceId-1234-5678/users/dbuser1");
+ UserApi api = requestsSendResponses(
+ keystoneAuthWithUsernameAndPasswordAndTenantName,
+ responseWithKeystoneAccess,
+ authenticatedGET().endpoint(endpoint) // bad naming convention, you should not be able to change the method to POST
+ .method("DELETE")
+ .build(),
+ HttpResponse.builder().statusCode(202).build() // response
+ ).getUserApiForZoneAndInstance("RegionOne", "instanceId-1234-5678");
+
+ Set databases = Sets.newHashSet();
+ databases.add( "database" );
+ databases.add( "database" );
+ boolean result = api.delete("dbuser1");
+ assertTrue(result);
+ }
+
+ public void testDeleteUserFail() {
+ URI endpoint = URI.create("http://172.16.0.1:8776/v1/3456/instances/instanceId-1234-5678/users/dbuser1");
+ UserApi api = requestsSendResponses(
+ keystoneAuthWithUsernameAndPasswordAndTenantName,
+ responseWithKeystoneAccess,
+ authenticatedGET().endpoint(endpoint) // bad naming convention, you should not be able to change the method to POST
+ .method("DELETE")
+ .build(),
+ HttpResponse.builder().statusCode(404).build() // response
+ ).getUserApiForZoneAndInstance("RegionOne", "instanceId-1234-5678");
+
+ Set databases = Sets.newHashSet();
+ databases.add( "database" );
+ databases.add( "database" );
+ boolean result = api.delete("dbuser1");
+ assertFalse(result);
+ }
+
+ public void testListUsers() {
+ URI endpoint = URI.create("http://172.16.0.1:8776/v1/3456/instances/instanceId-1234-5678/users");
+ UserApi api = requestsSendResponses(
+ keystoneAuthWithUsernameAndPasswordAndTenantName,
+ responseWithKeystoneAccess,
+ authenticatedGET().endpoint(endpoint).build(),
+ HttpResponse.builder().statusCode(200).payload(payloadFromResource("/trove_user_list.json")).build()
+ ).getUserApiForZoneAndInstance("RegionOne", "instanceId-1234-5678");
+
+ Set users = api.list().toSet();
+ assertEquals(users.size(), 4);
+ assertTrue(users.iterator().next().getDatabases().isEmpty());
+ assertEquals(users.iterator().next().getName(), "dbuser1");
+ }
+
+ public void testListUsersFail() {
+ URI endpoint = URI.create("http://172.16.0.1:8776/v1/3456/instances/instanceId-1234-5678/users");
+ UserApi api = requestsSendResponses(
+ keystoneAuthWithUsernameAndPasswordAndTenantName,
+ responseWithKeystoneAccess,
+ authenticatedGET().endpoint(endpoint).build(),
+ HttpResponse.builder().statusCode(404).payload(payloadFromResource("/trove_user_list.json")).build()
+ ).getUserApiForZoneAndInstance("RegionOne", "instanceId-1234-5678");
+
+ Set users = api.list().toSet();
+ assertTrue(users.isEmpty());
+ }
+
+ public void testUserGetDatabaseList() {
+ URI endpoint = URI.create("http://172.16.0.1:8776/v1/3456/instances/instanceId-1234-5678/users/dbuser1/databases");
+ UserApi api = requestsSendResponses(
+ keystoneAuthWithUsernameAndPasswordAndTenantName,
+ responseWithKeystoneAccess,
+ authenticatedGET().endpoint(endpoint).build(),
+ HttpResponse.builder().statusCode(200).payload(payloadFromResource("/user_list_access.json")).build()
+ ).getUserApiForZoneAndInstance("RegionOne", "instanceId-1234-5678");
+
+ List databases = api.getDatabaseList("dbuser1").toList();
+ assertEquals(databases.size(), 2);
+ assertEquals(databases.iterator().next(), "databaseA");
+ }
+
+ public void testUserGetDatabaseListFail() {
+ URI endpoint = URI.create("http://172.16.0.1:8776/v1/3456/instances/instanceId-1234-5678/users/dbuser1/databases");
+ UserApi api = requestsSendResponses(
+ keystoneAuthWithUsernameAndPasswordAndTenantName,
+ responseWithKeystoneAccess,
+ authenticatedGET().endpoint(endpoint).build(),
+ HttpResponse.builder().statusCode(404).payload(payloadFromResource("/user_list_access.json")).build()
+ ).getUserApiForZoneAndInstance("RegionOne", "instanceId-1234-5678");
+
+ Set databases = api.getDatabaseList("dbuser1").toSet();
+ assertTrue(databases.isEmpty());
+ }
+
+ public void testGetUser() {
+ URI endpoint = URI.create("http://172.16.0.1:8776/v1/3456/instances/instanceId-1234-5678/users/exampleuser");
+ UserApi api = requestsSendResponses(
+ keystoneAuthWithUsernameAndPasswordAndTenantName,
+ responseWithKeystoneAccess,
+ authenticatedGET().endpoint(endpoint).build(),
+ HttpResponse.builder().statusCode(200).payload(payloadFromResource("/user_get.json")).build()
+ ).getUserApiForZoneAndInstance("RegionOne", "instanceId-1234-5678");
+
+ User user = api.get("exampleuser");
+ assertEquals(user.getName(), "exampleuser");
+ assertEquals(user.getHost(), "%");
+ assertEquals(user.getDatabases().size(), 2);
+ assertEquals(user.getDatabases().iterator().next(), "databaseA");
+ }
+
+ public void testGetUserFail() {
+ URI endpoint = URI.create("http://172.16.0.1:8776/v1/3456/instances/instanceId-1234-5678/users/exampleuser");
+ UserApi api = requestsSendResponses(
+ keystoneAuthWithUsernameAndPasswordAndTenantName,
+ responseWithKeystoneAccess,
+ authenticatedGET().endpoint(endpoint).build(),
+ HttpResponse.builder().statusCode(404).payload(payloadFromResource("/user_get.json")).build()
+ ).getUserApiForZoneAndInstance("RegionOne", "instanceId-1234-5678");
+
+ User user = api.get("exampleuser");
+ assertNull(user);
+ }
+
+ public void testGetUserWithHostname() {
+ URI endpoint = URI.create("http://172.16.0.1:8776/v1/3456/instances/instanceId-1234-5678/users/example%2euser%40192%2e168%2e64%2e64");
+ UserApi api = requestsSendResponses(
+ keystoneAuthWithUsernameAndPasswordAndTenantName,
+ responseWithKeystoneAccess,
+ authenticatedGET().endpoint(endpoint).build(),
+ HttpResponse.builder().statusCode(200).payload(payloadFromResource("/user_get_withhost.json")).build()
+ ).getUserApiForZoneAndInstance("RegionOne", "instanceId-1234-5678");
+
+ User user = api.get("example.user", "192.168.64.64");
+ assertEquals(user.getName(), "example.user");
+ assertEquals(user.getHost(), "192.168.64.64");
+ assertEquals(user.getIdentifier(), "example.user@192.168.64.64");
+ assertEquals(user.getDatabases().size(), 2);
+ assertEquals(user.getDatabases().iterator().next(), "databaseA");
+ }
+
+ public void testGetUserWithHostnameFail() {
+ URI endpoint = URI.create("http://172.16.0.1:8776/v1/3456/instances/instanceId-1234-5678/users/example%2euser%40192%2e168%2e64%2e64");
+ UserApi api = requestsSendResponses(
+ keystoneAuthWithUsernameAndPasswordAndTenantName,
+ responseWithKeystoneAccess,
+ authenticatedGET().endpoint(endpoint).build(),
+ HttpResponse.builder().statusCode(404).payload(payloadFromResource("/user_get_withhost.json")).build()
+ ).getUserApiForZoneAndInstance("RegionOne", "instanceId-1234-5678");
+
+ User user = api.get("example.user", "192.168.64.64");
+ assertNull(user);
+ }
+}
diff --git a/apis/openstack-trove/src/test/java/org/jclouds/openstack/trove/v1/features/UserApiLiveTest.java b/apis/openstack-trove/src/test/java/org/jclouds/openstack/trove/v1/features/UserApiLiveTest.java
new file mode 100644
index 0000000000..cd4a46863d
--- /dev/null
+++ b/apis/openstack-trove/src/test/java/org/jclouds/openstack/trove/v1/features/UserApiLiveTest.java
@@ -0,0 +1,207 @@
+/*
+ * 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.trove.v1.features;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+
+import org.jclouds.openstack.trove.v1.domain.Instance;
+import org.jclouds.openstack.trove.v1.domain.User;
+import org.jclouds.openstack.trove.v1.internal.BaseTroveApiLiveTest;
+import org.jclouds.openstack.trove.v1.utils.TroveUtils;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+
+/**
+ * @author Zack Shoylev
+ */
+@Test(groups = "live", testName = "UserApiLiveTest")
+public class UserApiLiveTest extends BaseTroveApiLiveTest {
+
+ // zone to instance
+ private static Map> instancesToDelete = Maps.newHashMap();
+ // not deleting users. they will be deleted when instances are deleted
+
+ @Override
+ @BeforeClass(groups = { "integration", "live" })
+ public void setup() {
+ super.setup();
+ TroveUtils utils= new TroveUtils(api);
+ for (String zone : api.getConfiguredZones()) {
+ // create instances
+ List instanceList = Lists.newArrayList();
+ Instance first = utils.getWorkingInstance(zone, "first_user_trove_live_testing_" + zone, "1", 1);
+ Instance second = utils.getWorkingInstance(zone, "second_user_trove_live_testing_" + zone, "1", 1);
+ instanceList.add(first);
+ instanceList.add(second);
+ instancesToDelete.put(zone, instanceList);
+ // create users
+ User user1 = User.builder()
+ .name("user1")
+ .password(UUID.randomUUID().toString())
+ .databases(ImmutableSet.of(
+ "u1db1",
+ "u1db2")).build();
+ User user2 = User.builder()
+ .name("user2")
+ .password(UUID.randomUUID().toString())
+ .databases(ImmutableSet.of(
+ "u2db1",
+ "u2db2")).build();
+ User user3 = User.builder()
+ .name("user3")
+ .password(UUID.randomUUID().toString())
+ .host("173.203.44.122")
+ .databases(ImmutableSet.of(
+ "u3db1",
+ "u3db2")).build();
+ UserApi userApiFirst = api.getUserApiForZoneAndInstance(zone, first.getId());
+ UserApi userApiSecond = api.getUserApiForZoneAndInstance(zone, second.getId());
+ userApiFirst.create(ImmutableSet.of(user1, user2));
+ userApiSecond.create(ImmutableSet.of(user3));
+ }
+ }
+
+ @Override
+ @AfterClass(groups = { "integration", "live" })
+ public void tearDown(){
+ for (String zone : api.getConfiguredZones()) {
+ InstanceApi instanceApi = api.getInstanceApiForZone(zone);
+ for(Instance instance : instancesToDelete.get(zone)){
+ if( !instanceApi.delete(instance.getId() ) )
+ throw new RuntimeException("Could not delete a database instance after tests!");
+ }
+ }
+ super.tearDown();
+ }
+
+ private void checkUser(User user) {
+ assertNotNull(user.getName(), "Name cannot be null for " + user);
+ assertNotNull(user.getHost(), "Host cannot be null (should be '%' if default) for " + user);
+ checkArgument(!user.getDatabases().isEmpty(), "Number of databases must not be 0");
+ }
+
+ @Test
+ public void testListUsers() {
+ for (String zone : api.getConfiguredZones()) {
+ InstanceApi instanceApi = api.getInstanceApiForZone(zone);
+ assertTrue(instanceApi.list().size() >= 2);
+ for(Instance instance : instancesToDelete.get(zone)) {
+ UserApi userApi = api.getUserApiForZoneAndInstance(zone, instance.getId());
+ if(!instance.getName().contains("user_trove_live_testing"))continue;
+ assertTrue(userApi.list().size() >=1);
+ for(User user : userApi.list()){
+ checkUser(user);
+ }
+ }
+ }
+ }
+
+ @Test
+ public void testGetUser() {
+ for (String zone : api.getConfiguredZones()) {
+ InstanceApi instanceApi = api.getInstanceApiForZone(zone);
+ assertTrue(instanceApi.list().size() >= 2);
+ for(Instance instance : instancesToDelete.get(zone)) {
+ UserApi userApi = api.getUserApiForZoneAndInstance(zone, instance.getId());
+ if(!instance.getName().contains("user_trove_live_testing"))continue;
+ assertTrue(userApi.list().size() >=1);
+ for(User user : userApi.list()){
+ User userFromGet = userApi.get(user.getIdentifier());
+ assertEquals(userFromGet.getName(), user.getName());
+ assertEquals(userFromGet.getHost(), user.getHost());
+ assertEquals(userFromGet.getIdentifier(), user.getIdentifier());
+ assertEquals(userFromGet.getDatabases(), user.getDatabases());
+ assertEquals(userFromGet, user);
+ }
+ }
+ }
+ }
+
+ @Test
+ public void testGetDatabaseListForUser() {
+ for (String zone : api.getConfiguredZones()) {
+ InstanceApi instanceApi = api.getInstanceApiForZone(zone);
+ assertTrue(instanceApi.list().size() >= 2 );
+ for(Instance instance : instancesToDelete.get(zone)) {
+ UserApi userApi = api.getUserApiForZoneAndInstance(zone, instance.getId());
+ if(!instance.getName().contains("user_trove_live_testing"))continue;
+ assertTrue(userApi.list().size() >=1);
+ for(User user : userApi.list()){
+ assertFalse(userApi.getDatabaseList(user.getIdentifier()).isEmpty());
+ }
+ }
+ }
+ }
+
+ @Test
+ public void testGrantAndRevokeAcccessForUser() {
+ for (String zone : api.getConfiguredZones()) {
+ InstanceApi instanceApi = api.getInstanceApiForZone(zone);
+ assertTrue(instanceApi.list().size() >= 2);
+ for(Instance instance : instancesToDelete.get(zone)) {
+ UserApi userApi = api.getUserApiForZoneAndInstance(zone, instance.getId());
+ if(!instance.getName().contains("user_trove_live_testing"))continue;
+ assertTrue(userApi.list().size() >=1);
+ for(User user : userApi.list()){
+ userApi.grant(user.getIdentifier(), "dbA");
+ userApi.grant(user.getIdentifier(), ImmutableList.of(
+ "dbB",
+ "dbC"));
+
+ Set databases = userApi.getDatabaseList(user.getIdentifier()).toSet();
+ assertTrue(databases.contains("dbA"));
+ assertTrue(databases.contains("dbB"));
+ assertTrue(databases.contains("dbC"));
+
+ userApi.revoke(user.getIdentifier(), "dbA");
+ userApi.revoke(user.getIdentifier(), "dbB");
+ userApi.revoke(user.getIdentifier(), "dbC");
+
+ databases = userApi.getDatabaseList(user.getIdentifier()).toSet();
+ assertFalse(databases.contains("dbA"));
+ assertFalse(databases.contains("dbB"));
+ assertFalse(databases.contains("dbC"));
+ }
+ }
+ }
+ }
+
+ @Test
+ public void testGetUserWhenNotFound() {
+ for (String zone : api.getConfiguredZones()) {
+ String instanceId = instancesToDelete.get(zone).iterator().next().getId();
+ UserApi userApi = api.getUserApiForZoneAndInstance(zone, instanceId);
+ assertNull(userApi.get("9999"));
+ }
+ }
+}
diff --git a/apis/openstack-trove/src/test/java/org/jclouds/openstack/trove/v1/internal/BaseTroveApiExpectTest.java b/apis/openstack-trove/src/test/java/org/jclouds/openstack/trove/v1/internal/BaseTroveApiExpectTest.java
new file mode 100644
index 0000000000..b8c42160d3
--- /dev/null
+++ b/apis/openstack-trove/src/test/java/org/jclouds/openstack/trove/v1/internal/BaseTroveApiExpectTest.java
@@ -0,0 +1,27 @@
+/*
+ * 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.trove.v1.internal;
+
+import org.jclouds.openstack.trove.v1.TroveApi;
+
+/**
+ * Base class for writing Trove Rest Api Expect tests
+ *
+ * @author Everett Toews
+ */
+public class BaseTroveApiExpectTest extends BaseTroveExpectTest {
+}
diff --git a/apis/openstack-trove/src/test/java/org/jclouds/openstack/trove/v1/internal/BaseTroveApiLiveTest.java b/apis/openstack-trove/src/test/java/org/jclouds/openstack/trove/v1/internal/BaseTroveApiLiveTest.java
new file mode 100644
index 0000000000..978f4cee2e
--- /dev/null
+++ b/apis/openstack-trove/src/test/java/org/jclouds/openstack/trove/v1/internal/BaseTroveApiLiveTest.java
@@ -0,0 +1,43 @@
+/*
+ * 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.trove.v1.internal;
+
+import java.util.Properties;
+
+import org.jclouds.apis.BaseApiLiveTest;
+import org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties;
+import org.jclouds.openstack.trove.v1.TroveApi;
+
+/**
+ * Tests behavior of TroveApi
+ *
+ * @author Zack Shoylev
+ */
+public class BaseTroveApiLiveTest extends BaseApiLiveTest {
+
+ public BaseTroveApiLiveTest() {
+ provider = "openstack-trove";
+ }
+
+ @Override
+ protected Properties setupProperties() {
+ Properties props = super.setupProperties();
+ setIfTestSystemPropertyPresent(props, KeystoneProperties.CREDENTIAL_TYPE);
+ setIfTestSystemPropertyPresent(props, KeystoneProperties.SERVICE_TYPE);
+ return props;
+ }
+}
diff --git a/apis/openstack-trove/src/test/java/org/jclouds/openstack/trove/v1/internal/BaseTroveExpectTest.java b/apis/openstack-trove/src/test/java/org/jclouds/openstack/trove/v1/internal/BaseTroveExpectTest.java
new file mode 100644
index 0000000000..b43db05de0
--- /dev/null
+++ b/apis/openstack-trove/src/test/java/org/jclouds/openstack/trove/v1/internal/BaseTroveExpectTest.java
@@ -0,0 +1,78 @@
+/*
+ * 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.trove.v1.internal;
+
+import javax.ws.rs.core.MediaType;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.openstack.keystone.v2_0.internal.KeystoneFixture;
+import org.jclouds.rest.internal.BaseRestApiExpectTest;
+
+/**
+ * Base class for writing Nova Expect tests
+ *
+ * @author Adrian Cole
+ */
+public class BaseTroveExpectTest extends BaseRestApiExpectTest {
+ protected HttpRequest keystoneAuthWithUsernameAndPassword;
+ protected HttpRequest keystoneAuthWithUsernameAndPasswordAndTenantName;
+ protected HttpRequest keystoneAuthWithAccessKeyAndSecretKeyAndTenantName;
+ protected String authToken;
+ protected HttpResponse responseWithKeystoneAccess;
+ protected HttpRequest extensionsOfNovaRequest;
+ protected HttpResponse extensionsOfNovaResponse;
+ protected HttpResponse unmatchedExtensionsOfNovaResponse;
+ protected HttpRequest keystoneAuthWithAccessKeyAndSecretKeyAndTenantId;
+ protected String identityWithTenantId;
+
+ public BaseTroveExpectTest() {
+ provider = "openstack-trove";
+ keystoneAuthWithUsernameAndPassword = KeystoneFixture.INSTANCE.initialAuthWithUsernameAndPassword(identity,
+ credential);
+ keystoneAuthWithUsernameAndPasswordAndTenantName = KeystoneFixture.INSTANCE.initialAuthWithUsernameAndPasswordAndTenantName(identity,
+ credential);
+ keystoneAuthWithAccessKeyAndSecretKeyAndTenantName = KeystoneFixture.INSTANCE.initialAuthWithAccessKeyAndSecretKeyAndTenantName(identity,
+ credential);
+ keystoneAuthWithAccessKeyAndSecretKeyAndTenantId = KeystoneFixture.INSTANCE.initialAuthWithAccessKeyAndSecretKeyAndTenantId(identity,
+ credential);
+
+ authToken = KeystoneFixture.INSTANCE.getAuthToken();
+ responseWithKeystoneAccess = KeystoneFixture.INSTANCE.responseWithAccess();
+ // now, createContext arg will need tenant prefix
+ identityWithTenantId = KeystoneFixture.INSTANCE.getTenantId() + ":" + identity;
+ identity = KeystoneFixture.INSTANCE.getTenantName() + ":" + identity;
+ }
+
+ @Override
+ protected HttpRequestComparisonType compareHttpRequestAsType(HttpRequest input) {
+ return HttpRequestComparisonType.JSON;
+ }
+
+ protected HttpRequest.Builder> authenticatedGET() {
+ return HttpRequest.builder()
+ .method("GET")
+ .addHeader("Accept", MediaType.APPLICATION_JSON)
+ .addHeader("X-Auth-Token", authToken);
+ }
+
+ protected HttpRequest.Builder> authenticatedPOST() {
+ return HttpRequest.builder()
+ .method("POST")
+ .addHeader("Accept", MediaType.APPLICATION_JSON)
+ .addHeader("X-Auth-Token", authToken);
+ }
+}
diff --git a/apis/openstack-trove/src/test/java/org/jclouds/openstack/trove/v1/parse/ParseFlavorListTest.java b/apis/openstack-trove/src/test/java/org/jclouds/openstack/trove/v1/parse/ParseFlavorListTest.java
new file mode 100644
index 0000000000..39361cfc4d
--- /dev/null
+++ b/apis/openstack-trove/src/test/java/org/jclouds/openstack/trove/v1/parse/ParseFlavorListTest.java
@@ -0,0 +1,108 @@
+/*
+ * 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.trove.v1.parse;
+
+import java.util.Set;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.core.MediaType;
+
+import org.jclouds.http.Uris;
+import org.jclouds.json.BaseSetParserTest;
+import org.jclouds.openstack.trove.v1.domain.Flavor;
+import org.jclouds.openstack.v2_0.domain.Link;
+import org.jclouds.openstack.v2_0.domain.Link.Relation;
+import org.jclouds.rest.annotations.SelectJson;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+
+/**
+ *
+ * @author Zack Shoylev
+ */
+
+@Test(groups = "unit", testName = "ParseFlavorTest")
+public class ParseFlavorListTest extends BaseSetParserTest {
+
+ @Override
+ public String resource() {
+ return "/flavor_list.json";
+ }
+
+ @Override
+ @Consumes(MediaType.APPLICATION_JSON)
+ @SelectJson("flavors")
+ public Set expected() {
+ return ImmutableSet
+ .of(Flavor.builder()
+ .id(1)
+ .name("512MB Instance")
+ .ram(512)
+ .links(ImmutableList.of(
+ Link.create(Relation.SELF, Uris.uriBuilder("https://localhost:8778/v1.0/811050/flavors/1").build() ),
+ Link.create(Relation.BOOKMARK, Uris.uriBuilder("https://localhost:8778/flavors/1").build() )
+ ))
+ .build(),
+ Flavor.builder()
+ .id(2)
+ .name("1GB Instance")
+ .ram(1024)
+ .links(ImmutableList.of(
+ Link.create(Relation.SELF, Uris.uriBuilder("https://localhost:8778/v1.0/811050/flavors/2").build() ),
+ Link.create(Relation.BOOKMARK, Uris.uriBuilder("https://localhost:8778/flavors/2").build() )
+ ))
+ .build(),
+ Flavor.builder()
+ .id(3)
+ .name("2GB Instance")
+ .ram(2048)
+ .links(ImmutableList.of(
+ Link.create(Relation.SELF, Uris.uriBuilder("https://localhost:8778/v1.0/811050/flavors/3").build() ),
+ Link.create(Relation.BOOKMARK, Uris.uriBuilder("https://localhost:8778/flavors/3").build() )
+ ))
+ .build(),
+ Flavor.builder()
+ .id(4)
+ .name("4GB Instance")
+ .ram(4096)
+ .links(ImmutableList.of(
+ Link.create(Relation.SELF, Uris.uriBuilder("https://localhost:8778/v1.0/811050/flavors/4").build() ),
+ Link.create(Relation.BOOKMARK, Uris.uriBuilder("https://localhost:8778/flavors/4").build() )
+ ))
+ .build(),
+ Flavor.builder()
+ .id(5)
+ .name("8GB Instance")
+ .ram(8192)
+ .links(ImmutableList.of(
+ Link.create(Relation.SELF, Uris.uriBuilder("https://localhost:8778/v1.0/811050/flavors/5").build() ),
+ Link.create(Relation.BOOKMARK, Uris.uriBuilder("https://localhost:8778/flavors/5").build() )
+ ))
+ .build(),
+ Flavor.builder()
+ .id(6)
+ .name("16GB Instance")
+ .ram(16384)
+ .links(ImmutableList.of(
+ Link.create(Relation.SELF, Uris.uriBuilder("https://localhost:8778/v1.0/811050/flavors/6").build() ),
+ Link.create(Relation.BOOKMARK, Uris.uriBuilder("https://localhost:8778/flavors/6").build() )
+ ))
+ .build());
+ }
+}
diff --git a/apis/openstack-trove/src/test/java/org/jclouds/openstack/trove/v1/parse/ParseInstanceListTest.java b/apis/openstack-trove/src/test/java/org/jclouds/openstack/trove/v1/parse/ParseInstanceListTest.java
new file mode 100644
index 0000000000..995404e370
--- /dev/null
+++ b/apis/openstack-trove/src/test/java/org/jclouds/openstack/trove/v1/parse/ParseInstanceListTest.java
@@ -0,0 +1,95 @@
+/*
+ * 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.trove.v1.parse;
+
+import java.util.Set;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.core.MediaType;
+
+import org.jclouds.http.Uris;
+import org.jclouds.json.BaseSetParserTest;
+import org.jclouds.openstack.trove.v1.domain.Flavor;
+import org.jclouds.openstack.trove.v1.domain.Instance;
+import org.jclouds.openstack.v2_0.domain.Link;
+import org.jclouds.openstack.v2_0.domain.Link.Relation;
+import org.jclouds.rest.annotations.SelectJson;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+
+/**
+ *
+ * @author Zack Shoylev
+ */
+
+@Test(groups = "unit", testName = "ParseInstanceTest")
+public class ParseInstanceListTest extends BaseSetParserTest {
+
+ @Override
+ public String resource() {
+ return "/instance_list.json";
+ }
+
+ /*
+ * The instance needs to be comparable for this test to work
+ * */
+ @Override
+ @Consumes(MediaType.APPLICATION_JSON)
+ @SelectJson("instances")
+ public Set expected() {
+ return ImmutableSet
+ .of(Instance.builder()
+ .id("098653ba-218b-47ce-936a-e0b749101f81")
+ .name("xml_rack_instance")
+ .size(2)
+ .flavor(
+ Flavor.builder()
+ .id(1)
+ .links(ImmutableList.of(
+ Link.create(Relation.SELF, Uris.uriBuilder("https://ord.databases.api.rackspacecloud.com/v1.0/1234/flavors/1").build() ),
+ Link.create(Relation.BOOKMARK, Uris.uriBuilder("https://ord.databases.api.rackspacecloud.com/flavors/1").build())))
+ .build()
+ )
+ .status(Instance.Status.ACTIVE)
+ .links(ImmutableList.of(
+ Link.create(Relation.SELF, Uris.uriBuilder("https://ord.databases.api.rackspacecloud.com/v1.0/1234/instances/098653ba-218b-47ce-936a-e0b749101f81").build() ),
+ Link.create(Relation.BOOKMARK, Uris.uriBuilder("https://ord.databases.api.rackspacecloud.com/instances/098653ba-218b-47ce-936a-e0b749101f81").build() )
+ ))
+ .build(),
+ Instance.builder()
+ .id("44b277eb-39be-4921-be31-3d61b43651d7")
+ .name("json_rack_instance")
+ .size(2)
+ .flavor(
+ Flavor.builder()
+ .id(1)
+ .links(ImmutableList.of(
+ Link.create(Relation.SELF, Uris.uriBuilder("https://ord.databases.api.rackspacecloud.com/v1.0/1234/flavors/1").build() ),
+ Link.create(Relation.BOOKMARK, Uris.uriBuilder("https://ord.databases.api.rackspacecloud.com/flavors/1").build())))
+ .build()
+ )
+ .status(Instance.Status.ACTIVE)
+ .links(ImmutableList.of(
+ Link.create(Relation.SELF, Uris.uriBuilder("https://ord.databases.api.rackspacecloud.com/v1.0/1234/instances/44b277eb-39be-4921-be31-3d61b43651d7").build() ),
+ Link.create(Relation.BOOKMARK, Uris.uriBuilder("https://ord.databases.api.rackspacecloud.com/instances/44b277eb-39be-4921-be31-3d61b43651d7").build() )
+ ))
+ .build()
+ );
+ }
+}
diff --git a/apis/openstack-trove/src/test/java/org/jclouds/openstack/trove/v1/parse/ParseUserListTest.java b/apis/openstack-trove/src/test/java/org/jclouds/openstack/trove/v1/parse/ParseUserListTest.java
new file mode 100644
index 0000000000..e62fbf4581
--- /dev/null
+++ b/apis/openstack-trove/src/test/java/org/jclouds/openstack/trove/v1/parse/ParseUserListTest.java
@@ -0,0 +1,66 @@
+/*
+ * 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.trove.v1.parse;
+
+import java.util.Set;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.core.MediaType;
+import org.jclouds.json.BaseSetParserTest;
+import org.jclouds.openstack.trove.v1.domain.User;
+import org.jclouds.rest.annotations.SelectJson;
+import org.testng.annotations.Test;
+import com.google.common.collect.ImmutableSet;
+
+/**
+ *
+ * @author Zack Shoylev
+ */
+
+@Test(groups = "unit", testName = "ParseUserTest")
+public class ParseUserListTest extends BaseSetParserTest {
+
+ @Override
+ public String resource() {
+ return "/trove_user_list.json";
+ }
+
+ /*
+ * The user needs to be comparable for this test to work
+ * */
+ @Override
+ @Consumes(MediaType.APPLICATION_JSON)
+ @SelectJson("users")
+ public Set expected() {
+ return ImmutableSet
+ .of(User.builder()
+ .name("dbuser1")
+ .host("%")
+ .build(),
+ User.builder()
+ .name("dbuser2")
+ .host("%")
+ .databases( ImmutableSet.of(
+ "databaseB",
+ "databaseC") )
+ .build(),
+ User.builder().name("dbuser3").host("%").build(),
+ User.builder().name("demouser").host("%").databases(
+ ImmutableSet.of("sampledb"))
+ .build()
+ );
+ }
+}
diff --git a/apis/openstack-trove/src/test/java/org/jclouds/openstack/trove/v1/utils/TroveUtilsExpectTest.java b/apis/openstack-trove/src/test/java/org/jclouds/openstack/trove/v1/utils/TroveUtilsExpectTest.java
new file mode 100644
index 0000000000..1170710289
--- /dev/null
+++ b/apis/openstack-trove/src/test/java/org/jclouds/openstack/trove/v1/utils/TroveUtilsExpectTest.java
@@ -0,0 +1,67 @@
+/*
+ * 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.trove.v1.utils;
+
+import static org.testng.Assert.assertEquals;
+
+import java.net.URI;
+import java.util.List;
+
+import javax.ws.rs.core.MediaType;
+
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.openstack.trove.v1.TroveApi;
+import org.jclouds.openstack.trove.v1.domain.Instance;
+import org.jclouds.openstack.trove.v1.internal.BaseTroveApiExpectTest;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+
+/**
+ * Tests TroveUtils
+ *
+ * @author Zack Shoylev
+ */
+@Test(groups = "unit", testName = "InstanceApiExpectTest")
+public class TroveUtilsExpectTest extends BaseTroveApiExpectTest {
+
+ public void testHelperCreateInstance() {
+ HttpRequest createInstance = authenticatedGET().endpoint(URI.create("http://172.16.0.1:8776/v1/3456/instances"))
+ .method("POST")
+ .payload(payloadFromResourceWithContentType("/instance_create_request.json", MediaType.APPLICATION_JSON))
+ .build();
+ HttpResponse createInstanceSuccess = HttpResponse.builder().statusCode(200).payload(payloadFromResource("/instance_create.json")).build();
+ HttpResponse createInstanceFail = HttpResponse.builder().statusCode(404).payload(payloadFromResource("/instance_create.json")).build();
+ HttpRequest getInstance = authenticatedGET().endpoint(URI.create("http://172.16.0.1:8776/v1/3456/instances/44b277eb-39be-4921-be31-3d61b43651d7")).build();
+ HttpResponse badStatus = HttpResponse.builder().statusCode(200).payload(payloadFromResource("/instance_get_bad_instance.json")).build();
+ HttpResponse goodStatus = HttpResponse.builder().statusCode(200).payload(payloadFromResource("/instance_get.json")).build();
+ HttpResponse deletedStatus = HttpResponse.builder().statusCode(404).payload(payloadFromResource("/instance_get.json")).build();
+ HttpRequest deleteInstance = authenticatedGET().endpoint(URI.create("http://172.16.0.1:8776/v1/3456/instances/44b277eb-39be-4921-be31-3d61b43651d7")).method("DELETE").build();
+ HttpResponse deleteInstanceResponse = HttpResponse.builder().statusCode(202).build();
+
+ List requests = ImmutableList.of( keystoneAuthWithUsernameAndPasswordAndTenantName, createInstance, createInstance, createInstance, getInstance, deleteInstance, getInstance, createInstance, createInstance, getInstance);
+ List responses = ImmutableList.of(responseWithKeystoneAccess, createInstanceFail, createInstanceFail, createInstanceSuccess, badStatus, deleteInstanceResponse, deletedStatus, createInstanceFail, createInstanceSuccess, goodStatus);
+
+ TroveApi api = orderedRequestsSendResponses(requests, responses);
+
+ TroveUtils utils = new TroveUtils(api);
+ Instance instance = utils.getWorkingInstance("RegionOne", "json_rack_instance", "1", 2);
+ assertEquals(instance.getSize(),2);
+ assertEquals(instance.getName(), "json_rack_instance");
+ }
+}
diff --git a/apis/openstack-trove/src/test/resources/access_rax_us.json b/apis/openstack-trove/src/test/resources/access_rax_us.json
new file mode 100644
index 0000000000..b90bebc7f7
--- /dev/null
+++ b/apis/openstack-trove/src/test/resources/access_rax_us.json
@@ -0,0 +1,168 @@
+{
+ "access": {
+ "token": {
+ "id": "myToken",
+ "expires": "2012-09-30T17:15:32.000-05:00",
+ "tenant": {
+ "id": "717071",
+ "name": "717071"
+ }
+ },
+ "serviceCatalog": [
+ {
+ "endpoints": [
+ {
+ "tenantId": "717071",
+ "publicURL": "https://dns.api.rackspacecloud.com/v1.0/717071"
+ }
+ ],
+ "name": "cloudDNS",
+ "type": "rax:dns"
+ },
+ {
+ "endpoints": [
+ {
+ "tenantId": "717071",
+ "publicURL": "https://monitoring.api.rackspacecloud.com/v1.0/717071"
+ }
+ ],
+ "name": "cloudMonitoring",
+ "type": "rax:monitor"
+ },
+ {
+ "endpoints": [
+ {
+ "region": "DFW",
+ "tenantId": "717071",
+ "publicURL": "https://dfw.servers.api.rackspacecloud.com/v2/717071",
+ "versionInfo": "https://dfw.servers.api.rackspacecloud.com/v2",
+ "versionList": "https://dfw.servers.api.rackspacecloud.com/",
+ "versionId": "2"
+ },
+ {
+ "region": "ORD",
+ "tenantId": "717071",
+ "publicURL": "https://ord.servers.api.rackspacecloud.com/v2/717071",
+ "versionInfo": "https://ord.servers.api.rackspacecloud.com/v2",
+ "versionList": "https://ord.servers.api.rackspacecloud.com/",
+ "versionId": "2"
+ }
+ ],
+ "name": "cloudServersOpenStack",
+ "type": "compute"
+ },
+ {
+ "endpoints": [
+ {
+ "region": "ORD",
+ "tenantId": "717071",
+ "publicURL": "https://ord.loadbalancers.api.rackspacecloud.com/v1.0/717071"
+ },
+ {
+ "region": "DFW",
+ "tenantId": "717071",
+ "publicURL": "https://dfw.loadbalancers.api.rackspacecloud.com/v1.0/717071"
+ }
+ ],
+ "name": "cloudLoadBalancers",
+ "type": "rax:load-balancer"
+ },
+ {
+ "endpoints": [
+ {
+ "region": "DFW",
+ "tenantId": "717071",
+ "publicURL": "https://dfw.databases.api.rackspacecloud.com/v1.0/717071"
+ },
+ {
+ "region": "ORD",
+ "tenantId": "717071",
+ "publicURL": "https://ord.databases.api.rackspacecloud.com/v1.0/717071"
+ },
+ {
+ "region": "SYD",
+ "tenantId": "717071",
+ "publicURL": "https://syd.databases.api.rackspacecloud.com/v1.0/717071"
+ }
+ ],
+ "name": "cloudDatabases",
+ "type": "rax:database"
+ },
+ {
+ "endpoints": [
+ {
+ "region": "DFW",
+ "tenantId": "MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9",
+ "publicURL": "https://storage101.dfw1.clouddrive.com/v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9",
+ "internalURL": "https://snet-storage101.dfw1.clouddrive.com/v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9"
+ },
+ {
+ "region": "ORD",
+ "tenantId": "MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9",
+ "publicURL": "https://storage101.ord1.clouddrive.com/v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9",
+ "internalURL": "https://snet-storage101.ord1.clouddrive.com/v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9"
+ }
+ ],
+ "name": "cloudFiles",
+ "type": "object-store"
+ },
+ {
+ "endpoints": [
+ {
+ "region": "DFW",
+ "tenantId": "MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9",
+ "publicURL": "https://cdn1.clouddrive.com/v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9"
+ },
+ {
+ "region": "ORD",
+ "tenantId": "MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9",
+ "publicURL": "https://cdn2.clouddrive.com/v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9"
+ }
+ ],
+ "name": "cloudFilesCDN",
+ "type": "rax:object-cdn"
+ },
+ {
+ "endpoints": [
+ {
+ "tenantId": "717071",
+ "publicURL": "https://servers.api.rackspacecloud.com/v1.0/717071",
+ "versionInfo": "https://servers.api.rackspacecloud.com/v1.0",
+ "versionList": "https://servers.api.rackspacecloud.com/",
+ "versionId": "1.0"
+ }
+ ],
+ "name": "cloudServers",
+ "type": "compute"
+ },
+ {
+ "endpoints": [
+ {
+ "publicURL": "https://ord.blockstorage.api.rackspacecloud.com/v1/717071",
+ "tenantId": "717071",
+ "region": "ORD"
+ },
+ {
+ "region": "DFW",
+ "tenantId": "717071",
+ "publicURL": "https://dfw.blockstorage.api.rackspacecloud.com/v1/717071"
+ }
+ ],
+ "name": "cloudBlockStorage",
+ "type": "volume"
+ }
+ ],
+ "user": {
+ "id": "224085",
+ "roles": [
+ {
+ "id": "3",
+ "description": "User Admin Role.",
+ "name": "identity:user-admin"
+ }
+ ],
+ "name": "myUsername",
+ "RAX-AUTH:defaultRegion": "DFW"
+ }
+ }
+}
diff --git a/apis/openstack-trove/src/test/resources/database_create_request.json b/apis/openstack-trove/src/test/resources/database_create_request.json
new file mode 100644
index 0000000000..e032a2d56c
--- /dev/null
+++ b/apis/openstack-trove/src/test/resources/database_create_request.json
@@ -0,0 +1,9 @@
+{
+ "databases": [
+ {
+ "character_set": "utf8",
+ "collate": "utf8_general_ci",
+ "name": "testingdb"
+ }
+ ]
+}
diff --git a/apis/openstack-trove/src/test/resources/database_create_simple_request.json b/apis/openstack-trove/src/test/resources/database_create_simple_request.json
new file mode 100644
index 0000000000..6f9fd0095e
--- /dev/null
+++ b/apis/openstack-trove/src/test/resources/database_create_simple_request.json
@@ -0,0 +1,7 @@
+{
+ "databases": [
+ {
+ "name": "testingdb"
+ }
+ ]
+}
diff --git a/apis/openstack-trove/src/test/resources/database_list.json b/apis/openstack-trove/src/test/resources/database_list.json
new file mode 100644
index 0000000000..a895ddcf09
--- /dev/null
+++ b/apis/openstack-trove/src/test/resources/database_list.json
@@ -0,0 +1,19 @@
+{
+ "databases": [
+ {
+ "name": "anotherdb"
+ },
+ {
+ "name": "nextround"
+ },
+ {
+ "name": "oneMoreDB"
+ },
+ {
+ "name": "sampledb"
+ },
+ {
+ "name": "testingdb"
+ }
+ ]
+}
diff --git a/apis/openstack-trove/src/test/resources/flavor_get.json b/apis/openstack-trove/src/test/resources/flavor_get.json
new file mode 100644
index 0000000000..9290fa7576
--- /dev/null
+++ b/apis/openstack-trove/src/test/resources/flavor_get.json
@@ -0,0 +1,17 @@
+{
+ "flavor":{
+ "id":1,
+ "links":[
+ {
+ "href":"https://ord.databases.api.rackspacecloud.com/v1.0/1234/flavors/1",
+ "rel":"self"
+ },
+ {
+ "href":"https://ord.databases.api.rackspacecloud.com/flavors/1",
+ "rel":"bookmark"
+ }
+ ],
+ "name":"512MB Instance",
+ "ram":512
+ }
+}
diff --git a/apis/openstack-trove/src/test/resources/flavor_list.json b/apis/openstack-trove/src/test/resources/flavor_list.json
new file mode 100644
index 0000000000..edc364cc90
--- /dev/null
+++ b/apis/openstack-trove/src/test/resources/flavor_list.json
@@ -0,0 +1,94 @@
+{
+ "flavors":[
+ {
+ "ram":512,
+ "id":1,
+ "links":[
+ {
+ "href":"https://localhost:8778/v1.0/811050/flavors/1",
+ "rel":"self"
+ },
+ {
+ "href":"https://localhost:8778/flavors/1",
+ "rel":"bookmark"
+ }
+ ],
+ "name":"512MB Instance"
+ },
+ {
+ "ram":1024,
+ "id":2,
+ "links":[
+ {
+ "href":"https://localhost:8778/v1.0/811050/flavors/2",
+ "rel":"self"
+ },
+ {
+ "href":"https://localhost:8778/flavors/2",
+ "rel":"bookmark"
+ }
+ ],
+ "name":"1GB Instance"
+ },
+ {
+ "ram":2048,
+ "id":3,
+ "links":[
+ {
+ "href":"https://localhost:8778/v1.0/811050/flavors/3",
+ "rel":"self"
+ },
+ {
+ "href":"https://localhost:8778/flavors/3",
+ "rel":"bookmark"
+ }
+ ],
+ "name":"2GB Instance"
+ },
+ {
+ "ram":4096,
+ "id":4,
+ "links":[
+ {
+ "href":"https://localhost:8778/v1.0/811050/flavors/4",
+ "rel":"self"
+ },
+ {
+ "href":"https://localhost:8778/flavors/4",
+ "rel":"bookmark"
+ }
+ ],
+ "name":"4GB Instance"
+ },
+ {
+ "ram":8192,
+ "id":5,
+ "links":[
+ {
+ "href":"https://localhost:8778/v1.0/811050/flavors/5",
+ "rel":"self"
+ },
+ {
+ "href":"https://localhost:8778/flavors/5",
+ "rel":"bookmark"
+ }
+ ],
+ "name":"8GB Instance"
+ },
+ {
+ "ram":16384,
+ "id":6,
+ "links":[
+ {
+ "href":"https://localhost:8778/v1.0/811050/flavors/6",
+ "rel":"self"
+ },
+ {
+ "href":"https://localhost:8778/flavors/6",
+ "rel":"bookmark"
+ }
+ ],
+ "name":"16GB Instance"
+ }
+ ]
+}
diff --git a/apis/openstack-trove/src/test/resources/instance_create.json b/apis/openstack-trove/src/test/resources/instance_create.json
new file mode 100644
index 0000000000..cfb41069d9
--- /dev/null
+++ b/apis/openstack-trove/src/test/resources/instance_create.json
@@ -0,0 +1,35 @@
+{
+ "instance": {
+ "created": "2013-03-18T19:09:17",
+ "flavor": {
+ "id": "1",
+ "links": [
+ {
+ "href": "https://ord.databases.api.rackspacecloud.com/v1.0/1234/flavors/1",
+ "rel": "self"
+ },
+ {
+ "href": "https://ord.databases.api.rackspacecloud.com/flavors/1",
+ "rel": "bookmark"
+ }
+ ]
+ },
+ "id": "44b277eb-39be-4921-be31-3d61b43651d7",
+ "links": [
+ {
+ "href": "https://ord.databases.api.rackspacecloud.com/v1.0/1234/instances/44b277eb-39be-4921-be31-3d61b43651d7",
+ "rel": "self"
+ },
+ {
+ "href": "https://ord.databases.api.rackspacecloud.com/instances/44b277eb-39be-4921-be31-3d61b43651d7",
+ "rel": "bookmark"
+ }
+ ],
+ "name": "json_rack_instance",
+ "status": "BUILD",
+ "updated": "2013-03-18T19:09:17",
+ "volume": {
+ "size": 2
+ }
+ }
+}
diff --git a/apis/openstack-trove/src/test/resources/instance_create_request.json b/apis/openstack-trove/src/test/resources/instance_create_request.json
new file mode 100644
index 0000000000..a1031b958c
--- /dev/null
+++ b/apis/openstack-trove/src/test/resources/instance_create_request.json
@@ -0,0 +1,9 @@
+{
+ "instance":{
+ "name":"json_rack_instance",
+ "volume":{
+ "size":2
+ },
+ "flavorRef":"1"
+ }
+}
diff --git a/apis/openstack-trove/src/test/resources/instance_get.json b/apis/openstack-trove/src/test/resources/instance_get.json
new file mode 100644
index 0000000000..baf87082c6
--- /dev/null
+++ b/apis/openstack-trove/src/test/resources/instance_get.json
@@ -0,0 +1,37 @@
+{
+ "instance": {
+ "created": "2013-03-18T19:09:17",
+ "flavor": {
+ "id": "1",
+ "links": [
+ {
+ "href": "https://ord.databases.api.rackspacecloud.com/v1.0/1234/flavors/1",
+ "rel": "self"
+ },
+ {
+ "href": "https://ord.databases.api.rackspacecloud.com/flavors/1",
+ "rel": "bookmark"
+ }
+ ]
+ },
+ "hostname": "e09ad9a3f73309469cf1f43d11e79549caf9acf2.rackspaceclouddb.com",
+ "id": "44b277eb-39be-4921-be31-3d61b43651d7",
+ "links": [
+ {
+ "href": "https://ord.databases.api.rackspacecloud.com/v1.0/1234/instances/44b277eb-39be-4921-be31-3d61b43651d7",
+ "rel": "self"
+ },
+ {
+ "href": "https://ord.databases.api.rackspacecloud.com/instances/44b277eb-39be-4921-be31-3d61b43651d7",
+ "rel": "bookmark"
+ }
+ ],
+ "name": "json_rack_instance",
+ "status": "ACTIVE",
+ "updated": "2013-03-18T19:09:17",
+ "volume": {
+ "size": 2,
+ "used": 0.16368598397821188
+ }
+ }
+}
diff --git a/apis/openstack-trove/src/test/resources/instance_get_bad_instance.json b/apis/openstack-trove/src/test/resources/instance_get_bad_instance.json
new file mode 100644
index 0000000000..99b868844b
--- /dev/null
+++ b/apis/openstack-trove/src/test/resources/instance_get_bad_instance.json
@@ -0,0 +1,37 @@
+{
+ "instance": {
+ "created": "2013-03-18T19:09:17",
+ "flavor": {
+ "id": "1",
+ "links": [
+ {
+ "href": "https://ord.databases.api.rackspacecloud.com/v1.0/1234/flavors/1",
+ "rel": "self"
+ },
+ {
+ "href": "https://ord.databases.api.rackspacecloud.com/flavors/1",
+ "rel": "bookmark"
+ }
+ ]
+ },
+ "hostname": "e09ad9a3f73309469cf1f43d11e79549caf9acf2.rackspaceclouddb.com",
+ "id": "44b277eb-39be-4921-be31-3d61b43651d7",
+ "links": [
+ {
+ "href": "https://ord.databases.api.rackspacecloud.com/v1.0/1234/instances/44b277eb-39be-4921-be31-3d61b43651d7",
+ "rel": "self"
+ },
+ {
+ "href": "https://ord.databases.api.rackspacecloud.com/instances/44b277eb-39be-4921-be31-3d61b43651d7",
+ "rel": "bookmark"
+ }
+ ],
+ "name": "json_rack_instance",
+ "status": "ERROR",
+ "updated": "2013-03-18T19:09:17",
+ "volume": {
+ "size": 2,
+ "used": 0.16368598397821188
+ }
+ }
+}
diff --git a/apis/openstack-trove/src/test/resources/instance_is_rooted.json b/apis/openstack-trove/src/test/resources/instance_is_rooted.json
new file mode 100644
index 0000000000..f3990b45ab
--- /dev/null
+++ b/apis/openstack-trove/src/test/resources/instance_is_rooted.json
@@ -0,0 +1,3 @@
+{
+ "rootEnabled": true
+}
diff --git a/apis/openstack-trove/src/test/resources/instance_is_rooted_false.json b/apis/openstack-trove/src/test/resources/instance_is_rooted_false.json
new file mode 100644
index 0000000000..04e4e6d44a
--- /dev/null
+++ b/apis/openstack-trove/src/test/resources/instance_is_rooted_false.json
@@ -0,0 +1,3 @@
+{
+ "rootEnabled": false
+}
diff --git a/apis/openstack-trove/src/test/resources/instance_list.json b/apis/openstack-trove/src/test/resources/instance_list.json
new file mode 100644
index 0000000000..c1834a2aad
--- /dev/null
+++ b/apis/openstack-trove/src/test/resources/instance_list.json
@@ -0,0 +1,66 @@
+{
+ "instances": [
+ {
+ "flavor": {
+ "id": "1",
+ "links": [
+ {
+ "href": "https://ord.databases.api.rackspacecloud.com/v1.0/1234/flavors/1",
+ "rel": "self"
+ },
+ {
+ "href": "https://ord.databases.api.rackspacecloud.com/flavors/1",
+ "rel": "bookmark"
+ }
+ ]
+ },
+ "id": "098653ba-218b-47ce-936a-e0b749101f81",
+ "links": [
+ {
+ "href": "https://ord.databases.api.rackspacecloud.com/v1.0/1234/instances/098653ba-218b-47ce-936a-e0b749101f81",
+ "rel": "self"
+ },
+ {
+ "href": "https://ord.databases.api.rackspacecloud.com/instances/098653ba-218b-47ce-936a-e0b749101f81",
+ "rel": "bookmark"
+ }
+ ],
+ "name": "xml_rack_instance",
+ "status": "ACTIVE",
+ "volume": {
+ "size": 2
+ }
+ },
+ {
+ "flavor": {
+ "id": "1",
+ "links": [
+ {
+ "href": "https://ord.databases.api.rackspacecloud.com/v1.0/1234/flavors/1",
+ "rel": "self"
+ },
+ {
+ "href": "https://ord.databases.api.rackspacecloud.com/flavors/1",
+ "rel": "bookmark"
+ }
+ ]
+ },
+ "id": "44b277eb-39be-4921-be31-3d61b43651d7",
+ "links": [
+ {
+ "href": "https://ord.databases.api.rackspacecloud.com/v1.0/1234/instances/44b277eb-39be-4921-be31-3d61b43651d7",
+ "rel": "self"
+ },
+ {
+ "href": "https://ord.databases.api.rackspacecloud.com/instances/44b277eb-39be-4921-be31-3d61b43651d7",
+ "rel": "bookmark"
+ }
+ ],
+ "name": "json_rack_instance",
+ "status": "ACTIVE",
+ "volume": {
+ "size": 2
+ }
+ }
+ ]
+}
diff --git a/apis/openstack-trove/src/test/resources/instance_root.json b/apis/openstack-trove/src/test/resources/instance_root.json
new file mode 100644
index 0000000000..e937b909d9
--- /dev/null
+++ b/apis/openstack-trove/src/test/resources/instance_root.json
@@ -0,0 +1,6 @@
+{
+ "user": {
+ "name": "root",
+ "password": "12345"
+ }
+}
diff --git a/apis/openstack-trove/src/test/resources/logback-test.xml b/apis/openstack-trove/src/test/resources/logback-test.xml
new file mode 100644
index 0000000000..6559c2351d
--- /dev/null
+++ b/apis/openstack-trove/src/test/resources/logback-test.xml
@@ -0,0 +1,69 @@
+
+
+
+
+ target/test-data/jclouds.log
+
+
+ %d %-5p [%c] [%thread] %m%n
+
+
+
+
+ target/test-data/jclouds-wire.log
+
+
+ %d %-5p [%c] [%thread] %m%n
+
+
+
+
+ target/test-data/jclouds-blobstore.log
+
+
+ %d %-5p [%c] [%thread] %m%n
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apis/openstack-trove/src/test/resources/logback.xml b/apis/openstack-trove/src/test/resources/logback.xml
new file mode 100644
index 0000000000..6559c2351d
--- /dev/null
+++ b/apis/openstack-trove/src/test/resources/logback.xml
@@ -0,0 +1,69 @@
+
+
+
+
+ target/test-data/jclouds.log
+
+
+ %d %-5p [%c] [%thread] %m%n
+
+
+
+
+ target/test-data/jclouds-wire.log
+
+
+ %d %-5p [%c] [%thread] %m%n
+
+
+
+
+ target/test-data/jclouds-blobstore.log
+
+
+ %d %-5p [%c] [%thread] %m%n
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apis/openstack-trove/src/test/resources/trove_user_list.json b/apis/openstack-trove/src/test/resources/trove_user_list.json
new file mode 100644
index 0000000000..546df439b8
--- /dev/null
+++ b/apis/openstack-trove/src/test/resources/trove_user_list.json
@@ -0,0 +1,35 @@
+{
+ "users": [
+ {
+ "databases": [],
+ "host": "%",
+ "name": "dbuser1"
+ },
+ {
+ "databases": [
+ {
+ "name": "databaseB"
+ },
+ {
+ "name": "databaseC"
+ }
+ ],
+ "host": "%",
+ "name": "dbuser2"
+ },
+ {
+ "databases": [],
+ "name": "dbuser3",
+ "host": "%"
+ },
+ {
+ "databases": [
+ {
+ "name": "sampledb"
+ }
+ ],
+ "host": "%",
+ "name": "demouser"
+ }
+ ]
+}
diff --git a/apis/openstack-trove/src/test/resources/user_create_request.json b/apis/openstack-trove/src/test/resources/user_create_request.json
new file mode 100644
index 0000000000..3837b8b1c6
--- /dev/null
+++ b/apis/openstack-trove/src/test/resources/user_create_request.json
@@ -0,0 +1,35 @@
+{
+ "users":[
+ {
+ "databases":[
+ {
+ "name":"databaseA"
+ }
+ ],
+ "name":"dbuser1",
+ "password":"password"
+ },
+ {
+ "databases":[
+ {
+ "name":"databaseB"
+ },
+ {
+ "name":"databaseC"
+ }
+ ],
+ "name":"dbuser2",
+ "password":"password"
+ },
+ {
+ "databases":[
+ {
+ "name":"databaseD"
+ }
+ ],
+ "name":"dbuser3",
+ "host":"192.168.64.64",
+ "password":"password"
+ }
+ ]
+}
diff --git a/apis/openstack-trove/src/test/resources/user_create_simple_request.json b/apis/openstack-trove/src/test/resources/user_create_simple_request.json
new file mode 100644
index 0000000000..e28d0d7529
--- /dev/null
+++ b/apis/openstack-trove/src/test/resources/user_create_simple_request.json
@@ -0,0 +1,13 @@
+{
+ "users":[
+ {
+ "databases":[
+ {
+ "name":"databaseA"
+ }
+ ],
+ "name":"dbuser1",
+ "password":"password"
+ }
+ ]
+}
diff --git a/apis/openstack-trove/src/test/resources/user_create_with_host_simple_request.json b/apis/openstack-trove/src/test/resources/user_create_with_host_simple_request.json
new file mode 100644
index 0000000000..0449f49216
--- /dev/null
+++ b/apis/openstack-trove/src/test/resources/user_create_with_host_simple_request.json
@@ -0,0 +1,14 @@
+{
+ "users":[
+ {
+ "databases":[
+ {
+ "name":"databaseA"
+ }
+ ],
+ "name":"dbuser1",
+ "password":"password",
+ "host":"192.168.64.64"
+ }
+ ]
+}
diff --git a/apis/openstack-trove/src/test/resources/user_get.json b/apis/openstack-trove/src/test/resources/user_get.json
new file mode 100644
index 0000000000..478a94d94f
--- /dev/null
+++ b/apis/openstack-trove/src/test/resources/user_get.json
@@ -0,0 +1,14 @@
+{
+ "user": {
+ "name": "exampleuser",
+ "host": "%",
+ "databases": [
+ {
+ "name": "databaseA"
+ },
+ {
+ "name": "databaseB"
+ }
+ ]
+ }
+}
diff --git a/apis/openstack-trove/src/test/resources/user_get_withhost.json b/apis/openstack-trove/src/test/resources/user_get_withhost.json
new file mode 100644
index 0000000000..2827568f95
--- /dev/null
+++ b/apis/openstack-trove/src/test/resources/user_get_withhost.json
@@ -0,0 +1,14 @@
+{
+ "user": {
+ "name": "example.user",
+ "host": "192.168.64.64",
+ "databases": [
+ {
+ "name": "databaseA"
+ },
+ {
+ "name": "databaseB"
+ }
+ ]
+ }
+}
diff --git a/apis/openstack-trove/src/test/resources/user_grant_request.json b/apis/openstack-trove/src/test/resources/user_grant_request.json
new file mode 100644
index 0000000000..97a1239365
--- /dev/null
+++ b/apis/openstack-trove/src/test/resources/user_grant_request.json
@@ -0,0 +1,10 @@
+{
+ "databases": [
+ {
+ "name": "databaseC"
+ },
+ {
+ "name": "databaseD"
+ }
+ ]
+}
diff --git a/apis/openstack-trove/src/test/resources/user_grant_simple_request.json b/apis/openstack-trove/src/test/resources/user_grant_simple_request.json
new file mode 100644
index 0000000000..5113e4f926
--- /dev/null
+++ b/apis/openstack-trove/src/test/resources/user_grant_simple_request.json
@@ -0,0 +1,7 @@
+{
+ "databases": [
+ {
+ "name": "databaseZ"
+ }
+ ]
+}
diff --git a/apis/openstack-trove/src/test/resources/user_list_access.json b/apis/openstack-trove/src/test/resources/user_list_access.json
new file mode 100644
index 0000000000..c58e1fa92d
--- /dev/null
+++ b/apis/openstack-trove/src/test/resources/user_list_access.json
@@ -0,0 +1,10 @@
+{
+ "databases": [
+ {
+ "name": "databaseA"
+ },
+ {
+ "name": "databaseB"
+ }
+ ]
+}
diff --git a/apis/openstack-trove/src/test/resources/user_password_request.json b/apis/openstack-trove/src/test/resources/user_password_request.json
new file mode 100644
index 0000000000..e42b277dc1
--- /dev/null
+++ b/apis/openstack-trove/src/test/resources/user_password_request.json
@@ -0,0 +1,12 @@
+{
+ "users": [
+ {
+ "name": "dbuser1",
+ "password": "newpassword"
+ },
+ {
+ "name": "dbuser2",
+ "password": "anotherpassword"
+ }
+ ]
+}
diff --git a/apis/pom.xml b/apis/pom.xml
index 6a1ac22f55..4047677252 100644
--- a/apis/pom.xml
+++ b/apis/pom.xml
@@ -46,6 +46,7 @@
openstack-cinder
openstack-nova
openstack-nova-ec2
+ openstack-trove
cloudfiles
cloudservers
rackspace-cloudidentity
diff --git a/providers/pom.xml b/providers/pom.xml
index 320aa88fc8..d46571f23b 100644
--- a/providers/pom.xml
+++ b/providers/pom.xml
@@ -61,6 +61,8 @@
cloudfiles-uk
rackspace-clouddns-us
rackspace-clouddns-uk
+ rackspace-clouddatabases-us
+ rackspace-clouddatabases-uk
rackspace-cloudloadbalancers-us
rackspace-cloudloadbalancers-uk
rackspace-cloudservers-us
diff --git a/providers/rackspace-clouddatabases-uk/README.md b/providers/rackspace-clouddatabases-uk/README.md
new file mode 100644
index 0000000000..9357d9aec1
--- /dev/null
+++ b/providers/rackspace-clouddatabases-uk/README.md
@@ -0,0 +1,7 @@
+Rackspace Cloud Databases UK
+============================
+
+The Rackspace deployment of OpenStack Trove, the Database as a Service.
+
+Production ready?
+Yes
diff --git a/providers/rackspace-clouddatabases-uk/pom.xml b/providers/rackspace-clouddatabases-uk/pom.xml
new file mode 100644
index 0000000000..f3e50a712d
--- /dev/null
+++ b/providers/rackspace-clouddatabases-uk/pom.xml
@@ -0,0 +1,148 @@
+
+
+
+ 4.0.0
+
+ org.apache.jclouds
+ jclouds-project
+ 1.7.0-SNAPSHOT
+ ../../project/pom.xml
+
+ org.apache.jclouds.provider
+ rackspace-clouddatabases-uk
+ jclouds Rackspace Cloud Databases UK provider
+ OpenStack Trove implementation targeted to Rackspace Cloud Databases UK
+ bundle
+
+
+ https://identity.api.rackspacecloud.com/v2.0/
+ 1
+
+ ${test.rackspace-uk.identity}
+ ${test.rackspace-uk.credential}
+
+ org.jclouds.rackspace.clouddatabases.uk*;version="${project.version}"
+
+ org.jclouds.compute.internal;version="${project.version}",
+ org.jclouds.rest.internal;version="${project.version}",
+ org.jclouds*;version="${project.version}",
+ *
+
+
+
+
+
+ org.apache.jclouds
+ jclouds-core
+ ${project.version}
+
+
+ org.apache.jclouds.labs
+ openstack-trove
+ ${project.version}
+
+
+ org.apache.jclouds.api
+ openstack-keystone
+ ${project.version}
+
+
+ org.apache.jclouds.api
+ rackspace-cloudidentity
+ ${project.version}
+
+
+ org.apache.jclouds
+ jclouds-core
+ ${project.version}
+ test-jar
+ test
+
+
+ org.apache.jclouds.labs
+ openstack-trove
+ ${project.version}
+ test-jar
+ test
+
+
+ org.apache.jclouds.api
+ openstack-keystone
+ ${project.version}
+ test-jar
+ test
+
+
+ org.apache.jclouds.api
+ rackspace-cloudidentity
+ ${project.version}
+ test-jar
+ test
+
+
+ org.apache.jclouds.driver
+ jclouds-slf4j
+ ${project.version}
+ test
+
+
+ ch.qos.logback
+ logback-classic
+ test
+
+
+
+
+
+ live
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+
+ integration
+ integration-test
+
+ test
+
+
+ 5
+ true
+ classes
+
+ ${test.rackspace-clouddatabases-uk.endpoint}
+ ${test.rackspace-clouddatabases-uk.api-version}
+ ${test.rackspace-clouddatabases-uk.build-version}
+ ${test.rackspace-clouddatabases-uk.identity}
+ ${test.rackspace-clouddatabases-uk.credential}
+ ${test.rackspace-clouddatabases-uk.template}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/providers/rackspace-clouddatabases-uk/src/main/java/org/jclouds/rackspace/clouddatabases/uk/CloudDatabasesUKProviderMetadata.java b/providers/rackspace-clouddatabases-uk/src/main/java/org/jclouds/rackspace/clouddatabases/uk/CloudDatabasesUKProviderMetadata.java
new file mode 100644
index 0000000000..f6cfaa03cc
--- /dev/null
+++ b/providers/rackspace-clouddatabases-uk/src/main/java/org/jclouds/rackspace/clouddatabases/uk/CloudDatabasesUKProviderMetadata.java
@@ -0,0 +1,112 @@
+/*
+ * 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.rackspace.clouddatabases.uk;
+
+import static org.jclouds.location.reference.LocationConstants.ISO3166_CODES;
+import static org.jclouds.location.reference.LocationConstants.PROPERTY_ZONE;
+import static org.jclouds.location.reference.LocationConstants.PROPERTY_ZONES;
+import static org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties.CREDENTIAL_TYPE;
+import static org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties.SERVICE_TYPE;
+
+import java.net.URI;
+import java.util.Properties;
+
+import org.jclouds.openstack.keystone.v2_0.config.KeystoneAuthenticationModule.ZoneModule;
+import org.jclouds.openstack.trove.v1.TroveApiMetadata;
+import org.jclouds.openstack.trove.v1.config.TroveHttpApiModule;
+import org.jclouds.openstack.trove.v1.config.TroveParserModule;
+import org.jclouds.providers.ProviderMetadata;
+import org.jclouds.providers.internal.BaseProviderMetadata;
+import org.jclouds.rackspace.cloudidentity.v2_0.config.CloudIdentityAuthenticationApiModule;
+import org.jclouds.rackspace.cloudidentity.v2_0.config.CloudIdentityAuthenticationModule;
+import org.jclouds.rackspace.cloudidentity.v2_0.config.CloudIdentityCredentialTypes;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.inject.Module;
+
+/**
+ * Implementation of {@link org.jclouds.types.ProviderMetadata} for Rackspace Cloud Databases.
+ *
+ * @author Zack Shoylev
+ */
+public class CloudDatabasesUKProviderMetadata extends BaseProviderMetadata {
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ public Builder toBuilder() {
+ return builder().fromProviderMetadata(this);
+ }
+
+ public CloudDatabasesUKProviderMetadata() {
+ super(builder());
+ }
+
+ public CloudDatabasesUKProviderMetadata(Builder builder) {
+ super(builder);
+ }
+
+ public static Properties defaultProperties() {
+ Properties properties = new Properties();
+ properties.setProperty(CREDENTIAL_TYPE, CloudIdentityCredentialTypes.API_KEY_CREDENTIALS);
+ properties.setProperty(SERVICE_TYPE, "rax:database");
+ properties.setProperty(PROPERTY_ZONES, "LON");
+ properties.setProperty(PROPERTY_ZONE + ".LON." + ISO3166_CODES, "GB-SLG");
+ return properties;
+ }
+
+ public static class Builder extends BaseProviderMetadata.Builder {
+
+ protected Builder(){
+ id("rackspace-clouddatabases-uk")
+ .name("Rackspace Clouddatabases UK")
+ .apiMetadata(new TroveApiMetadata().toBuilder()
+ .identityName("${userName}")
+ .credentialName("${apiKey}")
+ .defaultEndpoint("https://lon.identity.api.rackspacecloud.com/v2.0")
+ .endpointName("identity service url ending in /v2.0/")
+ .documentation(URI.create("http://docs.rackspace.com/cbs/api/v1.0/cbs-devguide/content/overview.html"))
+ .defaultModules(ImmutableSet.>builder()
+ .add(CloudIdentityAuthenticationApiModule.class)
+ .add(CloudIdentityAuthenticationModule.class)
+ .add(ZoneModule.class)
+ .add(TroveParserModule.class)
+ .add(TroveHttpApiModule.class).build())
+ .build())
+ .homepage(URI.create("http://www.rackspace.com/cloud/public/databases/"))
+ .console(URI.create("https://mycloud.rackspace.com"))
+ .linkedServices("rackspace-cloudservers-uk", "cloudfiles-uk")
+ .iso3166Codes("GB-SLG")
+ .endpoint("https://lon.identity.api.rackspacecloud.com/v2.0")
+ .defaultProperties(CloudDatabasesUKProviderMetadata.defaultProperties());
+ }
+
+ @Override
+ public CloudDatabasesUKProviderMetadata build() {
+ return new CloudDatabasesUKProviderMetadata(this);
+ }
+
+ @Override
+ public Builder fromProviderMetadata(ProviderMetadata in) {
+ super.fromProviderMetadata(in);
+ return this;
+ }
+ }
+
+}
diff --git a/providers/rackspace-clouddatabases-uk/src/main/resources/META-INF/services/org.jclouds.providers.ProviderMetadata b/providers/rackspace-clouddatabases-uk/src/main/resources/META-INF/services/org.jclouds.providers.ProviderMetadata
new file mode 100644
index 0000000000..a6a3c9a986
--- /dev/null
+++ b/providers/rackspace-clouddatabases-uk/src/main/resources/META-INF/services/org.jclouds.providers.ProviderMetadata
@@ -0,0 +1 @@
+org.jclouds.rackspace.clouddatabases.uk.CloudDatabasesUKProviderMetadata
diff --git a/providers/rackspace-clouddatabases-uk/src/test/java/org/jclouds/rackspace/clouddatabases/uk/CloudDatabasesUKProviderMetadataExpectTest.java b/providers/rackspace-clouddatabases-uk/src/test/java/org/jclouds/rackspace/clouddatabases/uk/CloudDatabasesUKProviderMetadataExpectTest.java
new file mode 100644
index 0000000000..d0f7143d10
--- /dev/null
+++ b/providers/rackspace-clouddatabases-uk/src/test/java/org/jclouds/rackspace/clouddatabases/uk/CloudDatabasesUKProviderMetadataExpectTest.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.rackspace.clouddatabases.uk;
+
+import static org.testng.Assert.assertEquals;
+
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.openstack.trove.v1.TroveApi;
+import org.jclouds.openstack.trove.v1.internal.BaseTroveApiExpectTest;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * This test ensures that the wiring in {@link CloudDatabasesUKProviderMetadata} is correct.
+ *
+ * @author Zack Shoylev
+ */
+@Test(groups = "unit", testName = "CloudDatabasesUKProviderMetadataExpectTest")
+public class CloudDatabasesUKProviderMetadataExpectTest extends BaseTroveApiExpectTest {
+
+ public CloudDatabasesUKProviderMetadataExpectTest() {
+ this.provider = "rackspace-clouddatabases-uk";
+ this.identity = "myUsername";
+ this.credential = "myApiKey";
+ }
+
+ public void testCanGetConfiguredZones() {
+
+ HttpRequest authenticate = HttpRequest.builder().method("POST")
+ .endpoint("https://lon.identity.api.rackspacecloud.com/v2.0/tokens")
+ .addHeader("Accept", "application/json")
+ .payload(payloadFromStringWithContentType(
+ "{\"auth\":{\"RAX-KSKEY:apiKeyCredentials\":{\"username\":\"myUsername\",\"apiKey\":\"myApiKey\"}}}"
+ , "application/json")).build();
+
+
+ HttpResponse authenticationResponse = HttpResponse.builder()
+ .statusCode(200)
+ .payload(payloadFromResourceWithContentType("/access_rax_uk.json", "application/json"))
+ .build();
+
+ TroveApi whenNovaRegionExists = requestSendsResponse(authenticate, authenticationResponse);
+
+ assertEquals(whenNovaRegionExists.getConfiguredZones(), ImmutableSet.of("LON"));
+
+ }
+
+}
diff --git a/providers/rackspace-clouddatabases-uk/src/test/java/org/jclouds/rackspace/clouddatabases/uk/CloudDatabasesUKProviderTest.java b/providers/rackspace-clouddatabases-uk/src/test/java/org/jclouds/rackspace/clouddatabases/uk/CloudDatabasesUKProviderTest.java
new file mode 100644
index 0000000000..a4bb2c9aed
--- /dev/null
+++ b/providers/rackspace-clouddatabases-uk/src/test/java/org/jclouds/rackspace/clouddatabases/uk/CloudDatabasesUKProviderTest.java
@@ -0,0 +1,32 @@
+/*
+ * 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.rackspace.clouddatabases.uk;
+
+import org.jclouds.openstack.trove.v1.TroveApiMetadata;
+import org.jclouds.providers.internal.BaseProviderMetadataTest;
+import org.testng.annotations.Test;
+
+/**
+ * @author Zack Shoylev
+ */
+@Test(groups = "unit", testName = "CloudDatabasesUKProviderTest")
+public class CloudDatabasesUKProviderTest extends BaseProviderMetadataTest {
+
+ public CloudDatabasesUKProviderTest() {
+ super(new CloudDatabasesUKProviderMetadata(), new TroveApiMetadata());
+ }
+}
diff --git a/providers/rackspace-clouddatabases-uk/src/test/java/org/jclouds/rackspace/clouddatabases/uk/features/CloudDatabasesUKDatabaseApiLiveTest.java b/providers/rackspace-clouddatabases-uk/src/test/java/org/jclouds/rackspace/clouddatabases/uk/features/CloudDatabasesUKDatabaseApiLiveTest.java
new file mode 100644
index 0000000000..294e0218a5
--- /dev/null
+++ b/providers/rackspace-clouddatabases-uk/src/test/java/org/jclouds/rackspace/clouddatabases/uk/features/CloudDatabasesUKDatabaseApiLiveTest.java
@@ -0,0 +1,30 @@
+/*
+ * 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.rackspace.clouddatabases.uk.features;
+
+import org.jclouds.openstack.trove.v1.features.DatabaseApiLiveTest;
+import org.testng.annotations.Test;
+
+/**
+ * @author Zack Shoylev
+ */
+@Test(groups = "live", testName = "CloudDatabasesUKDatabaseApiLiveTest")
+public class CloudDatabasesUKDatabaseApiLiveTest extends DatabaseApiLiveTest {
+ public CloudDatabasesUKDatabaseApiLiveTest() {
+ provider = "rackspace-clouddatabases-uk";
+ }
+}
diff --git a/providers/rackspace-clouddatabases-uk/src/test/java/org/jclouds/rackspace/clouddatabases/uk/features/CloudDatabasesUKFlavorApiLiveTest.java b/providers/rackspace-clouddatabases-uk/src/test/java/org/jclouds/rackspace/clouddatabases/uk/features/CloudDatabasesUKFlavorApiLiveTest.java
new file mode 100644
index 0000000000..75d0f4fc90
--- /dev/null
+++ b/providers/rackspace-clouddatabases-uk/src/test/java/org/jclouds/rackspace/clouddatabases/uk/features/CloudDatabasesUKFlavorApiLiveTest.java
@@ -0,0 +1,30 @@
+/*
+ * 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.rackspace.clouddatabases.uk.features;
+
+import org.jclouds.openstack.trove.v1.features.FlavorApiLiveTest;
+import org.testng.annotations.Test;
+
+/**
+ * @author Zack Shoylev
+ */
+@Test(groups = "live", testName = "CloudDatabasesUKFlavorApiLiveTest")
+public class CloudDatabasesUKFlavorApiLiveTest extends FlavorApiLiveTest {
+ public CloudDatabasesUKFlavorApiLiveTest() {
+ provider = "rackspace-clouddatabases-uk";
+ }
+}
diff --git a/providers/rackspace-clouddatabases-uk/src/test/java/org/jclouds/rackspace/clouddatabases/uk/features/CloudDatabasesUKInstanceApiLiveTest.java b/providers/rackspace-clouddatabases-uk/src/test/java/org/jclouds/rackspace/clouddatabases/uk/features/CloudDatabasesUKInstanceApiLiveTest.java
new file mode 100644
index 0000000000..7419e18803
--- /dev/null
+++ b/providers/rackspace-clouddatabases-uk/src/test/java/org/jclouds/rackspace/clouddatabases/uk/features/CloudDatabasesUKInstanceApiLiveTest.java
@@ -0,0 +1,30 @@
+/*
+ * 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.rackspace.clouddatabases.uk.features;
+
+import org.jclouds.openstack.trove.v1.features.InstanceApiLiveTest;
+import org.testng.annotations.Test;
+
+/**
+ * @author Zack Shoylev
+ */
+@Test(groups = "live", testName = "CloudDatabasesUKInstanceApiLiveTest")
+public class CloudDatabasesUKInstanceApiLiveTest extends InstanceApiLiveTest {
+ public CloudDatabasesUKInstanceApiLiveTest() {
+ provider = "rackspace-clouddatabases-uk";
+ }
+}
diff --git a/providers/rackspace-clouddatabases-uk/src/test/java/org/jclouds/rackspace/clouddatabases/uk/features/CloudDatabasesUKUserApiLiveTest.java b/providers/rackspace-clouddatabases-uk/src/test/java/org/jclouds/rackspace/clouddatabases/uk/features/CloudDatabasesUKUserApiLiveTest.java
new file mode 100644
index 0000000000..afde455b43
--- /dev/null
+++ b/providers/rackspace-clouddatabases-uk/src/test/java/org/jclouds/rackspace/clouddatabases/uk/features/CloudDatabasesUKUserApiLiveTest.java
@@ -0,0 +1,30 @@
+/*
+ * 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.rackspace.clouddatabases.uk.features;
+
+import org.jclouds.openstack.trove.v1.features.UserApiLiveTest;
+import org.testng.annotations.Test;
+
+/**
+ * @author Zack Shoylev
+ */
+@Test(groups = "live", testName = "CloudDatabasesUKUserApiLiveTest")
+public class CloudDatabasesUKUserApiLiveTest extends UserApiLiveTest {
+ public CloudDatabasesUKUserApiLiveTest() {
+ provider = "rackspace-clouddatabases-uk";
+ }
+}
diff --git a/providers/rackspace-clouddatabases-uk/src/test/resources/access_rax_uk.json b/providers/rackspace-clouddatabases-uk/src/test/resources/access_rax_uk.json
new file mode 100644
index 0000000000..3a94f6c9fa
--- /dev/null
+++ b/providers/rackspace-clouddatabases-uk/src/test/resources/access_rax_uk.json
@@ -0,0 +1,129 @@
+{
+ "access": {
+ "token": {
+ "id": "bdd18214-e266-4ad3-b985-d9bfb22c8da8",
+ "expires": "2012-10-01T02:11:16.000+01:00",
+ "tenant": {
+ "id": "10001786",
+ "name": "10001786"
+ }
+ },
+ "serviceCatalog": [
+ {
+ "endpoints": [
+ {
+ "region": "LON",
+ "tenantId": "MossoCloudFS_83a9d536-2e25-4166-bd3b-a503a934f953",
+ "publicURL": "https://storage101.lon3.clouddrive.com/v1/MossoCloudFS_83a9d536-2e25-4166-bd3b-a503a934f953",
+ "internalURL": "https://snet-storage101.lon3.clouddrive.com/v1/MossoCloudFS_83a9d536-2e25-4166-bd3b-a503a934f953"
+ }
+ ],
+ "name": "cloudFiles",
+ "type": "object-store"
+ },
+ {
+ "endpoints": [
+ {
+ "region": "LON",
+ "tenantId": "MossoCloudFS_83a9d536-2e25-4166-bd3b-a503a934f953",
+ "publicURL": "https://cdn3.clouddrive.com/v1/MossoCloudFS_83a9d536-2e25-4166-bd3b-a503a934f953"
+ }
+ ],
+ "name": "cloudFilesCDN",
+ "type": "rax:object-cdn"
+ },
+ {
+ "endpoints": [
+ {
+ "tenantId": "10001786",
+ "publicURL": "https://lon.servers.api.rackspacecloud.com/v1.0/10001786",
+ "versionInfo": "https://lon.servers.api.rackspacecloud.com/v1.0",
+ "versionList": "https://lon.servers.api.rackspacecloud.com/",
+ "versionId": "1.0"
+ }
+ ],
+ "name": "cloudServers",
+ "type": "compute"
+ },
+ {
+ "endpoints": [
+ {
+ "tenantId": "10001786",
+ "publicURL": "https://lon.dns.api.rackspacecloud.com/v1.0/10001786"
+ }
+ ],
+ "name": "cloudDNS",
+ "type": "rax:dns"
+ },
+ {
+ "endpoints": [
+ {
+ "region": "LON",
+ "tenantId": "10001786",
+ "publicURL": "https://lon.loadbalancers.api.rackspacecloud.com/v1.0/10001786"
+ }
+ ],
+ "name": "cloudLoadBalancers",
+ "type": "rax:load-balancer"
+ },
+ {
+ "endpoints": [
+ {
+ "tenantId": "10001786",
+ "publicURL": "https://monitoring.api.rackspacecloud.com/v1.0/10001786"
+ }
+ ],
+ "name": "cloudMonitoring",
+ "type": "rax:monitor"
+ },
+ {
+ "endpoints": [
+ {
+ "region": "LON",
+ "tenantId": "10001786",
+ "publicURL": "https://lon.databases.api.rackspacecloud.com/v1.0/10001786"
+ }
+ ],
+ "name": "cloudDatabases",
+ "type": "rax:database"
+ },
+ {
+ "endpoints": [
+ {
+ "region": "LON",
+ "tenantId": "10001786",
+ "publicURL": "https://lon.servers.api.rackspacecloud.com/v2/10001786",
+ "versionInfo": "https://lon.servers.api.rackspacecloud.com/v2",
+ "versionList": "https://lon.servers.api.rackspacecloud.com/",
+ "versionId": "2"
+ }
+ ],
+ "name": "cloudServersOpenStack",
+ "type": "compute"
+ },
+ {
+ "endpoints": [
+ {
+ "publicURL": "https://lon.blockstorage.api.rackspacecloud.com/v1/10001786",
+ "tenantId": "10001786",
+ "region": "LON"
+ }
+ ],
+ "name": "cloudBlockStorage",
+ "type": "volume"
+ }
+ ],
+ "user": {
+ "id": "378",
+ "roles": [
+ {
+ "id": "3",
+ "description": "User Admin Role.",
+ "name": "identity:user-admin"
+ }
+ ],
+ "name": "jclouds",
+ "RAX-AUTH:defaultRegion": ""
+ }
+ }
+}
diff --git a/providers/rackspace-clouddatabases-uk/src/test/resources/logback.xml b/providers/rackspace-clouddatabases-uk/src/test/resources/logback.xml
new file mode 100644
index 0000000000..6559c2351d
--- /dev/null
+++ b/providers/rackspace-clouddatabases-uk/src/test/resources/logback.xml
@@ -0,0 +1,69 @@
+
+
+
+
+ target/test-data/jclouds.log
+
+
+ %d %-5p [%c] [%thread] %m%n
+
+
+
+
+ target/test-data/jclouds-wire.log
+
+
+ %d %-5p [%c] [%thread] %m%n
+
+
+
+
+ target/test-data/jclouds-blobstore.log
+
+
+ %d %-5p [%c] [%thread] %m%n
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/providers/rackspace-clouddatabases-us/README.md b/providers/rackspace-clouddatabases-us/README.md
new file mode 100644
index 0000000000..b3eabc164b
--- /dev/null
+++ b/providers/rackspace-clouddatabases-us/README.md
@@ -0,0 +1,7 @@
+Rackspace Cloud Databases US
+============================
+
+The Rackspace deployment of OpenStack Trove, the Database as a Service.
+
+Production ready?
+Yes
diff --git a/providers/rackspace-clouddatabases-us/pom.xml b/providers/rackspace-clouddatabases-us/pom.xml
new file mode 100644
index 0000000000..5363572c61
--- /dev/null
+++ b/providers/rackspace-clouddatabases-us/pom.xml
@@ -0,0 +1,148 @@
+
+
+
+ 4.0.0
+
+ org.apache.jclouds
+ jclouds-project
+ 1.7.0-SNAPSHOT
+ ../../project/pom.xml
+
+ org.apache.jclouds.provider
+ rackspace-clouddatabases-us
+ jclouds Rackspace Cloud Databases US provider
+ OpenStack Trove implementation targeted to Rackspace Cloud Databases US
+ bundle
+
+
+ https://identity.api.rackspacecloud.com/v2.0/
+ 1
+
+ ${test.rackspace-us.identity}
+ ${test.rackspace-us.credential}
+
+ org.jclouds.rackspace.clouddatabases.us*;version="${project.version}"
+
+ org.jclouds.compute.internal;version="${project.version}",
+ org.jclouds.rest.internal;version="${project.version}",
+ org.jclouds*;version="${project.version}",
+ *
+
+
+
+
+
+ org.apache.jclouds
+ jclouds-core
+ ${project.version}
+
+
+ org.apache.jclouds.labs
+ openstack-trove
+ ${project.version}
+
+
+ org.apache.jclouds.api
+ openstack-keystone
+ ${project.version}
+
+
+ org.apache.jclouds.api
+ rackspace-cloudidentity
+ ${project.version}
+
+
+ org.apache.jclouds
+ jclouds-core
+ ${project.version}
+ test-jar
+ test
+
+
+ org.apache.jclouds.labs
+ openstack-trove
+ ${project.version}
+ test-jar
+ test
+
+
+ org.apache.jclouds.api
+ openstack-keystone
+ ${project.version}
+ test-jar
+ test
+
+
+ org.apache.jclouds.api
+ rackspace-cloudidentity
+ ${project.version}
+ test-jar
+ test
+
+
+ org.apache.jclouds.driver
+ jclouds-slf4j
+ ${project.version}
+ test
+
+
+ ch.qos.logback
+ logback-classic
+ test
+
+
+
+
+
+ live
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+
+ integration
+ integration-test
+
+ test
+
+
+ 5
+ true
+ classes
+
+ ${test.rackspace-clouddatabases-us.endpoint}
+ ${test.rackspace-clouddatabases-us.api-version}
+ ${test.rackspace-clouddatabases-us.build-version}
+ ${test.rackspace-clouddatabases-us.identity}
+ ${test.rackspace-clouddatabases-us.credential}
+ ${test.rackspace-clouddatabases-us.template}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/providers/rackspace-clouddatabases-us/src/main/java/org/jclouds/rackspace/clouddatabases/us/CloudDatabasesUSProviderMetadata.java b/providers/rackspace-clouddatabases-us/src/main/java/org/jclouds/rackspace/clouddatabases/us/CloudDatabasesUSProviderMetadata.java
new file mode 100644
index 0000000000..fc024ed606
--- /dev/null
+++ b/providers/rackspace-clouddatabases-us/src/main/java/org/jclouds/rackspace/clouddatabases/us/CloudDatabasesUSProviderMetadata.java
@@ -0,0 +1,116 @@
+/*
+ * 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.rackspace.clouddatabases.us;
+
+import static org.jclouds.location.reference.LocationConstants.ISO3166_CODES;
+import static org.jclouds.location.reference.LocationConstants.PROPERTY_ZONE;
+import static org.jclouds.location.reference.LocationConstants.PROPERTY_ZONES;
+import static org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties.CREDENTIAL_TYPE;
+import static org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties.SERVICE_TYPE;
+
+import java.net.URI;
+import java.util.Properties;
+
+import org.jclouds.openstack.trove.v1.TroveApiMetadata;
+import org.jclouds.openstack.trove.v1.config.TroveParserModule;
+import org.jclouds.openstack.trove.v1.config.TroveHttpApiModule;
+import org.jclouds.openstack.keystone.v2_0.config.KeystoneAuthenticationModule.ZoneModule;
+import org.jclouds.providers.ProviderMetadata;
+import org.jclouds.providers.internal.BaseProviderMetadata;
+import org.jclouds.rackspace.cloudidentity.v2_0.config.CloudIdentityAuthenticationModule;
+import org.jclouds.rackspace.cloudidentity.v2_0.config.CloudIdentityCredentialTypes;
+import org.jclouds.rackspace.cloudidentity.v2_0.config.CloudIdentityAuthenticationApiModule;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.inject.Module;
+
+/**
+ * Implementation of {@link org.jclouds.types.ProviderMetadata} for Rackspace Cloud Databases.
+ *
+ * @author Zack Shoylev
+ */
+public class CloudDatabasesUSProviderMetadata extends BaseProviderMetadata {
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ public Builder toBuilder() {
+ return builder().fromProviderMetadata(this);
+ }
+
+ public CloudDatabasesUSProviderMetadata() {
+ super(builder());
+ }
+
+ public CloudDatabasesUSProviderMetadata(Builder builder) {
+ super(builder);
+ }
+
+ public static Properties defaultProperties() {
+ Properties properties = new Properties();
+ properties.setProperty(CREDENTIAL_TYPE, CloudIdentityCredentialTypes.API_KEY_CREDENTIALS);
+ properties.setProperty(SERVICE_TYPE, "rax:database");
+ properties.setProperty(PROPERTY_ZONES, "ORD,DFW,IAD,SYD,HKG");
+ properties.setProperty(PROPERTY_ZONE + ".ORD." + ISO3166_CODES, "US-IL");
+ properties.setProperty(PROPERTY_ZONE + ".DFW." + ISO3166_CODES, "US-TX");
+ properties.setProperty(PROPERTY_ZONE + ".IAD." + ISO3166_CODES, "US-VA");
+ properties.setProperty(PROPERTY_ZONE + ".SYD." + ISO3166_CODES, "AU-NSW");
+ properties.setProperty(PROPERTY_ZONE + ".HKG." + ISO3166_CODES, "HK");
+ return properties;
+ }
+
+ public static class Builder extends BaseProviderMetadata.Builder {
+
+ protected Builder(){
+ id("rackspace-clouddatabases-us")
+ .name("Rackspace Clouddatabases US")
+ .apiMetadata(new TroveApiMetadata().toBuilder()
+ .identityName("${userName}")
+ .credentialName("${apiKey}")
+ .defaultEndpoint("https://identity.api.rackspacecloud.com/v2.0/")
+ .endpointName("identity service url ending in /v2.0/")
+ .documentation(URI.create("http://docs.rackspace.com/cbs/api/v1.0/cbs-devguide/content/overview.html"))
+ .defaultModules(ImmutableSet.>builder()
+ .add(CloudIdentityAuthenticationApiModule.class)
+ .add(CloudIdentityAuthenticationModule.class)
+ .add(ZoneModule.class)
+ .add(TroveParserModule.class)
+ .add(TroveHttpApiModule.class).build())
+ .build())
+ .homepage(URI.create("http://www.rackspace.com/cloud/public/databases/"))
+ .console(URI.create("https://mycloud.rackspace.com"))
+ .linkedServices("rackspace-cloudservers-us", "cloudfiles-us")
+ .iso3166Codes("US-IL", "US-TX")
+ .endpoint("https://identity.api.rackspacecloud.com/v2.0/")
+ .defaultProperties(CloudDatabasesUSProviderMetadata.defaultProperties());
+ }
+
+ @Override
+ public CloudDatabasesUSProviderMetadata build() {
+ return new CloudDatabasesUSProviderMetadata(this);
+ }
+
+ @Override
+ public Builder fromProviderMetadata(ProviderMetadata in) {
+ super.fromProviderMetadata(in);
+ return this;
+ }
+ }
+
+}
diff --git a/providers/rackspace-clouddatabases-us/src/main/resources/META-INF/services/org.jclouds.providers.ProviderMetadata b/providers/rackspace-clouddatabases-us/src/main/resources/META-INF/services/org.jclouds.providers.ProviderMetadata
new file mode 100644
index 0000000000..607e501c3a
--- /dev/null
+++ b/providers/rackspace-clouddatabases-us/src/main/resources/META-INF/services/org.jclouds.providers.ProviderMetadata
@@ -0,0 +1 @@
+org.jclouds.rackspace.clouddatabases.us.CloudDatabasesUSProviderMetadata
diff --git a/providers/rackspace-clouddatabases-us/src/test/java/org/jclouds/rackspace/clouddatabases/us/CloudDatabasesUSProviderMetadataExpectTest.java b/providers/rackspace-clouddatabases-us/src/test/java/org/jclouds/rackspace/clouddatabases/us/CloudDatabasesUSProviderMetadataExpectTest.java
new file mode 100644
index 0000000000..ac49b61fef
--- /dev/null
+++ b/providers/rackspace-clouddatabases-us/src/test/java/org/jclouds/rackspace/clouddatabases/us/CloudDatabasesUSProviderMetadataExpectTest.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.rackspace.clouddatabases.us;
+
+import static org.testng.Assert.assertEquals;
+
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.openstack.trove.v1.TroveApi;
+import org.jclouds.openstack.trove.v1.internal.BaseTroveApiExpectTest;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * This test ensures that the wiring in {@link CloudDatabasesUSProviderMetadata} is correct.
+ *
+ * @author Zack Shoylev
+ */
+@Test(groups = "unit", testName = "CloudDatabasesUSProviderMetadataExpectTest")
+public class CloudDatabasesUSProviderMetadataExpectTest extends BaseTroveApiExpectTest {
+
+ public CloudDatabasesUSProviderMetadataExpectTest() {
+ this.provider = "rackspace-clouddatabases-us";
+ this.identity = "myUsername";
+ this.credential = "myApiKey";
+ }
+
+ public void testCanGetConfiguredZones() {
+
+ HttpRequest authenticate = HttpRequest.builder().method("POST")
+ .endpoint("https://identity.api.rackspacecloud.com/v2.0/tokens")
+ .addHeader("Accept", "application/json")
+ .payload(payloadFromStringWithContentType(
+ "{\"auth\":{\"RAX-KSKEY:apiKeyCredentials\":{\"username\":\"myUsername\",\"apiKey\":\"myApiKey\"}}}"
+ , "application/json")).build();
+
+
+ HttpResponse authenticationResponse = HttpResponse.builder()
+ .statusCode(200)
+ .payload(payloadFromResourceWithContentType("/access_rax_us.json", "application/json"))
+ .build();
+
+ TroveApi whenRegionExists = requestSendsResponse(authenticate, authenticationResponse);
+
+ assertEquals(whenRegionExists.getConfiguredZones(), ImmutableSet.of("ORD", "DFW", "SYD"));
+
+ }
+
+}
diff --git a/providers/rackspace-clouddatabases-us/src/test/java/org/jclouds/rackspace/clouddatabases/us/CloudDatabasesUSProviderTest.java b/providers/rackspace-clouddatabases-us/src/test/java/org/jclouds/rackspace/clouddatabases/us/CloudDatabasesUSProviderTest.java
new file mode 100644
index 0000000000..e6dbbedb0c
--- /dev/null
+++ b/providers/rackspace-clouddatabases-us/src/test/java/org/jclouds/rackspace/clouddatabases/us/CloudDatabasesUSProviderTest.java
@@ -0,0 +1,32 @@
+/*
+ * 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.rackspace.clouddatabases.us;
+
+import org.jclouds.openstack.trove.v1.TroveApiMetadata;
+import org.jclouds.providers.internal.BaseProviderMetadataTest;
+import org.testng.annotations.Test;
+
+/**
+ * @author Zack Shoylev
+ */
+@Test(groups = "unit", testName = "CloudDatabasesUSProviderTest")
+public class CloudDatabasesUSProviderTest extends BaseProviderMetadataTest {
+
+ public CloudDatabasesUSProviderTest() {
+ super(new CloudDatabasesUSProviderMetadata(), new TroveApiMetadata());
+ }
+}
diff --git a/providers/rackspace-clouddatabases-us/src/test/java/org/jclouds/rackspace/clouddatabases/us/features/CloudDatabasesUSDatabaseApiLiveTest.java b/providers/rackspace-clouddatabases-us/src/test/java/org/jclouds/rackspace/clouddatabases/us/features/CloudDatabasesUSDatabaseApiLiveTest.java
new file mode 100644
index 0000000000..982c88a96a
--- /dev/null
+++ b/providers/rackspace-clouddatabases-us/src/test/java/org/jclouds/rackspace/clouddatabases/us/features/CloudDatabasesUSDatabaseApiLiveTest.java
@@ -0,0 +1,30 @@
+/*
+ * 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.rackspace.clouddatabases.us.features;
+
+import org.jclouds.openstack.trove.v1.features.DatabaseApiLiveTest;
+import org.testng.annotations.Test;
+
+/**
+ * @author Zack Shoylev
+ */
+@Test(groups = "live", testName = "CloudDatabasesUSDatabaseApiLiveTest")
+public class CloudDatabasesUSDatabaseApiLiveTest extends DatabaseApiLiveTest {
+ public CloudDatabasesUSDatabaseApiLiveTest() {
+ provider = "rackspace-clouddatabases-us";
+ }
+}
diff --git a/providers/rackspace-clouddatabases-us/src/test/java/org/jclouds/rackspace/clouddatabases/us/features/CloudDatabasesUSFlavorApiLiveTest.java b/providers/rackspace-clouddatabases-us/src/test/java/org/jclouds/rackspace/clouddatabases/us/features/CloudDatabasesUSFlavorApiLiveTest.java
new file mode 100644
index 0000000000..369d9c2655
--- /dev/null
+++ b/providers/rackspace-clouddatabases-us/src/test/java/org/jclouds/rackspace/clouddatabases/us/features/CloudDatabasesUSFlavorApiLiveTest.java
@@ -0,0 +1,30 @@
+/*
+ * 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.rackspace.clouddatabases.us.features;
+
+import org.jclouds.openstack.trove.v1.features.FlavorApiLiveTest;
+import org.testng.annotations.Test;
+
+/**
+ * @author Zack Shoylev
+ */
+@Test(groups = "live", testName = "CloudDatabasesUSFlavorApiLiveTest")
+public class CloudDatabasesUSFlavorApiLiveTest extends FlavorApiLiveTest {
+ public CloudDatabasesUSFlavorApiLiveTest() {
+ provider = "rackspace-clouddatabases-us";
+ }
+}
diff --git a/providers/rackspace-clouddatabases-us/src/test/java/org/jclouds/rackspace/clouddatabases/us/features/CloudDatabasesUSInstanceApiLiveTest.java b/providers/rackspace-clouddatabases-us/src/test/java/org/jclouds/rackspace/clouddatabases/us/features/CloudDatabasesUSInstanceApiLiveTest.java
new file mode 100644
index 0000000000..2498e049ee
--- /dev/null
+++ b/providers/rackspace-clouddatabases-us/src/test/java/org/jclouds/rackspace/clouddatabases/us/features/CloudDatabasesUSInstanceApiLiveTest.java
@@ -0,0 +1,30 @@
+/*
+ * 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.rackspace.clouddatabases.us.features;
+
+import org.jclouds.openstack.trove.v1.features.InstanceApiLiveTest;
+import org.testng.annotations.Test;
+
+/**
+ * @author Zack Shoylev
+ */
+@Test(groups = "live", testName = "CloudDatabasesUSInstanceApiLiveTest")
+public class CloudDatabasesUSInstanceApiLiveTest extends InstanceApiLiveTest {
+ public CloudDatabasesUSInstanceApiLiveTest() {
+ provider = "rackspace-clouddatabases-us";
+ }
+}
diff --git a/providers/rackspace-clouddatabases-us/src/test/java/org/jclouds/rackspace/clouddatabases/us/features/CloudDatabasesUSUserApiLiveTest.java b/providers/rackspace-clouddatabases-us/src/test/java/org/jclouds/rackspace/clouddatabases/us/features/CloudDatabasesUSUserApiLiveTest.java
new file mode 100644
index 0000000000..95daa4d238
--- /dev/null
+++ b/providers/rackspace-clouddatabases-us/src/test/java/org/jclouds/rackspace/clouddatabases/us/features/CloudDatabasesUSUserApiLiveTest.java
@@ -0,0 +1,30 @@
+/*
+ * 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.rackspace.clouddatabases.us.features;
+
+import org.jclouds.openstack.trove.v1.features.UserApiLiveTest;
+import org.testng.annotations.Test;
+
+/**
+ * @author Zack Shoylev
+ */
+@Test(groups = "live", testName = "CloudDatabasesUSUserApiLiveTest")
+public class CloudDatabasesUSUserApiLiveTest extends UserApiLiveTest {
+ public CloudDatabasesUSUserApiLiveTest() {
+ provider = "rackspace-clouddatabases-us";
+ }
+}
diff --git a/providers/rackspace-clouddatabases-us/src/test/resources/logback.xml b/providers/rackspace-clouddatabases-us/src/test/resources/logback.xml
new file mode 100644
index 0000000000..6559c2351d
--- /dev/null
+++ b/providers/rackspace-clouddatabases-us/src/test/resources/logback.xml
@@ -0,0 +1,69 @@
+
+
+
+
+ target/test-data/jclouds.log
+
+
+ %d %-5p [%c] [%thread] %m%n
+
+
+
+
+ target/test-data/jclouds-wire.log
+
+
+ %d %-5p [%c] [%thread] %m%n
+
+
+
+
+ target/test-data/jclouds-blobstore.log
+
+
+ %d %-5p [%c] [%thread] %m%n
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+