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/07 00:36:35 UTC

jclouds-labs git commit: Added ServiceKeys API to the Shipyard provider

Repository: jclouds-labs
Updated Branches:
  refs/heads/master b9036f5af -> 766caab06


Added ServiceKeys API to the Shipyard provider


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

Branch: refs/heads/master
Commit: 766caab0685bbe6723c7704157c90d1a17a8da6d
Parents: b9036f5
Author: ChristopherDancy <ch...@pega.com>
Authored: Tue Mar 10 09:38:20 2015 -0400
Committer: Ignasi Barrera <na...@apache.org>
Committed: Tue Apr 7 00:30:34 2015 +0200

----------------------------------------------------------------------
 shipyard/README.md                              |  34 +++---
 shipyard/pom.xml                                |   2 +
 .../java/org/jclouds/shipyard/ShipyardApi.java  |   4 +
 .../shipyard/domain/servicekeys/ServiceKey.java |  38 +++++++
 .../shipyard/fallbacks/ShipyardFallbacks.java   |  39 +++++++
 .../shipyard/features/ServiceKeysApi.java       |  55 +++++++++
 .../shipyard/handlers/ShipyardErrorHandler.java |  21 ++--
 .../features/ContainersApiLiveTest.java         |   3 +-
 .../features/ContainersApiMockTest.java         |   1 -
 .../features/ServiceKeysApiLiveTest.java        |  73 ++++++++++++
 .../features/ServiceKeysApiMockTest.java        | 112 +++++++++++++++++++
 .../src/test/resources/servicekey-create.json   |   3 +
 .../src/test/resources/servicekey-response.json |   5 +
 shipyard/src/test/resources/servicekeys.json    |   9 ++
 14 files changed, 371 insertions(+), 28 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/766caab0/shipyard/README.md
----------------------------------------------------------------------
diff --git a/shipyard/README.md b/shipyard/README.md
index c394147..0545f87 100644
--- a/shipyard/README.md
+++ b/shipyard/README.md
@@ -1,26 +1,22 @@
-#Shipyard provider for jclouds
-- jclouds-shipyard allows one to connect and administer multiple docker daemons from a single endpoint.
+Shipyard provider for jclouds
+=============================
+jclouds-shipyard allows one to connect and administer multiple docker daemons from a single endpoint.
 
