You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jclouds.apache.org by na...@apache.org on 2015/04/23 12:53:21 UTC

jclouds-labs git commit: Initial implementation of Roles API

Repository: jclouds-labs
Updated Branches:
  refs/heads/1.9.x bc99a7028 -> e7fb50988


Initial implementation of Roles API

ADDED: fallback 'NullOnRoleNotFoundAnd500' and its corresponding code block in our errorhandler to catch and return null when role not found instead of propagating exception.

FIX: Changed use of Boolean to boolean in Roles API


Project: http://git-wip-us.apache.org/repos/asf/jclouds-labs/repo
Commit: http://git-wip-us.apache.org/repos/asf/jclouds-labs/commit/e7fb5098
Tree: http://git-wip-us.apache.org/repos/asf/jclouds-labs/tree/e7fb5098
Diff: http://git-wip-us.apache.org/repos/asf/jclouds-labs/diff/e7fb5098

Branch: refs/heads/1.9.x
Commit: e7fb50988551c21294561370dd1fb998bbf8a1b5
Parents: bc99a70
Author: ChristopherDancy <ch...@pega.com>
Authored: Sat Apr 11 12:54:38 2015 -0400
Committer: Ignasi Barrera <na...@apache.org>
Committed: Thu Apr 23 12:50:10 2015 +0200

----------------------------------------------------------------------
 .../java/org/jclouds/shipyard/ShipyardApi.java  |   4 +
 .../jclouds/shipyard/domain/roles/RoleInfo.java |  38 ++++++
 .../shipyard/fallbacks/ShipyardFallbacks.java   |  22 ++++
 .../org/jclouds/shipyard/features/RolesApi.java |  63 ++++++++++
 .../shipyard/handlers/ShipyardErrorHandler.java |  10 +-
 .../shipyard/features/RolesApiLiveTest.java     |  90 +++++++++++++
 .../shipyard/features/RolesApiMockTest.java     | 125 +++++++++++++++++++
 .../features/ServiceKeysApiLiveTest.java        |  26 ++--
 .../src/test/resources/role-delete-create.json  |   3 +
 shipyard/src/test/resources/role.json           |   4 +
 shipyard/src/test/resources/roles.json          |  10 ++
 11 files changed, 385 insertions(+), 10 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e7fb5098/shipyard/src/main/java/org/jclouds/shipyard/ShipyardApi.java
----------------------------------------------------------------------
diff --git a/shipyard/src/main/java/org/jclouds/shipyard/ShipyardApi.java b/shipyard/src/main/java/org/jclouds/shipyard/ShipyardApi.java
index 2c447d5..79bde85 100644
--- a/shipyard/src/main/java/org/jclouds/shipyard/ShipyardApi.java
+++ b/shipyard/src/main/java/org/jclouds/shipyard/ShipyardApi.java
@@ -20,6 +20,7 @@ import org.jclouds.rest.annotations.Delegate;
 import org.jclouds.shipyard.features.ContainersApi;
 import org.jclouds.shipyard.features.EnginesApi;
 import org.jclouds.shipyard.features.ImagesApi;
+import org.jclouds.shipyard.features.RolesApi;
 import org.jclouds.shipyard.features.ServiceKeysApi;
 
 import java.io.Closeable;
@@ -37,4 +38,7 @@ public interface ShipyardApi extends Closeable {
    
    @Delegate
    ServiceKeysApi serviceKeysApi();
+   
+   @Delegate
+   RolesApi rolesApi();
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e7fb5098/shipyard/src/main/java/org/jclouds/shipyard/domain/roles/RoleInfo.java
----------------------------------------------------------------------
diff --git a/shipyard/src/main/java/org/jclouds/shipyard/domain/roles/RoleInfo.java b/shipyard/src/main/java/org/jclouds/shipyard/domain/roles/RoleInfo.java
new file mode 100644
index 0000000..a56b99c
--- /dev/null
+++ b/shipyard/src/main/java/org/jclouds/shipyard/domain/roles/RoleInfo.java
@@ -0,0 +1,38 @@
+/*
+ * 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.shipyard.domain.roles;
+
+import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.json.SerializedNames;
+
+import com.google.auto.value.AutoValue;
+
+@AutoValue
+public abstract class RoleInfo {
+
+   public abstract String name();
+   
+   @Nullable public abstract String id();
+      
+   RoleInfo() {
+   }
+
+   @SerializedNames({ "name", "id" })
+   public static RoleInfo create(String name, String id) {
+      return new AutoValue_RoleInfo(name, id);
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e7fb5098/shipyard/src/main/java/org/jclouds/shipyard/fallbacks/ShipyardFallbacks.java
----------------------------------------------------------------------
diff --git a/shipyard/src/main/java/org/jclouds/shipyard/fallbacks/ShipyardFallbacks.java b/shipyard/src/main/java/org/jclouds/shipyard/fallbacks/ShipyardFallbacks.java
index 995eb3b..4e15a32 100644
--- a/shipyard/src/main/java/org/jclouds/shipyard/fallbacks/ShipyardFallbacks.java
+++ b/shipyard/src/main/java/org/jclouds/shipyard/fallbacks/ShipyardFallbacks.java
@@ -36,4 +36,26 @@ public final class ShipyardFallbacks {
          throw propagate(t);
       }
    }
+   
+   public static final class BooleanOnRoleNotFoundAnd500 implements Fallback<Boolean> {
+      public Boolean createOrPropagate(Throwable t) throws Exception {
+         if (checkNotNull(t, "throwable") != null && 
+               t.getMessage().contains("role does not exist") && 
+               returnValueOnCodeOrNull(t, true, equalTo(500)) != null) {
+            return Boolean.FALSE;
+         }
+         throw propagate(t);
+      }
+   }
+   
+   public static final class NullOnRoleNotFoundAnd500 implements Fallback<Object> {
+      public Object createOrPropagate(Throwable t) throws Exception {
+         if (checkNotNull(t, "throwable") != null && 
+               t.getMessage().contains("role does not exist") && 
+               returnValueOnCodeOrNull(t, true, equalTo(500)) != null) {
+            return null;
+         }
+         throw propagate(t);
+      }
+   }
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e7fb5098/shipyard/src/main/java/org/jclouds/shipyard/features/RolesApi.java
----------------------------------------------------------------------
diff --git a/shipyard/src/main/java/org/jclouds/shipyard/features/RolesApi.java b/shipyard/src/main/java/org/jclouds/shipyard/features/RolesApi.java
new file mode 100644
index 0000000..2528fc7
--- /dev/null
+++ b/shipyard/src/main/java/org/jclouds/shipyard/features/RolesApi.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.shipyard.features;
+
+import java.util.List;
+
+import javax.inject.Named;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+import org.jclouds.rest.annotations.Fallback;
+import org.jclouds.rest.annotations.RequestFilters;
+import org.jclouds.rest.annotations.WrapWith;
+import org.jclouds.shipyard.domain.roles.RoleInfo;
+import org.jclouds.shipyard.fallbacks.ShipyardFallbacks.BooleanOnRoleNotFoundAnd500;
+import org.jclouds.shipyard.fallbacks.ShipyardFallbacks.NullOnRoleNotFoundAnd500;
+import org.jclouds.shipyard.filters.ServiceKeyAuthentication;
+
+@Consumes(MediaType.APPLICATION_JSON)
+@Produces(MediaType.APPLICATION_JSON)
+@RequestFilters({ ServiceKeyAuthentication.class })
+@Path("/api/roles")
+public interface RolesApi {
+
+   @Named("roles:list")
+   @GET
+   List<RoleInfo> listRoles();
+   
+   @Named("roles:info")
+   @Fallback(NullOnRoleNotFoundAnd500.class)
+   @GET
+   @Path("/{name}")
+   RoleInfo getRole(@PathParam("name") String name);
+   
+   @Named("roles:create")
+   @POST
+   void createRole(@WrapWith("name") String name);
+   
+   @Named("roles:delete")
+   @Fallback(BooleanOnRoleNotFoundAnd500.class)
+   @DELETE
+   boolean deleteRole(@WrapWith("name") String name);
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e7fb5098/shipyard/src/main/java/org/jclouds/shipyard/handlers/ShipyardErrorHandler.java
----------------------------------------------------------------------
diff --git a/shipyard/src/main/java/org/jclouds/shipyard/handlers/ShipyardErrorHandler.java b/shipyard/src/main/java/org/jclouds/shipyard/handlers/ShipyardErrorHandler.java
index 2e3b53b..374ce7e 100644
--- a/shipyard/src/main/java/org/jclouds/shipyard/handlers/ShipyardErrorHandler.java
+++ b/shipyard/src/main/java/org/jclouds/shipyard/handlers/ShipyardErrorHandler.java
@@ -65,13 +65,21 @@ public class ShipyardErrorHandler implements HttpErrorHandler {
                }
                break;
             case 500:
-               if (command.getCurrentRequest().getMethod().equals("POST")) {
+               if (command.getCurrentRequest().getMethod().equals("GET")) {
+                  if (command.getCurrentRequest().getEndpoint().getPath().startsWith("/api/roles") && 
+                        message.contains("role does not exist")) {
+                     exception = new ResourceNotFoundException(message, new HttpResponseException(command, response, message));
+                  }
+               } else if (command.getCurrentRequest().getMethod().equals("POST")) {
                   if (command.getCurrentRequest().getEndpoint().getPath().endsWith("/engines")) 
                      exception = new HttpResponseException("Connection refused registering docker daemon", command, response);
                } else if (command.getCurrentRequest().getMethod().equals("DELETE")) {
                   if (command.getCurrentRequest().getEndpoint().getPath().endsWith("/servicekeys") && 
                         message.contains("service key does not exist")) {
                      exception = new ResourceNotFoundException(message, new HttpResponseException(command, response, message));
+                  } else if (command.getCurrentRequest().getEndpoint().getPath().endsWith("/roles") && 
+                        message.contains("role does not exist")) {
+                     exception = new ResourceNotFoundException(message, new HttpResponseException(command, response, message));
                   }
                }
                break;

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e7fb5098/shipyard/src/test/java/org/jclouds/shipyard/features/RolesApiLiveTest.java
----------------------------------------------------------------------
diff --git a/shipyard/src/test/java/org/jclouds/shipyard/features/RolesApiLiveTest.java b/shipyard/src/test/java/org/jclouds/shipyard/features/RolesApiLiveTest.java
new file mode 100644
index 0000000..e362bac
--- /dev/null
+++ b/shipyard/src/test/java/org/jclouds/shipyard/features/RolesApiLiveTest.java
@@ -0,0 +1,90 @@
+/*
+ * 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.shipyard.features;
+
+import java.util.List;
+import java.util.UUID;
+
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertNotNull;
+
+import org.jclouds.shipyard.domain.roles.RoleInfo;
+import org.jclouds.shipyard.internal.BaseShipyardApiLiveTest;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+
+@Test(groups = "live", testName = "RolesApiLiveTest", singleThreaded = true)
+public class RolesApiLiveTest extends BaseShipyardApiLiveTest {
+
+   private final String roleName = "jclouds-shipyard-test-" + UUID.randomUUID().toString().replaceAll("-", "");
+   
+   @AfterClass (alwaysRun = true)
+   protected void tearDown() {
+      api().deleteRole(roleName);
+   }
+   
+   public void testCreateRole() throws Exception {
+     api().createRole(roleName);
+   }
+   
+   @Test (dependsOnMethods = "testCreateRole")
+   public void testGetRole() throws Exception {
+      RoleInfo role = api().getRole(roleName);
+      assertNotNull(role, "Role was not set");
+      assertTrue(role.name().equals(roleName), "Found Role name does not match expected name: found=" + role.name() + ", expected=" + roleName);
+   }
+   
+   @Test (dependsOnMethods = "testGetRole")
+   public void testListRoles() throws Exception {
+      List<RoleInfo> possibleRoles = api().listRoles();
+      assertNotNull(possibleRoles, "possibleRoles was not set");
+      assertTrue(possibleRoles.size() > 0, "Expected at least 1 Role but list was empty");
+      RoleInfo possibleRole = Iterables.find(possibleRoles, new Predicate<RoleInfo>() {
+         @Override
+         public boolean apply(RoleInfo arg0) {
+            return arg0.name().equals(roleName);
+         }
+      }, null);
+      assertNotNull(possibleRole, "Expected but could not find Role amongst " + possibleRoles.size() + " found");
+   }
+   
+   @Test (dependsOnMethods = "testListRoles")
+   public void testRemoveRole() throws Exception {
+      boolean removed = api().deleteRole(UUID.randomUUID().toString().replaceAll("-", ""));
+      assertTrue(removed);
+   }
+   
+   @Test (dependsOnMethods = "testRemoveRole")
+   public void testRemoveNonExistentRole() throws Exception {
+      boolean removed = api().deleteRole(UUID.randomUUID().toString().replaceAll("-", ""));
+      assertTrue(removed);
+   }
+   
+   @Test (dependsOnMethods = "testRemoveNonExistentRole")
+   public void testGetNonExistentRole() throws Exception {
+      RoleInfo role = api().getRole("jclouds-shipyard-test-" + UUID.randomUUID().toString().replaceAll("-", ""));
+      assertNull(role, "Role was expected to be NULL but was not");
+   }
+   
+   private RolesApi api() {
+      return api.rolesApi();
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e7fb5098/shipyard/src/test/java/org/jclouds/shipyard/features/RolesApiMockTest.java
----------------------------------------------------------------------
diff --git a/shipyard/src/test/java/org/jclouds/shipyard/features/RolesApiMockTest.java b/shipyard/src/test/java/org/jclouds/shipyard/features/RolesApiMockTest.java
new file mode 100644
index 0000000..807442d
--- /dev/null
+++ b/shipyard/src/test/java/org/jclouds/shipyard/features/RolesApiMockTest.java
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.shipyard.features;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+import org.jclouds.shipyard.ShipyardApi;
+import org.jclouds.shipyard.domain.roles.RoleInfo;
+import org.jclouds.shipyard.internal.BaseShipyardMockTest;
+import org.testng.annotations.Test;
+
+import com.squareup.okhttp.mockwebserver.MockResponse;
+import com.squareup.okhttp.mockwebserver.MockWebServer;
+
+/**
+ * Mock tests for the {@link org.jclouds.shipyard.features.RolesApi} class.
+ */
+@Test(groups = "unit", testName = "RolesApiMockTest")
+public class RolesApiMockTest extends BaseShipyardMockTest {
+
+   public void testListRoles() throws Exception {
+      MockWebServer server = mockShipyardWebServer();
+      server.enqueue(new MockResponse().setResponseCode(200).setBody(payloadFromResource("/roles.json")));
+      ShipyardApi shipyardApi = api(server.getUrl("/"));
+      RolesApi api = shipyardApi.rolesApi();
+      try {
+         assertEquals(api.listRoles().size(), 2);
+         assertSent(server, "GET", "/api/roles");
+      } finally {
+         shipyardApi.close();
+         server.shutdown();
+      }
+   }
+
+   public void testGetRole() throws Exception {
+      MockWebServer server = mockShipyardWebServer();
+      server.enqueue(new MockResponse().setResponseCode(200).setBody(payloadFromResource("/role.json")));
+      ShipyardApi shipyardApi = api(server.getUrl("/"));
+      RolesApi api = shipyardApi.rolesApi();
+      try {
+         RoleInfo role = api.getRole("admin");
+         assertNotNull(role);
+         assertEquals(role.name(), "admin");
+         assertSent(server, "GET", "/api/roles/admin");
+      } finally {
+         shipyardApi.close();
+         server.shutdown();
+      }
+   }
+   
+   public void testGetNonExistentRole() throws Exception {
+      MockWebServer server = mockShipyardWebServer();
+      server.enqueue(new MockResponse().setResponseCode(500).setBody("role does not exist"));
+      ShipyardApi shipyardApi = api(server.getUrl("/"));
+      RolesApi api = shipyardApi.rolesApi();
+      try {
+         RoleInfo role = api.getRole("NonExistentRole");
+         assertNull(role);
+      } finally {
+         shipyardApi.close();
+         server.shutdown();
+      }
+   }
+   
+   public void testCreateRole() throws Exception {
+      MockWebServer server = mockShipyardWebServer();
+      server.enqueue(new MockResponse().setResponseCode(204));
+      ShipyardApi shipyardApi = api(server.getUrl("/"));
+      RolesApi api = shipyardApi.rolesApi();
+      try {
+         api.createRole("admin");
+         assertSent(server, "POST", "/api/roles", new String(payloadFromResource("/role-delete-create.json")));
+      } finally {
+         shipyardApi.close();
+         server.shutdown();
+      }
+   }
+   
+   public void testDeleteRole() throws Exception {
+      MockWebServer server = mockShipyardWebServer();
+      server.enqueue(new MockResponse().setResponseCode(204));
+      ShipyardApi shipyardApi = api(server.getUrl("/"));
+      RolesApi api = shipyardApi.rolesApi();
+      try {
+         boolean removed = api.deleteRole("admin");
+         assertTrue(removed);
+         assertSent(server, "DELETE", "/api/roles", new String(payloadFromResource("/role-delete-create.json")));
+      } finally {
+         shipyardApi.close();
+         server.shutdown();
+      }
+   }
+   
+   public void testDeleteNonExistentRole() throws Exception {
+      MockWebServer server = mockShipyardWebServer();
+      server.enqueue(new MockResponse().setResponseCode(500).setBody("role does not exist"));
+      ShipyardApi shipyardApi = api(server.getUrl("/"));
+      RolesApi api = shipyardApi.rolesApi();
+      try {
+         boolean removed = api.deleteRole("NonExistentRole");
+         assertFalse(removed);
+      } finally {
+         shipyardApi.close();
+         server.shutdown();
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e7fb5098/shipyard/src/test/java/org/jclouds/shipyard/features/ServiceKeysApiLiveTest.java
----------------------------------------------------------------------
diff --git a/shipyard/src/test/java/org/jclouds/shipyard/features/ServiceKeysApiLiveTest.java b/shipyard/src/test/java/org/jclouds/shipyard/features/ServiceKeysApiLiveTest.java
index 470eff9..b55b958 100644
--- a/shipyard/src/test/java/org/jclouds/shipyard/features/ServiceKeysApiLiveTest.java
+++ b/shipyard/src/test/java/org/jclouds/shipyard/features/ServiceKeysApiLiveTest.java
@@ -28,6 +28,9 @@ import org.jclouds.shipyard.internal.BaseShipyardApiLiveTest;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.Test;
 
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+
 @Test(groups = "live", testName = "ServiceKeysApiLiveTest", singleThreaded = true)
 public class ServiceKeysApiLiveTest extends BaseShipyardApiLiveTest {
 
@@ -36,9 +39,7 @@ public class ServiceKeysApiLiveTest extends BaseShipyardApiLiveTest {
    
    @AfterClass (alwaysRun = true)
    protected void tearDown() {
-      assertNotNull(serviceKey, "Expected serviceKey to be set but was not");
-      boolean removed = api().deleteServiceKey(serviceKey);
-      assertTrue(removed);
+      api().deleteServiceKey(serviceKey);
    }
    
    public void testCreateServiceKey() throws Exception {
@@ -53,13 +54,20 @@ public class ServiceKeysApiLiveTest extends BaseShipyardApiLiveTest {
       List<ServiceKey> possibleServiceKeys = api().listServiceKeys();
       assertNotNull(possibleServiceKeys, "possibleServiceKeys was not set");
       assertTrue(possibleServiceKeys.size() > 0, "Expected at least 1 ServiceKey but list was empty");
-      boolean serviceKeyFound = false;
-      for (ServiceKey possibleKey : possibleServiceKeys) {
-         if (possibleKey.key().equals(serviceKey)) {
-            serviceKeyFound = true;
+      ServiceKey possibleServiceKey = Iterables.find(possibleServiceKeys, new Predicate<ServiceKey>() {
+         @Override
+         public boolean apply(ServiceKey arg0) {
+            return arg0.key().equals(serviceKey);
          }
-      }
-      assertTrue(serviceKeyFound, "Expected but could not find ServiceKey amongst " + possibleServiceKeys.size() + " found");
+      }, null);
+      assertNotNull(possibleServiceKey, "Expected but could not find ServiceKey amongst " + possibleServiceKeys.size() + " found");
+   }
+   
+   @Test (dependsOnMethods = "testListServiceKeys")
+   public void testRemoveServiceKey() throws Exception {
+      assertNotNull(serviceKey, "Expected serviceKey to be set but was not");
+      boolean removed = api().deleteServiceKey(serviceKey);
+      assertTrue(removed);
    }
    
    public void testRemoveNonExistentServiceKey() throws Exception {

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e7fb5098/shipyard/src/test/resources/role-delete-create.json
----------------------------------------------------------------------
diff --git a/shipyard/src/test/resources/role-delete-create.json b/shipyard/src/test/resources/role-delete-create.json
new file mode 100644
index 0000000..6447c85
--- /dev/null
+++ b/shipyard/src/test/resources/role-delete-create.json
@@ -0,0 +1,3 @@
+{
+  "name": "admin"
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e7fb5098/shipyard/src/test/resources/role.json
----------------------------------------------------------------------
diff --git a/shipyard/src/test/resources/role.json b/shipyard/src/test/resources/role.json
new file mode 100644
index 0000000..fdda8a1
--- /dev/null
+++ b/shipyard/src/test/resources/role.json
@@ -0,0 +1,4 @@
+{
+  "name": "admin",
+  "id": "448ebe2d-89d9-412c-aab9-ad774d1ec78f"
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e7fb5098/shipyard/src/test/resources/roles.json
----------------------------------------------------------------------
diff --git a/shipyard/src/test/resources/roles.json b/shipyard/src/test/resources/roles.json
new file mode 100644
index 0000000..6cb32a1
--- /dev/null
+++ b/shipyard/src/test/resources/roles.json
@@ -0,0 +1,10 @@
+[
+  {
+    "name": "admin",
+    "id": "448ebe2d-89d9-412c-aab9-ad774d1ec78f"
+  },
+  {
+    "name": "user",
+    "id": "2243edf5-55f1-43ad-9ebb-c36c5233f007"
+  }
+]
\ No newline at end of file