---------------
+## Setup
+Documentation on how to standup a Shipyard cluster can be found [here](https://github.com/shipyard/shipyard).
 
-#Setup
+More detailed information (docs, API, etc..) can be found on the Shipyard [site](http://shipyard-project.com/).
 
-- Notes on how to standup a Shipyard instance can be found here:
+## Testing
+To run integration tests we need a valid Shipyard instance/endpoint, identity-key, and docker daemon to use. Testing, for now, assumes the docker daemon does not have ssl enabled. To run integration tests you could do something like:
 
-	https://github.com/shipyard/shipyard
-	
-- More detailed information (docs, API, etc..) can be found on their site:
+```
+mvn clean install -Plive -Dtest.shipyard.endpoint=http://10.0.0.8:8080 -Dtest.shipyard.identity=zEswusMbqMR8D7QA0yJbIc1CxGYqfLAG5bZO -Dtest.shipyard.docker.endpoint=http://10.0.0.8:2375
+```
 
-	http://shipyard-project.com/
+## Help
+For help with jclouds-shipyard you can email the jclouds-dev mailing list or ask questions on [IRC](http://irc.lc/freenode/jclouds/shipyard-help). 
 
---------------
+For help with Shipyard itself you can reach out to devs on [IRC](http://irc.lc/freenode/shipyard/jclouds-help).
 
-#Notes
-- jclouds-shipyard is still at alpha stage please report any issues you find at [jclouds issues](https://issues.apache.org/jira/browse/JCLOUDS)
-
---------------
-
-#Testing
-- To run integration tests we need a valid Shipyard instance/endpoint, identity-key, and docker daemon to use. Testing, for now, assumes the docker daemon does not have ssl enabled. To run integration tests you could do something like:
-
-	$> mvn clean install -Plive -Dtest.shipyard.endpoint=http://10.0.0.8:8080 -Dtest.shipyard.identity=zEswusMbqMR8D7QA0yJbIc1CxGYqfLAG5bZO -Dtest.shipyard.docker.endpoint=http://10.0.0.8:2375
+jclouds-shipyard is still at alpha stage please report any issues you find at [jclouds-shipyard-issues](https://issues.apache.org/jira/browse/JCLOUDS).
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/766caab0/shipyard/pom.xml
----------------------------------------------------------------------
diff --git a/shipyard/pom.xml b/shipyard/pom.xml
index 30dfbf8..a1bbd77 100644
--- a/shipyard/pom.xml
+++ b/shipyard/pom.xml
@@ -36,6 +36,7 @@
     <test.shipyard.endpoint>http://localhost:8080</test.shipyard.endpoint>
     <test.shipyard.docker.endpoint>http://localhost:2375</test.shipyard.docker.endpoint>
     <test.shipyard.identity>FIXME</test.shipyard.identity>
+    <test.shipyard.credential>NA</test.shipyard.credential>
     <test.shipyard.api-version>2.0.8</test.shipyard.api-version>
     <jclouds.osgi.export>org.jclouds.shipyard*;version="${project.version}"</jclouds.osgi.export>
     <jclouds.osgi.import>
@@ -132,6 +133,7 @@
                     <test.shipyard.endpoint>${test.shipyard.endpoint}</test.shipyard.endpoint>
                     <test.shipyard.docker.endpoint>${test.shipyard.docker.endpoint}</test.shipyard.docker.endpoint>
                     <test.shipyard.identity>${test.shipyard.identity}</test.shipyard.identity>
+                    <test.shipyard.credential>${test.shipyard.credential}</test.shipyard.credential>
                     <test.shipyard.api-version>${test.shipyard.api-version}</test.shipyard.api-version>
                   </systemPropertyVariables>
                 </configuration>

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/766caab0/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 c92a4b5..2c447d5 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.ServiceKeysApi;
 
 import java.io.Closeable;
 
@@ -33,4 +34,7 @@ public interface ShipyardApi extends Closeable {
    
    @Delegate
    EnginesApi enginesApi();
+   
+   @Delegate
+   ServiceKeysApi serviceKeysApi();
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/766caab0/shipyard/src/main/java/org/jclouds/shipyard/domain/servicekeys/ServiceKey.java
----------------------------------------------------------------------
diff --git a/shipyard/src/main/java/org/jclouds/shipyard/domain/servicekeys/ServiceKey.java b/shipyard/src/main/java/org/jclouds/shipyard/domain/servicekeys/ServiceKey.java
new file mode 100644
index 0000000..ce9f754
--- /dev/null
+++ b/shipyard/src/main/java/org/jclouds/shipyard/domain/servicekeys/ServiceKey.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.servicekeys;
+
+import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.json.SerializedNames;
+
+import com.google.auto.value.AutoValue;
+
+@AutoValue
+public abstract class ServiceKey {
+
+   public abstract String key();
+   
+   @Nullable public abstract String description();
+      
+   ServiceKey() {
+   }
+
+   @SerializedNames({ "key", "description" })
+   public static ServiceKey create(String key, String description) {
+      return new AutoValue_ServiceKey(key, description);
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/766caab0/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
new file mode 100644
index 0000000..995eb3b
--- /dev/null
+++ b/shipyard/src/main/java/org/jclouds/shipyard/fallbacks/ShipyardFallbacks.java
@@ -0,0 +1,39 @@
+/*
+ * 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.fallbacks;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Predicates.equalTo;
+import static com.google.common.base.Throwables.propagate;
+
+import static org.jclouds.http.HttpUtils.returnValueOnCodeOrNull;
+
+import org.jclouds.Fallback;
+
+public final class ShipyardFallbacks {
+
+   public static final class BooleanOnServiceKeyNotFoundAnd500 implements Fallback<Boolean> {
+      public Boolean createOrPropagate(Throwable t) throws Exception {
+         if (checkNotNull(t, "throwable") != null && 
+               t.getMessage().contains("service key does not exist") && 
+               returnValueOnCodeOrNull(t, true, equalTo(500)) != null) {
+            return Boolean.FALSE;
+         }
+         throw propagate(t);
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/766caab0/shipyard/src/main/java/org/jclouds/shipyard/features/ServiceKeysApi.java
----------------------------------------------------------------------
diff --git a/shipyard/src/main/java/org/jclouds/shipyard/features/ServiceKeysApi.java b/shipyard/src/main/java/org/jclouds/shipyard/features/ServiceKeysApi.java
new file mode 100644
index 0000000..9cd8aaa
--- /dev/null
+++ b/shipyard/src/main/java/org/jclouds/shipyard/features/ServiceKeysApi.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.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.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.servicekeys.ServiceKey;
+import org.jclouds.shipyard.fallbacks.ShipyardFallbacks.BooleanOnServiceKeyNotFoundAnd500;
+import org.jclouds.shipyard.filters.ServiceKeyAuthentication;
+
+@Consumes(MediaType.APPLICATION_JSON)
+@Produces(MediaType.APPLICATION_JSON)
+@RequestFilters({ ServiceKeyAuthentication.class })
+@Path("/api/servicekeys")
+public interface ServiceKeysApi {
+
+   @Named("servicekeys:list")
+   @GET
+   List<ServiceKey> listServiceKeys();
+   
+   @Named("servicekeys:create")
+   @POST
+   ServiceKey createServiceKey(@WrapWith("description") String description);
+   
+   @Named("servicekeys:delete")
+   @Fallback(BooleanOnServiceKeyNotFoundAnd500.class)
+   @DELETE
+   Boolean deleteServiceKey(@WrapWith("key") String key);
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/766caab0/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 0e43814..2e3b53b 100644
--- a/shipyard/src/main/java/org/jclouds/shipyard/handlers/ShipyardErrorHandler.java
+++ b/shipyard/src/main/java/org/jclouds/shipyard/handlers/ShipyardErrorHandler.java
@@ -28,6 +28,7 @@ import org.jclouds.http.HttpResponse;
 import org.jclouds.http.HttpResponseException;
 import org.jclouds.logging.Logger;
 import org.jclouds.rest.AuthorizationException;
+import org.jclouds.rest.ResourceNotFoundException;
 import org.jclouds.util.Strings2;
 
 import com.google.common.base.Throwables;
@@ -42,10 +43,10 @@ public class ShipyardErrorHandler implements HttpErrorHandler {
 
    public void handleError(HttpCommand command, HttpResponse response) {
 
+      String message = parseMessage(response);
       Exception exception = null;
       try {
-
-         String message = parseMessage(response);
+         
          message = message != null ? message : String.format("%s -> %s", command.getCurrentRequest().getRequestLine(), response.getStatusLine());
          switch (response.getStatusCode()) {
             case 401:
@@ -53,22 +54,24 @@ public class ShipyardErrorHandler implements HttpErrorHandler {
                break;
             case 404:
                if (command.getCurrentRequest().getMethod().equals("GET")) {
-                  if (command.getCurrentRequest().getEndpoint().getPath().endsWith("/images/json")) {
+                  if (command.getCurrentRequest().getEndpoint().getPath().endsWith("/images/json")) 
                      exception = new HttpResponseException("Unable to reach docker daemon", command, response);
-                  }
                }
                break;
             case 409:
                if (command.getCurrentRequest().getMethod().equals("POST")) {
-                  if (command.getCurrentRequest().getEndpoint().getPath().endsWith("/containers")) {
+                  if (command.getCurrentRequest().getEndpoint().getPath().endsWith("/containers"))
                      exception = new HttpResponseException("Container already exists", command, response);
-                  }
                }
                break;
             case 500:
                if (command.getCurrentRequest().getMethod().equals("POST")) {
-                  if (command.getCurrentRequest().getEndpoint().getPath().endsWith("/engines")) {
+                  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));
                   }
                }
                break;
@@ -79,6 +82,10 @@ public class ShipyardErrorHandler implements HttpErrorHandler {
       } catch (Exception e) {
          exception = new HttpResponseException(command, response, e);
       } finally {
+         if (exception == null) {
+            exception = message != null ? new HttpResponseException(command, response, message)
+                        : new HttpResponseException(command, response);
+         }
          closeQuietly(response.getPayload());
          command.setException(exception);
       }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/766caab0/shipyard/src/test/java/org/jclouds/shipyard/features/ContainersApiLiveTest.java
----------------------------------------------------------------------
diff --git a/shipyard/src/test/java/org/jclouds/shipyard/features/ContainersApiLiveTest.java b/shipyard/src/test/java/org/jclouds/shipyard/features/ContainersApiLiveTest.java
index 8ec10d1..4361854 100644
--- a/shipyard/src/test/java/org/jclouds/shipyard/features/ContainersApiLiveTest.java
+++ b/shipyard/src/test/java/org/jclouds/shipyard/features/ContainersApiLiveTest.java
@@ -76,7 +76,7 @@ public class ContainersApiLiveTest extends EnginesApiLiveTest {
       containerID = container.get(0).id();
    }
    
-   @Test (dependsOnMethods = "testDeployContainer")
+   @Test (dependsOnMethods = "testDeployContainer", expectedExceptions = HttpResponseException.class)
    public void testDeployAlreadyExistentContainer() throws Exception {
 
       DeployContainer deployContainer = DeployContainer.create("ubuntu:14.04.1", 
@@ -97,6 +97,7 @@ public class ContainersApiLiveTest extends EnginesApiLiveTest {
       assertNull(container);
    }
    
+   @Test (expectedExceptions = HttpResponseException.class)
    public void testDeployNonExistentContainer() throws Exception {
 
       DeployContainer deployContainer = DeployContainer.create("jclouds-shipyard-test:99.99.99", 

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/766caab0/shipyard/src/test/java/org/jclouds/shipyard/features/ContainersApiMockTest.java
----------------------------------------------------------------------
diff --git a/shipyard/src/test/java/org/jclouds/shipyard/features/ContainersApiMockTest.java b/shipyard/src/test/java/org/jclouds/shipyard/features/ContainersApiMockTest.java
index ca30e28..20de82f 100644
--- a/shipyard/src/test/java/org/jclouds/shipyard/features/ContainersApiMockTest.java
+++ b/shipyard/src/test/java/org/jclouds/shipyard/features/ContainersApiMockTest.java
@@ -18,7 +18,6 @@ package org.jclouds.shipyard.features;
 
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
-
 import java.util.List;
 
 import org.jclouds.shipyard.ShipyardApi;

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/766caab0/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
new file mode 100644
index 0000000..470eff9
--- /dev/null
+++ b/shipyard/src/test/java/org/jclouds/shipyard/features/ServiceKeysApiLiveTest.java
@@ -0,0 +1,73 @@
+/*
+ * 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.assertFalse;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.assertNotNull;
+
+import org.jclouds.shipyard.domain.servicekeys.ServiceKey;
+import org.jclouds.shipyard.internal.BaseShipyardApiLiveTest;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.Test;
+
+@Test(groups = "live", testName = "ServiceKeysApiLiveTest", singleThreaded = true)
+public class ServiceKeysApiLiveTest extends BaseShipyardApiLiveTest {
+
+   private final String serviceKeyDescription = "ShipyardJCloudsLiveTest";
+   private String serviceKey = null;
+   
+   @AfterClass (alwaysRun = true)
+   protected void tearDown() {
+      assertNotNull(serviceKey, "Expected serviceKey to be set but was not");
+      boolean removed = api().deleteServiceKey(serviceKey);
+      assertTrue(removed);
+   }
+   
+   public void testCreateServiceKey() throws Exception {
+     ServiceKey possibleServiceKey = api().createServiceKey(serviceKeyDescription);
+     assertNotNull(possibleServiceKey, "Did not successfully create ServiceKey");
+     assertTrue(possibleServiceKey.description().equals(serviceKeyDescription), "ServiceKey description returned from Shipyard did not match expected value");
+     serviceKey = possibleServiceKey.key();
+   }
+   
+   @Test (dependsOnMethods = "testCreateServiceKey")
+   public void testListServiceKeys() throws Exception {
+      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;
+         }
+      }
+      assertTrue(serviceKeyFound, "Expected but could not find ServiceKey amongst " + possibleServiceKeys.size() + " found");
+   }
+   
+   public void testRemoveNonExistentServiceKey() throws Exception {
+      boolean removed = api().deleteServiceKey(UUID.randomUUID().toString().replaceAll("-", ""));
+      assertFalse(removed);
+   }
+   
+   private ServiceKeysApi api() {
+      return api.serviceKeysApi();
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/766caab0/shipyard/src/test/java/org/jclouds/shipyard/features/ServiceKeysApiMockTest.java
----------------------------------------------------------------------
diff --git a/shipyard/src/test/java/org/jclouds/shipyard/features/ServiceKeysApiMockTest.java b/shipyard/src/test/java/org/jclouds/shipyard/features/ServiceKeysApiMockTest.java
new file mode 100644
index 0000000..23369a1
--- /dev/null
+++ b/shipyard/src/test/java/org/jclouds/shipyard/features/ServiceKeysApiMockTest.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.shipyard.features;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+import org.jclouds.http.HttpResponseException;
+import org.jclouds.shipyard.ShipyardApi;
+import org.jclouds.shipyard.domain.servicekeys.ServiceKey;
+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.ServiceKeysApi} class.
+ */
+@Test(groups = "unit", testName = "ServiceKeysApiMockTest")
+public class ServiceKeysApiMockTest extends BaseShipyardMockTest {
+
+   public void testListServiceKeys() throws Exception {
+      MockWebServer server = mockShipyardWebServer();
+      server.enqueue(new MockResponse().setResponseCode(200).setBody(payloadFromResource("/servicekeys.json")));
+      ShipyardApi shipyardApi = api(server.getUrl("/"));
+      ServiceKeysApi api = shipyardApi.serviceKeysApi();
+      try {
+         assertEquals(api.listServiceKeys().size(), 2);
+         assertSent(server, "GET", "/api/servicekeys");
+      } finally {
+         shipyardApi.close();
+         server.shutdown();
+      }
+   }
+
+   public void testCreateServiceKey() throws Exception {
+      MockWebServer server = mockShipyardWebServer();
+      server.enqueue(new MockResponse().setResponseCode(200).setBody(payloadFromResource("/servicekey-response.json")));
+      ShipyardApi shipyardApi = api(server.getUrl("/"));
+      ServiceKeysApi api = shipyardApi.serviceKeysApi();
+      try {
+         ServiceKey serviceKey = api.createServiceKey("jclouds-shipyard-testing");
+         assertNotNull(serviceKey);
+         assertEquals(serviceKey.description(), "jclouds-shipyard-testing");
+         assertSent(server, "POST", "/api/servicekeys", new String(payloadFromResource("/servicekey-create.json")));
+      } finally {
+         shipyardApi.close();
+         server.shutdown();
+      }
+   }
+   
+   public void testDeleteServiceKey() throws Exception {
+      MockWebServer server = mockShipyardWebServer();
+      server.enqueue(new MockResponse().setResponseCode(204));
+      ShipyardApi shipyardApi = api(server.getUrl("/"));
+      ServiceKeysApi api = shipyardApi.serviceKeysApi();
+      try {
+         boolean removed = api.deleteServiceKey("1111222233334444");
+         assertTrue(removed);
+         assertSent(server, "DELETE", "/api/servicekeys");
+      } finally {
+         shipyardApi.close();
+         server.shutdown();
+      }
+   }
+   
+   public void testDeleteNonExistentServiceKey() throws Exception {
+      MockWebServer server = mockShipyardWebServer();
+      server.enqueue(new MockResponse().setResponseCode(500).setBody("service key does not exist"));
+      ShipyardApi shipyardApi = api(server.getUrl("/"));
+      ServiceKeysApi api = shipyardApi.serviceKeysApi();
+      try {
+         Boolean removed = api.deleteServiceKey("NonExistentServiceKey");
+         assertFalse(removed);
+         assertSent(server, "DELETE", "/api/servicekeys");
+      } finally {
+         shipyardApi.close();
+         server.shutdown();
+      }
+   }
+   
+   @Test (expectedExceptions = HttpResponseException.class)
+   public void testDeleteNonExistentServiceKeyWithErroneousData() throws Exception {
+      MockWebServer server = mockShipyardWebServer();
+      server.enqueue(new MockResponse().setResponseCode(555).setBody("erroneous data"));
+      ShipyardApi shipyardApi = api(server.getUrl("/"));
+      ServiceKeysApi api = shipyardApi.serviceKeysApi();
+      try {
+         api.deleteServiceKey("NonExistentServiceKey");
+      } finally {
+         shipyardApi.close();
+         server.shutdown();
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/766caab0/shipyard/src/test/resources/servicekey-create.json
----------------------------------------------------------------------
diff --git a/shipyard/src/test/resources/servicekey-create.json b/shipyard/src/test/resources/servicekey-create.json
new file mode 100644
index 0000000..8e4be28
--- /dev/null
+++ b/shipyard/src/test/resources/servicekey-create.json
@@ -0,0 +1,3 @@
+{
+    "description": "jclouds-shipyard-testing"
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/766caab0/shipyard/src/test/resources/servicekey-response.json
----------------------------------------------------------------------
diff --git a/shipyard/src/test/resources/servicekey-response.json b/shipyard/src/test/resources/servicekey-response.json
new file mode 100644
index 0000000..a09a907
--- /dev/null
+++ b/shipyard/src/test/resources/servicekey-response.json
@@ -0,0 +1,5 @@
+{
+    "key": "1111222233334444",
+    "description": "jclouds-shipyard-testing"
+}
+

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/766caab0/shipyard/src/test/resources/servicekeys.json
----------------------------------------------------------------------
diff --git a/shipyard/src/test/resources/servicekeys.json b/shipyard/src/test/resources/servicekeys.json
new file mode 100644
index 0000000..b695192
--- /dev/null
+++ b/shipyard/src/test/resources/servicekeys.json
@@ -0,0 +1,9 @@
+[
+    {
+        "key": "aaaabbbbccccdddd"
+    },
+    {
+        "key": "1111222233334444",
+        "description": "testing service keys"
+    }
+]
\ No newline at end of file