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/02/24 17:06:03 UTC

[2/2] jclouds-labs git commit: Initial PR for jclouds Shipyard provider. This PR is concerned only with the Containers, Engines, and Images API's.

Initial PR for jclouds Shipyard provider. This PR is concerned only with the Containers, Engines, and Images API's.


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

Branch: refs/heads/master
Commit: 02bc515dd6177c5ba7b1798f401503c445462dcb
Parents: a34e62c
Author: ChristopherDancy <ch...@pega.com>
Authored: Mon Feb 23 12:52:04 2015 -0500
Committer: Ignasi Barrera <na...@apache.org>
Committed: Tue Feb 24 17:02:52 2015 +0100

----------------------------------------------------------------------
 .gitignore                                      |   2 +-
 pom.xml                                         |   2 +
 shipyard/README.md                              |  26 ++++
 shipyard/pom.xml                                | 146 ++++++++++++++++++
 .../java/org/jclouds/shipyard/ShipyardApi.java  |  36 +++++
 .../jclouds/shipyard/ShipyardApiMetadata.java   |  81 ++++++++++
 .../shipyard/config/ShipyardHttpApiModule.java  |  45 ++++++
 .../domain/containers/ContainerImageInfo.java   |  71 +++++++++
 .../domain/containers/ContainerInfo.java        |  58 +++++++
 .../domain/containers/DeployContainer.java      |  82 ++++++++++
 .../shipyard/domain/engines/AddEngine.java      |  45 ++++++
 .../shipyard/domain/engines/EngineInfo.java     |  37 +++++
 .../domain/engines/EngineSettingsInfo.java      |  54 +++++++
 .../shipyard/domain/images/ImageInfo.java       |  58 +++++++
 .../shipyard/domain/images/ImagePortsInfo.java  |  41 +++++
 .../shipyard/features/ContainersApi.java        |  69 +++++++++
 .../jclouds/shipyard/features/EnginesApi.java   |  59 +++++++
 .../jclouds/shipyard/features/ImagesApi.java    |  38 +++++
 .../filters/ServiceKeyAuthentication.java       |  55 +++++++
 .../shipyard/handlers/ShipyardErrorHandler.java |  96 ++++++++++++
 .../shipyard/ShipyardApiMetadataTest.java       |  49 ++++++
 .../features/ContainersApiLiveTest.java         | 153 +++++++++++++++++++
 .../features/ContainersApiMockTest.java         | 145 ++++++++++++++++++
 .../shipyard/features/EnginesApiLiveTest.java   |  90 +++++++++++
 .../shipyard/features/EnginesApiMockTest.java   | 134 ++++++++++++++++
 .../shipyard/features/ImagesApiLiveTest.java    |  42 +++++
 .../shipyard/features/ImagesApiMockTest.java    |  69 +++++++++
 .../internal/BaseShipyardApiLiveTest.java       |  47 ++++++
 .../shipyard/internal/BaseShipyardMockTest.java | 112 ++++++++++++++
 .../internal/BaseShipyardParseTest.java         |  31 ++++
 .../shipyard/parse/ContainersParseTest.java     |  67 ++++++++
 .../shipyard/parse/EnginesParseTest.java        |  51 +++++++
 .../jclouds/shipyard/parse/ImagesParseTest.java |  51 +++++++
 .../resources/container-deploy-response.json    |  49 ++++++
 .../src/test/resources/container-deploy.json    |  18 +++
 shipyard/src/test/resources/container.json      |  47 ++++++
 shipyard/src/test/resources/containers.json     |  49 ++++++
 shipyard/src/test/resources/engine-add.json     |  15 ++
 shipyard/src/test/resources/engine.json         |  12 ++
 shipyard/src/test/resources/engines.json        |  14 ++
 shipyard/src/test/resources/image.json          |  10 ++
 shipyard/src/test/resources/images.json         |  12 ++
 42 files changed, 2367 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/02bc515d/.gitignore
----------------------------------------------------------------------
diff --git a/.gitignore b/.gitignore
index 9fc2438..50f3821 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,4 +18,4 @@ atlassian-ide-plugin.xml
 .DS_Store
 .java-version
 *nb-configuration.xml
-*nbactions.xml
\ No newline at end of file
+*nbactions.xml

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/02bc515d/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 424b755..b5265bf 100644
--- a/pom.xml
+++ b/pom.xml
@@ -79,6 +79,7 @@
     <module>joyentcloud</module>
     <module>abiquo</module>
     <module>profitbricks</module>
+    <module>shipyard</module>
   </modules>
 
   <build>
@@ -188,3 +189,4 @@
     </profile>
   </profiles>
 </project>
+

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/02bc515d/shipyard/README.md
----------------------------------------------------------------------
diff --git a/shipyard/README.md b/shipyard/README.md
new file mode 100644
index 0000000..c394147
--- /dev/null
+++ b/shipyard/README.md
@@ -0,0 +1,26 @@
+#Shipyard provider for jclouds
+- jclouds-shipyard allows one to connect and administer multiple docker daemons from a single endpoint.
+
+--------------
+
+#Setup
+
+- Notes on how to standup a Shipyard instance can be found here:
+
+	https://github.com/shipyard/shipyard
+	
+- More detailed information (docs, API, etc..) can be found on their site:
+
+	http://shipyard-project.com/
+
+--------------
+
+#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

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/02bc515d/shipyard/pom.xml
----------------------------------------------------------------------
diff --git a/shipyard/pom.xml b/shipyard/pom.xml
new file mode 100644
index 0000000..30dfbf8
--- /dev/null
+++ b/shipyard/pom.xml
@@ -0,0 +1,146 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.jclouds.labs</groupId>
+    <artifactId>jclouds-labs</artifactId>
+    <version>2.0.0-SNAPSHOT</version>
+  </parent>
+
+  <!-- TODO: when out of labs, switch to org.jclouds.provider -->
+  <artifactId>shipyard</artifactId>
+  <name>jclouds shipyard API</name>
+  <description>ComputeService binding to the Shipyard API</description>
+  <packaging>bundle</packaging>
+
+  <properties>
+    <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.api-version>2.0.8</test.shipyard.api-version>
+    <jclouds.osgi.export>org.jclouds.shipyard*;version="${project.version}"</jclouds.osgi.export>
+    <jclouds.osgi.import>
+      org.jclouds.compute.internal;version="${project.version}",
+      org.jclouds.rest.internal;version="${project.version}",
+      org.jclouds*;version="${project.version}",
+      *
+    </jclouds.osgi.import>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.jclouds</groupId>
+      <artifactId>jclouds-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.jclouds</groupId>
+      <artifactId>jclouds-compute</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.google.auto.value</groupId>
+      <artifactId>auto-value</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.google.auto.service</groupId>
+      <artifactId>auto-service</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.jclouds.driver</groupId>
+      <artifactId>jclouds-okhttp</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>ch.qos.logback</groupId>
+      <artifactId>logback-classic</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.jclouds.driver</groupId>
+      <artifactId>jclouds-slf4j</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.jclouds</groupId>
+      <artifactId>jclouds-core</artifactId>
+      <version>${project.version}</version>
+      <type>test-jar</type>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.jclouds</groupId>
+      <artifactId>jclouds-compute</artifactId>
+      <version>${project.version}</version>
+      <type>test-jar</type>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.squareup.okhttp</groupId>
+      <artifactId>mockwebserver</artifactId>
+      <scope>test</scope>
+      <exclusions>
+        <!-- Already provided by jclouds-sshj -->
+        <exclusion>
+          <groupId>org.bouncycastle</groupId>
+          <artifactId>bcprov-jdk15on</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+  </dependencies>
+
+  <profiles>
+    <profile>
+      <id>live</id>
+      <build>
+        <plugins>
+          <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-surefire-plugin</artifactId>
+            <executions>
+              <execution>
+                <id>integration</id>
+                <phase>integration-test</phase>
+                <goals>
+                  <goal>test</goal>
+                </goals>
+                <configuration>
+                  <threadCount>1</threadCount>
+                  <systemPropertyVariables>
+                    <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.api-version>${test.shipyard.api-version}</test.shipyard.api-version>
+                  </systemPropertyVariables>
+                </configuration>
+              </execution>
+            </executions>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+  </profiles>
+
+</project>

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/02bc515d/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
new file mode 100644
index 0000000..c92a4b5
--- /dev/null
+++ b/shipyard/src/main/java/org/jclouds/shipyard/ShipyardApi.java
@@ -0,0 +1,36 @@
+/*
+ * 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;
+
+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 java.io.Closeable;
+
+public interface ShipyardApi extends Closeable {
+
+   @Delegate
+   ContainersApi containersApi();
+
+   @Delegate
+   ImagesApi imagesApi();
+   
+   @Delegate
+   EnginesApi enginesApi();
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/02bc515d/shipyard/src/main/java/org/jclouds/shipyard/ShipyardApiMetadata.java
----------------------------------------------------------------------
diff --git a/shipyard/src/main/java/org/jclouds/shipyard/ShipyardApiMetadata.java b/shipyard/src/main/java/org/jclouds/shipyard/ShipyardApiMetadata.java
new file mode 100644
index 0000000..ea4adb3
--- /dev/null
+++ b/shipyard/src/main/java/org/jclouds/shipyard/ShipyardApiMetadata.java
@@ -0,0 +1,81 @@
+/*
+ * 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;
+
+import com.google.auto.service.AutoService;
+import com.google.common.collect.ImmutableSet;
+import com.google.inject.Module;
+
+import org.jclouds.apis.ApiMetadata;
+import org.jclouds.rest.internal.BaseHttpApiMetadata;
+import org.jclouds.shipyard.config.ShipyardHttpApiModule;
+
+import java.net.URI;
+import java.util.Properties;
+
+@AutoService(ApiMetadata.class)
+public class ShipyardApiMetadata extends BaseHttpApiMetadata<ShipyardApi> {
+
+   @Override
+   public Builder toBuilder() {
+      return new Builder().fromApiMetadata(this);
+   }
+
+   public ShipyardApiMetadata() {
+      this(new Builder());
+   }
+
+   protected ShipyardApiMetadata(Builder builder) {
+      super(builder);
+   }
+
+   public static Properties defaultProperties() {
+      return BaseHttpApiMetadata.defaultProperties();
+   }
+
+   public static class Builder extends BaseHttpApiMetadata.Builder<ShipyardApi, Builder> {
+
+      protected Builder() {
+         super(ShipyardApi.class);
+         id("shipyard")
+           .name("Shipyard Remote Docker Management API")
+           .identityName("<shipyard-service-key>")
+           .credentialName("not used")
+           .documentation(URI.create("http://shipyard-project.com/docs/api/"))
+           .version("2.0.4")
+           .defaultEndpoint("https://127.0.0.1:8080")
+           .defaultProperties(ShipyardApiMetadata.defaultProperties())
+           .defaultModules(ImmutableSet.<Class<? extends Module>>of(
+                   ShipyardHttpApiModule.class));
+      }
+
+      @Override
+      public ShipyardApiMetadata build() {
+         return new ShipyardApiMetadata(this);
+      }
+
+      @Override
+      protected Builder self() {
+         return this;
+      }
+
+      @Override
+      public Builder fromApiMetadata(ApiMetadata in) {
+         return this;
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/02bc515d/shipyard/src/main/java/org/jclouds/shipyard/config/ShipyardHttpApiModule.java
----------------------------------------------------------------------
diff --git a/shipyard/src/main/java/org/jclouds/shipyard/config/ShipyardHttpApiModule.java b/shipyard/src/main/java/org/jclouds/shipyard/config/ShipyardHttpApiModule.java
new file mode 100644
index 0000000..adcfc38
--- /dev/null
+++ b/shipyard/src/main/java/org/jclouds/shipyard/config/ShipyardHttpApiModule.java
@@ -0,0 +1,45 @@
+/*
+ * 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.config;
+
+import org.jclouds.http.HttpErrorHandler;
+import org.jclouds.http.annotation.ClientError;
+import org.jclouds.http.annotation.Redirection;
+import org.jclouds.http.annotation.ServerError;
+import org.jclouds.http.config.ConfiguresHttpCommandExecutorService;
+import org.jclouds.http.okhttp.config.OkHttpCommandExecutorServiceModule;
+import org.jclouds.rest.ConfiguresHttpApi;
+import org.jclouds.rest.config.HttpApiModule;
+import org.jclouds.shipyard.ShipyardApi;
+import org.jclouds.shipyard.handlers.ShipyardErrorHandler;
+
+@ConfiguresHttpApi
+@ConfiguresHttpCommandExecutorService
+public class ShipyardHttpApiModule extends HttpApiModule<ShipyardApi> {
+
+   @Override
+   protected void bindErrorHandlers() {
+      bind(HttpErrorHandler.class).annotatedWith(Redirection.class).to(ShipyardErrorHandler.class);
+      bind(HttpErrorHandler.class).annotatedWith(ClientError.class).to(ShipyardErrorHandler.class);
+      bind(HttpErrorHandler.class).annotatedWith(ServerError.class).to(ShipyardErrorHandler.class);
+   }
+   
+   protected void configure() {
+      super.configure();
+      install(new OkHttpCommandExecutorServiceModule());
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/02bc515d/shipyard/src/main/java/org/jclouds/shipyard/domain/containers/ContainerImageInfo.java
----------------------------------------------------------------------
diff --git a/shipyard/src/main/java/org/jclouds/shipyard/domain/containers/ContainerImageInfo.java b/shipyard/src/main/java/org/jclouds/shipyard/domain/containers/ContainerImageInfo.java
new file mode 100644
index 0000000..315e787
--- /dev/null
+++ b/shipyard/src/main/java/org/jclouds/shipyard/domain/containers/ContainerImageInfo.java
@@ -0,0 +1,71 @@
+/*
+ * 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.containers;
+
+import java.util.List;
+import java.util.Map;
+
+import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.json.SerializedNames;
+import org.jclouds.shipyard.domain.images.ImagePortsInfo;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+
+@AutoValue
+public abstract class ContainerImageInfo {
+   
+   @Nullable public abstract String name();
+   
+   public abstract Map<String, String> environment();
+   
+   public abstract List<String> entryPoint();
+   
+   @Nullable public abstract String hostName();
+   
+   public abstract List<ImagePortsInfo> bindPorts();
+   
+   public abstract List<String> volumes();
+   
+   public abstract Map<String, String> restartPolicy();
+   
+   public abstract boolean publish();
+   
+   @Nullable public abstract String networkMode();
+      
+   ContainerImageInfo() {
+   }
+
+   @SerializedNames({ "name", "environment", "entrypoint", "hostname", "bind_ports", "volumes", "restart_policy", "publish", "network_mode" })
+   public static ContainerImageInfo create(String name, Map<String, String> environment, List<String> entryPoint,
+                                             String hostName, List<ImagePortsInfo> bindPorts,
+                                             List<String> volumes, Map<String, String> restartPolicy,
+                                             boolean publish, String networkMode) {
+      
+      if (environment == null) environment = Maps.newHashMap();
+      if (entryPoint == null) entryPoint = Lists.newArrayList();
+      if (bindPorts == null) bindPorts = Lists.newArrayList();
+      if (volumes == null) volumes = Lists.newArrayList();
+      if (restartPolicy == null) restartPolicy = Maps.newHashMap();
+         
+      return new AutoValue_ContainerImageInfo(name, environment, entryPoint, 
+                                             hostName, bindPorts, 
+                                             volumes, restartPolicy, 
+                                             publish, networkMode);         
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/02bc515d/shipyard/src/main/java/org/jclouds/shipyard/domain/containers/ContainerInfo.java
----------------------------------------------------------------------
diff --git a/shipyard/src/main/java/org/jclouds/shipyard/domain/containers/ContainerInfo.java b/shipyard/src/main/java/org/jclouds/shipyard/domain/containers/ContainerInfo.java
new file mode 100644
index 0000000..8e6404d
--- /dev/null
+++ b/shipyard/src/main/java/org/jclouds/shipyard/domain/containers/ContainerInfo.java
@@ -0,0 +1,58 @@
+/*
+ * 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.containers;
+
+import java.util.List;
+
+import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.json.SerializedNames;
+import org.jclouds.shipyard.domain.engines.EngineSettingsInfo;
+import org.jclouds.shipyard.domain.images.ImagePortsInfo;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.Lists;
+
+@AutoValue
+public abstract class ContainerInfo {
+
+   public abstract String id();
+   
+   @Nullable public abstract String name();
+   
+   public abstract ContainerImageInfo image();
+   
+   public abstract EngineSettingsInfo engine();
+   
+   public abstract String state();
+   
+   public abstract List<ImagePortsInfo> ports();
+   
+   ContainerInfo() {
+   }
+
+   @SerializedNames({ "id", "name", "image", "engine", "state", "ports" })
+   public static ContainerInfo create(String id, String name, 
+                                       ContainerImageInfo image,
+                                       EngineSettingsInfo engine,
+                                       String state, List<ImagePortsInfo> ports) {
+      
+      if (state == null) state = "unknown";
+      if (ports == null) ports = Lists.newArrayList();
+      
+      return new AutoValue_ContainerInfo(id, name, image, engine, state, ports);
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/02bc515d/shipyard/src/main/java/org/jclouds/shipyard/domain/containers/DeployContainer.java
----------------------------------------------------------------------
diff --git a/shipyard/src/main/java/org/jclouds/shipyard/domain/containers/DeployContainer.java b/shipyard/src/main/java/org/jclouds/shipyard/domain/containers/DeployContainer.java
new file mode 100644
index 0000000..e1352ae
--- /dev/null
+++ b/shipyard/src/main/java/org/jclouds/shipyard/domain/containers/DeployContainer.java
@@ -0,0 +1,82 @@
+/*
+ * 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.containers;
+
+import java.util.List;
+import java.util.Map;
+
+import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.json.SerializedNames;
+import org.jclouds.shipyard.domain.images.ImagePortsInfo;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+
+@AutoValue
+public abstract class DeployContainer {
+
+   public abstract String name();
+   
+   public abstract String containerName();
+   
+   public abstract double cpus();
+   
+   public abstract int memory();
+   
+   public abstract String type();
+   
+   @Nullable public abstract String hostName();
+   
+   @Nullable public abstract String domain();
+   
+   public abstract List<String> labels();
+   
+   public abstract List<String> args();
+   
+   public abstract Map<String, String> environment();
+   
+   public abstract Map<String, String> restartPolicy();
+   
+   public abstract List<ImagePortsInfo> bindPorts();
+   
+   public abstract Map<String, String> links();
+   
+   DeployContainer() {
+   }
+
+   @SerializedNames({ "name", "container_name", "cpus", "memory", "type", "hostname", "domain", "labels", "args", "environment", "restart_policy", "bind_ports", "links" })
+   public static DeployContainer create(String name, String containerName, double cpus, 
+                                       int memory, String type, String hostName, 
+                                       String domain, List<String> labels, List<String> args,
+                                       Map<String, String> environment, Map<String, String> restartPolicy,
+                                       List<ImagePortsInfo> bindPorts, Map<String, String> links) {
+      
+      if (labels == null) labels = Lists.newArrayList();
+      if (args == null) args = Lists.newArrayList();
+      if (environment == null) environment = Maps.newHashMap();
+      if (restartPolicy == null) restartPolicy = Maps.newHashMap();
+      if (bindPorts == null) bindPorts = Lists.newArrayList();
+      if (links == null) links = Maps.newHashMap();
+         
+      return new AutoValue_DeployContainer(name, containerName, cpus, 
+                                          memory, "service", hostName, 
+                                          domain, labels, args, 
+                                          environment, restartPolicy, bindPorts, 
+                                          links);         
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/02bc515d/shipyard/src/main/java/org/jclouds/shipyard/domain/engines/AddEngine.java
----------------------------------------------------------------------
diff --git a/shipyard/src/main/java/org/jclouds/shipyard/domain/engines/AddEngine.java b/shipyard/src/main/java/org/jclouds/shipyard/domain/engines/AddEngine.java
new file mode 100644
index 0000000..683568d
--- /dev/null
+++ b/shipyard/src/main/java/org/jclouds/shipyard/domain/engines/AddEngine.java
@@ -0,0 +1,45 @@
+/*
+ * 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.engines;
+
+import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.json.SerializedNames;
+
+import com.google.auto.value.AutoValue;
+
+@AutoValue
+public abstract class AddEngine {
+
+   public abstract String local();
+   
+   @Nullable abstract String sslCert();
+   
+   @Nullable abstract String sslKey();
+   
+   @Nullable abstract String caCert();
+   
+   public abstract EngineSettingsInfo engine();
+   
+   AddEngine() {
+   }
+
+   @SerializedNames({ "local", "ssl_cert", "ssl_key", "ca_cert", "engine" })
+   public static AddEngine create(String local, String sslCert, 
+                                 String sslKey, String caCert, EngineSettingsInfo engine) {
+      return new AutoValue_AddEngine(local, sslCert, sslKey, caCert, engine);
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/02bc515d/shipyard/src/main/java/org/jclouds/shipyard/domain/engines/EngineInfo.java
----------------------------------------------------------------------
diff --git a/shipyard/src/main/java/org/jclouds/shipyard/domain/engines/EngineInfo.java b/shipyard/src/main/java/org/jclouds/shipyard/domain/engines/EngineInfo.java
new file mode 100644
index 0000000..f7bbf89
--- /dev/null
+++ b/shipyard/src/main/java/org/jclouds/shipyard/domain/engines/EngineInfo.java
@@ -0,0 +1,37 @@
+/*
+ * 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.engines;
+
+import org.jclouds.json.SerializedNames;
+
+import com.google.auto.value.AutoValue;
+
+@AutoValue
+public abstract class EngineInfo {
+
+   public abstract String id();
+      
+   public abstract EngineSettingsInfo engine();
+
+   EngineInfo() {
+   }
+
+   @SerializedNames({ "id", "engine" })
+   public static EngineInfo create(String id, EngineSettingsInfo engine) {
+      return new AutoValue_EngineInfo(id, engine);
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/02bc515d/shipyard/src/main/java/org/jclouds/shipyard/domain/engines/EngineSettingsInfo.java
----------------------------------------------------------------------
diff --git a/shipyard/src/main/java/org/jclouds/shipyard/domain/engines/EngineSettingsInfo.java b/shipyard/src/main/java/org/jclouds/shipyard/domain/engines/EngineSettingsInfo.java
new file mode 100644
index 0000000..1a5f923
--- /dev/null
+++ b/shipyard/src/main/java/org/jclouds/shipyard/domain/engines/EngineSettingsInfo.java
@@ -0,0 +1,54 @@
+/*
+ * 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.engines;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
+import java.util.List;
+
+import org.jclouds.json.SerializedNames;
+
+import com.google.auto.value.AutoValue;
+
+@AutoValue
+public abstract class EngineSettingsInfo {
+
+   public abstract String id();
+      
+   public abstract String addr();
+   
+   public abstract double cpus();
+   
+   public abstract double memory();
+   
+   public abstract List<String> labels();
+
+   EngineSettingsInfo() {
+   }
+
+   @SerializedNames({ "id", "addr", "cpus", "memory", "labels" })
+   public static EngineSettingsInfo create(String id, String addr, 
+                                          double cpus, double memory, 
+                                          List<String> labels) {
+      
+      checkNotNull(labels, "labels must be non-null");
+      checkState(labels.size() > 0, "labels must have at least 1 entry");
+      
+      return new AutoValue_EngineSettingsInfo(id, addr, cpus, memory, labels);
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/02bc515d/shipyard/src/main/java/org/jclouds/shipyard/domain/images/ImageInfo.java
----------------------------------------------------------------------
diff --git a/shipyard/src/main/java/org/jclouds/shipyard/domain/images/ImageInfo.java b/shipyard/src/main/java/org/jclouds/shipyard/domain/images/ImageInfo.java
new file mode 100644
index 0000000..c18bf30
--- /dev/null
+++ b/shipyard/src/main/java/org/jclouds/shipyard/domain/images/ImageInfo.java
@@ -0,0 +1,58 @@
+/*
+ * 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.images;
+
+import java.util.List;
+
+import org.jclouds.domain.Location;
+import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.json.SerializedNames;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.Lists;
+
+@AutoValue
+public abstract class ImageInfo {
+   
+   public abstract int created();
+   
+   @Nullable public abstract String id();
+   
+   public abstract String parentId();
+   
+   public abstract List<String> repoTags();
+   
+   public abstract int size();
+   
+   public abstract int virtualSize();
+   
+   @Nullable abstract Location location();
+
+   ImageInfo() {
+   }
+
+   @SerializedNames({ "Created", "Id", "ParentId", "RepoTags", "Size", "VirtualSize" , "Location"})
+   public static ImageInfo create(int created, String id, 
+                                 String parentId, List<String> repoTags, 
+                                 int size, int virtualSize, 
+                                 Location location) {
+      
+      if (repoTags == null) repoTags = Lists.newArrayList();
+      
+      return new AutoValue_ImageInfo(created, id, parentId, repoTags, size, virtualSize, location);
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/02bc515d/shipyard/src/main/java/org/jclouds/shipyard/domain/images/ImagePortsInfo.java
----------------------------------------------------------------------
diff --git a/shipyard/src/main/java/org/jclouds/shipyard/domain/images/ImagePortsInfo.java b/shipyard/src/main/java/org/jclouds/shipyard/domain/images/ImagePortsInfo.java
new file mode 100644
index 0000000..1728e2e
--- /dev/null
+++ b/shipyard/src/main/java/org/jclouds/shipyard/domain/images/ImagePortsInfo.java
@@ -0,0 +1,41 @@
+/*
+ * 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.images;
+
+import org.jclouds.json.SerializedNames;
+
+import com.google.auto.value.AutoValue;
+
+@AutoValue
+public abstract class ImagePortsInfo {
+   
+   public abstract String proto();
+   
+   public abstract String hostIp();
+   
+   public abstract int port();
+   
+   public abstract int containerPort();
+
+   ImagePortsInfo() {
+   }
+
+   @SerializedNames({ "proto", "host_ip", "port", "container_port" })
+   public static ImagePortsInfo create(String proto, String hostIp, int port, int containerPort) {
+      return new AutoValue_ImagePortsInfo(proto, hostIp, port, containerPort);
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/02bc515d/shipyard/src/main/java/org/jclouds/shipyard/features/ContainersApi.java
----------------------------------------------------------------------
diff --git a/shipyard/src/main/java/org/jclouds/shipyard/features/ContainersApi.java b/shipyard/src/main/java/org/jclouds/shipyard/features/ContainersApi.java
new file mode 100644
index 0000000..0ca98fc
--- /dev/null
+++ b/shipyard/src/main/java/org/jclouds/shipyard/features/ContainersApi.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.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.core.MediaType;
+
+import org.jclouds.rest.annotations.BinderParam;
+import org.jclouds.rest.annotations.RequestFilters;
+import org.jclouds.rest.binders.BindToJsonPayload;
+import org.jclouds.shipyard.domain.containers.ContainerInfo;
+import org.jclouds.shipyard.domain.containers.DeployContainer;
+import org.jclouds.shipyard.filters.ServiceKeyAuthentication;
+
+@Consumes(MediaType.APPLICATION_JSON)
+@RequestFilters({ ServiceKeyAuthentication.class })
+@Path("/api/containers")
+public interface ContainersApi {
+
+   @Named("containers:list")
+   @GET
+   List<ContainerInfo> listContainers();
+   
+   @Named("containers:info")
+   @GET
+   @Path("/{id}")
+   ContainerInfo getContainer(@PathParam("id") String id);
+   
+   @Named("containers:delete")
+   @DELETE
+   @Path("/{id}")
+   void deleteContainer(@PathParam("id") String id);
+   
+   @Named("containers:stop")
+   @GET
+   @Path("/{id}/stop")
+   void stopContainer(@PathParam("id") String id);
+   
+   @Named("containers:restart")
+   @GET
+   @Path("/{id}/restart")
+   void restartContainer(@PathParam("id") String id);
+   
+   @Named("containers:deploy")
+   @POST
+   List<ContainerInfo> deployContainer(@BinderParam(BindToJsonPayload.class) DeployContainer deployContainer);
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/02bc515d/shipyard/src/main/java/org/jclouds/shipyard/features/EnginesApi.java
----------------------------------------------------------------------
diff --git a/shipyard/src/main/java/org/jclouds/shipyard/features/EnginesApi.java b/shipyard/src/main/java/org/jclouds/shipyard/features/EnginesApi.java
new file mode 100644
index 0000000..bcb027c
--- /dev/null
+++ b/shipyard/src/main/java/org/jclouds/shipyard/features/EnginesApi.java
@@ -0,0 +1,59 @@
+/*
+ * 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.core.MediaType;
+
+import org.jclouds.rest.annotations.BinderParam;
+import org.jclouds.rest.annotations.RequestFilters;
+import org.jclouds.rest.binders.BindToJsonPayload;
+import org.jclouds.shipyard.domain.engines.AddEngine;
+import org.jclouds.shipyard.domain.engines.EngineInfo;
+import org.jclouds.shipyard.filters.ServiceKeyAuthentication;
+
+@Consumes(MediaType.APPLICATION_JSON)
+@RequestFilters({ ServiceKeyAuthentication.class })
+@Path("/api/engines")
+public interface EnginesApi {
+
+   @Named("engines:list-info")
+   @GET
+   List<EngineInfo> listEngines();
+
+   @Named("engines:info")
+   @GET
+   @Path("/{id}")
+   EngineInfo getEngine(@PathParam("id") String engineID);
+   
+   @Named("engines:add")
+   @POST
+   void addEngine(@BinderParam(BindToJsonPayload.class) AddEngine addEngine);
+   
+   @Named("engines:remove")
+   @DELETE
+   @Path("/{id}")
+   void removeEngine(@PathParam("id") String engineID);
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/02bc515d/shipyard/src/main/java/org/jclouds/shipyard/features/ImagesApi.java
----------------------------------------------------------------------
diff --git a/shipyard/src/main/java/org/jclouds/shipyard/features/ImagesApi.java b/shipyard/src/main/java/org/jclouds/shipyard/features/ImagesApi.java
new file mode 100644
index 0000000..80dad17
--- /dev/null
+++ b/shipyard/src/main/java/org/jclouds/shipyard/features/ImagesApi.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.features;
+
+import java.net.URI;
+import java.util.List;
+
+import javax.inject.Named;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.MediaType;
+
+import org.jclouds.rest.annotations.EndpointParam;
+import org.jclouds.shipyard.domain.images.ImageInfo;
+
+@Consumes(MediaType.APPLICATION_JSON)
+@Path("/images/json")
+public interface ImagesApi {
+
+   @Named("images:list")
+   @GET
+   List<ImageInfo> listImages(@EndpointParam URI rootURL);
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/02bc515d/shipyard/src/main/java/org/jclouds/shipyard/filters/ServiceKeyAuthentication.java
----------------------------------------------------------------------
diff --git a/shipyard/src/main/java/org/jclouds/shipyard/filters/ServiceKeyAuthentication.java b/shipyard/src/main/java/org/jclouds/shipyard/filters/ServiceKeyAuthentication.java
new file mode 100644
index 0000000..9750bb1
--- /dev/null
+++ b/shipyard/src/main/java/org/jclouds/shipyard/filters/ServiceKeyAuthentication.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.filters;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.jclouds.domain.Credentials;
+import org.jclouds.http.HttpException;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.HttpRequestFilter;
+import org.jclouds.location.Provider;
+import org.jclouds.rest.AuthorizationException;
+
+import com.google.common.base.Supplier;
+
+/**
+ * Shipyard remote API authentication is made via the HTTP header 'X-Service-Key' which in turns
+ * has it's value as an encoded string (Shipyard-cli generates this for you).
+ * 
+ */
+@Singleton
+public class ServiceKeyAuthentication implements HttpRequestFilter {
+   private final Supplier<Credentials> creds;
+
+   @Inject
+   ServiceKeyAuthentication(@Provider Supplier<Credentials> creds) {
+      this.creds = creds;
+   }
+
+   @Override
+   public HttpRequest filter(HttpRequest request) throws HttpException {
+      Credentials currentCreds = checkNotNull(creds.get(), "credential supplier returned null");
+      if (currentCreds.identity == null) {
+         throw new AuthorizationException("Credentials identity can not be null");
+      }
+      return request.toBuilder().addHeader("X-Service-Key", currentCreds.identity).build();
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/02bc515d/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
new file mode 100644
index 0000000..0e43814
--- /dev/null
+++ b/shipyard/src/main/java/org/jclouds/shipyard/handlers/ShipyardErrorHandler.java
@@ -0,0 +1,96 @@
+/*
+ * 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.handlers;
+
+import static org.jclouds.util.Closeables2.closeQuietly;
+
+import java.io.IOException;
+
+import javax.annotation.Resource;
+
+import org.jclouds.http.HttpCommand;
+import org.jclouds.http.HttpErrorHandler;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.http.HttpResponseException;
+import org.jclouds.logging.Logger;
+import org.jclouds.rest.AuthorizationException;
+import org.jclouds.util.Strings2;
+
+import com.google.common.base.Throwables;
+
+/**
+ * Handle errors and propagate exception
+ * 
+ */
+public class ShipyardErrorHandler implements HttpErrorHandler {
+   @Resource
+   protected Logger logger = Logger.NULL;
+
+   public void handleError(HttpCommand command, HttpResponse 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:
+               exception = new AuthorizationException(message, exception);
+               break;
+            case 404:
+               if (command.getCurrentRequest().getMethod().equals("GET")) {
+                  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")) {
+                     exception = new HttpResponseException("Container already exists", command, response);
+                  }
+               }
+               break;
+            case 500:
+               if (command.getCurrentRequest().getMethod().equals("POST")) {
+                  if (command.getCurrentRequest().getEndpoint().getPath().endsWith("/engines")) {
+                     exception = new HttpResponseException("Connection refused registering docker daemon", command, response);
+                  }
+               }
+               break;
+            default:
+               exception = new HttpResponseException(message, command, response);
+               break;
+         }
+      } catch (Exception e) {
+         exception = new HttpResponseException(command, response, e);
+      } finally {
+         closeQuietly(response.getPayload());
+         command.setException(exception);
+      }
+   }
+
+   private String parseMessage(HttpResponse response) {
+      if (response.getPayload() == null)
+         return null;
+      try {
+         return Strings2.toStringAndClose(response.getPayload().openStream());         
+      } catch (IOException e) {
+         throw Throwables.propagate(e);
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/02bc515d/shipyard/src/test/java/org/jclouds/shipyard/ShipyardApiMetadataTest.java
----------------------------------------------------------------------
diff --git a/shipyard/src/test/java/org/jclouds/shipyard/ShipyardApiMetadataTest.java b/shipyard/src/test/java/org/jclouds/shipyard/ShipyardApiMetadataTest.java
new file mode 100644
index 0000000..b6db473
--- /dev/null
+++ b/shipyard/src/test/java/org/jclouds/shipyard/ShipyardApiMetadataTest.java
@@ -0,0 +1,49 @@
+/*
+ * 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;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+import java.util.HashSet;
+import org.jclouds.View;
+import org.jclouds.apis.ApiMetadata;
+import org.jclouds.apis.Apis;
+import org.jclouds.apis.internal.BaseApiMetadataTest;
+import org.testng.annotations.Test;
+
+import com.google.common.reflect.TypeToken;
+
+/**
+ * Unit tests for the {@link ShipyardApiMetadata} class.
+ */
+@Test(groups = "unit", testName = "ShipyardApiMetadataTest")
+public class ShipyardApiMetadataTest extends BaseApiMetadataTest {
+
+   public ShipyardApiMetadataTest() {  
+      super(new ShipyardApiMetadata(), new HashSet<TypeToken<? extends View>>());
+   }
+
+   public void testShipyardApiRegistered() {
+      ApiMetadata api = Apis.withId("shipyard");
+
+      assertNotNull(api);
+      assertTrue(api instanceof ShipyardApiMetadata);
+      assertEquals(api.getId(), "shipyard");
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/02bc515d/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
new file mode 100644
index 0000000..8ec10d1
--- /dev/null
+++ b/shipyard/src/test/java/org/jclouds/shipyard/features/ContainersApiLiveTest.java
@@ -0,0 +1,153 @@
+/*
+ * 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.assertNull;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+import java.util.List;
+
+import org.jclouds.http.HttpResponseException;
+import org.jclouds.shipyard.domain.containers.ContainerInfo;
+import org.jclouds.shipyard.domain.containers.DeployContainer;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+
+
+@Test(groups = "live", testName = "ContainersApiLiveTest", singleThreaded = true)
+public class ContainersApiLiveTest extends EnginesApiLiveTest {
+  
+   private String containerName = "shipyard-jclouds-container-test";
+   private String containerID = null;
+   
+   @BeforeClass
+   protected void init() throws Exception {
+      super.init();
+      super.testGetAllEngines();
+   }
+   
+   @AfterClass (alwaysRun = true)
+   protected void tearDown() {
+      super.tearDown();
+   }
+   
+   public void testDeployContainer() throws Exception {
+
+      DeployContainer deployContainer = DeployContainer.create("ubuntu:14.04.1", 
+            containerName, 
+            1, 
+            512, 
+            null,
+            null, 
+            null, 
+            Lists.newArrayList(EnginesApiLiveTest.engineName), 
+            Lists.newArrayList("/bin/bash"), 
+            ImmutableMap.of("shipyard-jclouds", "test"), 
+            null, 
+            null, 
+            null);
+      
+      List<ContainerInfo> container = api().deployContainer(deployContainer);
+      assertNotNull(container, "Expected valid container but returned NULL");
+      assertTrue(container.size() == 1, "Expected exactly 1 container removed and found " + container.size());
+      assertTrue(container.get(0).name().endsWith(containerName), 
+               "Expected name does not match actual name: requested=" + containerName + ", actual=" + container.get(0).name());
+      assertTrue(container.get(0).engine().labels().contains(EnginesApiLiveTest.engineName), 
+               "Expected label was not found in container: expected=" + EnginesApiLiveTest.engineName + ", found=" + container.get(0).engine().labels());
+      containerID = container.get(0).id();
+   }
+   
+   @Test (dependsOnMethods = "testDeployContainer")
+   public void testDeployAlreadyExistentContainer() throws Exception {
+
+      DeployContainer deployContainer = DeployContainer.create("ubuntu:14.04.1", 
+            containerName, 
+            1, 
+            512, 
+            null,
+            null, 
+            null, 
+            Lists.newArrayList(EnginesApiLiveTest.engineName), 
+            Lists.newArrayList("/bin/bash"), 
+            ImmutableMap.of("shipyard-jclouds", "test"), 
+            null, 
+            null, 
+            null);
+      
+      List<ContainerInfo> container = api().deployContainer(deployContainer);
+      assertNull(container);
+   }
+   
+   public void testDeployNonExistentContainer() throws Exception {
+
+      DeployContainer deployContainer = DeployContainer.create("jclouds-shipyard-test:99.99.99", 
+            containerName, 
+            1, 
+            512, 
+            null,
+            null, 
+            null, 
+            Lists.newArrayList(EnginesApiLiveTest.engineName), 
+            Lists.newArrayList("/bin/bash"), 
+            ImmutableMap.of("shipyard-jclouds", "test"), 
+            null, 
+            null, 
+            null);
+      
+      List<ContainerInfo> container = api().deployContainer(deployContainer);
+      assertNull(container);
+   }
+   
+   @Test (dependsOnMethods = "testDeployAlreadyExistentContainer")
+   public void testStopContainer() throws Exception {
+      api().stopContainer(containerID);
+   }
+   
+   @Test (expectedExceptions = HttpResponseException.class)
+   public void testStopNonExistentContainer() throws Exception {
+      api().stopContainer("aaabbbccc111222333");
+   }
+   
+   @Test (dependsOnMethods = "testStopContainer")
+   public void testRestartContainer() throws Exception {
+      api().restartContainer(containerID);
+   }
+   
+   @Test (expectedExceptions = HttpResponseException.class)
+   public void testRestartNonExistentContainer() throws Exception {
+      api().restartContainer("aaabbbccc111222333");
+   }
+   
+   @Test (dependsOnMethods = "testRestartContainer")
+   public void testDeleteContainer() throws Exception {
+      api().deleteContainer(containerID);
+   }
+   
+   @Test (expectedExceptions = HttpResponseException.class)
+   public void testDeleteNonExistentContainer() throws Exception {
+      api().deleteContainer("aaabbbccc111222333");
+   }
+   
+   private ContainersApi api() {
+      return api.containersApi();
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/02bc515d/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
new file mode 100644
index 0000000..ca30e28
--- /dev/null
+++ b/shipyard/src/test/java/org/jclouds/shipyard/features/ContainersApiMockTest.java
@@ -0,0 +1,145 @@
+/*
+ * 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 java.util.List;
+
+import org.jclouds.shipyard.ShipyardApi;
+import org.jclouds.shipyard.domain.containers.ContainerInfo;
+import org.jclouds.shipyard.domain.containers.DeployContainer;
+import org.jclouds.shipyard.internal.BaseShipyardMockTest;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+import com.squareup.okhttp.mockwebserver.MockResponse;
+import com.squareup.okhttp.mockwebserver.MockWebServer;
+
+/**
+ * Mock tests for the {@link org.jclouds.shipyard.features.ContainersApi} class.
+ */
+@Test(groups = "unit", testName = "ContainersApiMockTest")
+public class ContainersApiMockTest extends BaseShipyardMockTest {
+
+   
+   public void testListContainers() throws Exception {
+      MockWebServer server = mockShipyardWebServer();
+      server.enqueue(new MockResponse().setBody(payloadFromResource("/containers.json")));
+      ShipyardApi shipyardApi = api(server.getUrl("/"));
+      ContainersApi api = shipyardApi.containersApi();
+      try {
+         assertEquals(api.listContainers().size(), 1);
+         assertSent(server, "GET", "/api/containers");
+      } finally {
+         shipyardApi.close();
+         server.shutdown();
+      }
+   }
+
+   public void testGetContainer() throws Exception {
+      MockWebServer server = mockShipyardWebServer();
+      server.enqueue(new MockResponse().setBody(payloadFromResource("/container.json")));
+      ShipyardApi shipyardApi = api(server.getUrl("/"));
+      ContainersApi api = shipyardApi.containersApi();
+      String containerId = "e2f6784b75ed8768e83b7ec46ca8ef784941f6ce4c53231023804277965da1d2";
+      try {
+         assertEquals(api.getContainer(containerId).id(), containerId);
+         assertSent(server, "GET", "/api/containers/" + containerId);
+      } finally {
+         shipyardApi.close();
+         server.shutdown();
+      }
+   }
+
+   public void testDeployContainer() throws Exception {
+      MockWebServer server = mockShipyardWebServer();
+      server.enqueue(new MockResponse().setResponseCode(201).setBody(payloadFromResource("/container-deploy-response.json")));
+      ShipyardApi shipyardApi = api(server.getUrl("/"));
+      ContainersApi api = shipyardApi.containersApi();
+
+      DeployContainer deployContainer = DeployContainer.create("nkatsaros/atlassian-stash:3.5", 
+                                                         "atlassian-stash", 
+                                                         8, 
+                                                         8096, 
+                                                         null,
+                                                         null, 
+                                                         null, 
+                                                         Lists.newArrayList("localhost"), 
+                                                         null, 
+                                                         ImmutableMap.of("STASH_HOME", "/var/atlassian/stash", "STASH_VERSION", "3.5.0"), 
+                                                         null, 
+                                                         null, 
+                                                         null);
+      try {
+         List<ContainerInfo> container = api.deployContainer(deployContainer);
+         assertNotNull(container);
+         assertEquals(container.get(0).id(), "e2f6784b75ed8768e83b7ec46ca8ef784941f6ce4c53231023804277965da1d2");
+         assertSent(server, "POST", "/api/containers", new String(payloadFromResource("/container-deploy.json")));
+      } finally {
+         shipyardApi.close();
+         server.shutdown();
+      }
+   }
+
+   public void testDeleteContainer() throws Exception {
+      MockWebServer server = mockShipyardWebServer();
+      server.enqueue(new MockResponse().setResponseCode(204));
+      ShipyardApi shipyardApi = api(server.getUrl("/"));
+      ContainersApi api = shipyardApi.containersApi();
+      String containerId = "e2f6784b75ed8768e83b7ec46ca8ef784941f6ce4c53231023804277965da1d2";
+      try {
+         api.deleteContainer(containerId);
+         assertSent(server, "DELETE", "/api/containers/" + containerId);
+      } finally {
+         shipyardApi.close();
+         server.shutdown();
+      }
+   }
+
+   public void testStopContainer() throws Exception {
+      MockWebServer server = mockShipyardWebServer();
+      server.enqueue(new MockResponse().setResponseCode(204));
+      ShipyardApi shipyardApi = api(server.getUrl("/"));
+      ContainersApi api = shipyardApi.containersApi();
+      String containerId = "e2f6784b75ed8768e83b7ec46ca8ef784941f6ce4c53231023804277965da1d2";
+      try {
+         api.stopContainer(containerId);
+         assertSent(server, "GET", "/api/containers/" + containerId + "/stop");
+      } finally {
+         shipyardApi.close();
+         server.shutdown();
+      }
+   }
+
+   public void testStartOrRestartContainer() throws Exception {
+      MockWebServer server = mockShipyardWebServer();
+      server.enqueue(new MockResponse().setResponseCode(204));
+      ShipyardApi shipyardApi = api(server.getUrl("/"));
+      ContainersApi api = shipyardApi.containersApi();
+      String containerId = "e2f6784b75ed8768e83b7ec46ca8ef784941f6ce4c53231023804277965da1d2";
+      try {
+         api.restartContainer(containerId);
+         assertSent(server, "GET", "/api/containers/" + containerId + "/restart");
+      } finally {
+         shipyardApi.close();
+         server.shutdown();
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/02bc515d/shipyard/src/test/java/org/jclouds/shipyard/features/EnginesApiLiveTest.java
----------------------------------------------------------------------
diff --git a/shipyard/src/test/java/org/jclouds/shipyard/features/EnginesApiLiveTest.java b/shipyard/src/test/java/org/jclouds/shipyard/features/EnginesApiLiveTest.java
new file mode 100644
index 0000000..8a45468
--- /dev/null
+++ b/shipyard/src/test/java/org/jclouds/shipyard/features/EnginesApiLiveTest.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 static org.testng.Assert.assertTrue;
+import static org.testng.Assert.assertNotNull;
+
+import org.jclouds.http.HttpResponseException;
+import org.jclouds.shipyard.domain.engines.AddEngine;
+import org.jclouds.shipyard.domain.engines.EngineInfo;
+import org.jclouds.shipyard.domain.engines.EngineSettingsInfo;
+import org.jclouds.shipyard.internal.BaseShipyardApiLiveTest;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.Lists;
+
+@Test(groups = "live", testName = "EnginesApiLiveTest", singleThreaded = true)
+public class EnginesApiLiveTest extends BaseShipyardApiLiveTest {
+
+   protected static final String engineName = "jclouds-shipard-live-test";
+   protected String engineID = null;
+   
+   @BeforeClass
+   protected void init() throws Exception {
+      String dockerEndpoint = System.getProperty("test.shipyard.docker.endpoint");
+      EngineSettingsInfo info = EngineSettingsInfo.create(engineName, dockerEndpoint, 1, 1024, Lists.newArrayList(engineName));
+      AddEngine additionalEngine = AddEngine.create("local", "", "", "", info);
+      api().addEngine(additionalEngine);
+   }
+   
+   @AfterClass (alwaysRun = true)
+   protected void tearDown() {
+      assertNotNull(engineID, "Expected engineID to be set but was not");
+      api().removeEngine(engineID);
+   }
+   
+   public void testGetAllEngines() throws Exception {
+     List<EngineInfo> engines = api().listEngines();
+     assertTrue(engines.size() >= 1, "Shipyard did not contain at least 1 Engine which was expected");
+     boolean engineFound = false;
+     for (EngineInfo engine : api().listEngines()) {
+        if (engine.engine().id().equals(engineName)) {
+           engineID = engine.id();
+           engineFound = true;
+        }
+     }
+     assertTrue(engineFound, "Expected but could not find Engine amongst " + engines.size() + " found");
+   }
+   
+   @Test (dependsOnMethods = "testGetAllEngines")
+   public void testGetEngine() throws Exception {
+      assertNotNull(engineID, "Expected engineID to be set but was not");
+      EngineInfo engine = api().getEngine(engineID);
+      assertTrue(engine.engine().id().equals(engineName), "Expected Engine name " + engineName + " but found " + engine.engine().id());
+   }
+   
+   @Test (expectedExceptions = HttpResponseException.class)
+   public void testAddNonExistentEngine() throws Exception {
+      EngineSettingsInfo info = EngineSettingsInfo.create("local", "http://www.test-jclouds-shipyard:9999", 1, 1024, Lists.newArrayList("default"));
+      AddEngine additionalEngine = AddEngine.create("local", "", "", "", info);
+      api().addEngine(additionalEngine);
+   }
+   
+   @Test (expectedExceptions = HttpResponseException.class)
+   public void testRemoveNonExistentEngine() throws Exception {
+      api().removeEngine("1234567890-shipyard-jclouds");
+   }
+   
+   private EnginesApi api() {
+      return api.enginesApi();
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/02bc515d/shipyard/src/test/java/org/jclouds/shipyard/features/EnginesApiMockTest.java
----------------------------------------------------------------------
diff --git a/shipyard/src/test/java/org/jclouds/shipyard/features/EnginesApiMockTest.java b/shipyard/src/test/java/org/jclouds/shipyard/features/EnginesApiMockTest.java
new file mode 100644
index 0000000..b5a4ad1
--- /dev/null
+++ b/shipyard/src/test/java/org/jclouds/shipyard/features/EnginesApiMockTest.java
@@ -0,0 +1,134 @@
+/*
+ * 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 com.google.common.collect.Lists;
+import com.squareup.okhttp.mockwebserver.MockResponse;
+import com.squareup.okhttp.mockwebserver.MockWebServer;
+
+import org.jclouds.http.HttpResponseException;
+import org.jclouds.shipyard.ShipyardApi;
+import org.jclouds.shipyard.domain.engines.AddEngine;
+import org.jclouds.shipyard.domain.engines.EngineSettingsInfo;
+import org.jclouds.shipyard.internal.BaseShipyardMockTest;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+
+/**
+ * Mock tests for the {@link org.jclouds.shipyard.features.EnginesApi} class.
+ */
+@Test(groups = "unit", testName = "EnginesApiMockTest")
+public class EnginesApiMockTest extends BaseShipyardMockTest {
+
+   public void testGetEngine() throws Exception {
+      MockWebServer server = mockShipyardWebServer();
+      server.enqueue(new MockResponse().setBody(payloadFromResource("/engine.json")));
+      ShipyardApi shipyardApi = api(server.getUrl("/"));
+      EnginesApi api = shipyardApi.enginesApi();
+      try {
+         String specificEngineID = "e2059d20-e9df-44f3-8a9b-1bf2321b4eae";
+         assertEquals(api.getEngine(specificEngineID).id(), specificEngineID);
+         assertSent(server, "GET", "/api/engines/" + specificEngineID);
+      } finally {
+         shipyardApi.close();
+         server.shutdown();
+      }
+   }
+   
+   public void testGetAllEngines() throws Exception {
+      MockWebServer server = mockShipyardWebServer();
+      server.enqueue(new MockResponse().setBody(payloadFromResource("/engines.json")));
+      ShipyardApi shipyardApi = api(server.getUrl("/"));
+      EnginesApi api = shipyardApi.enginesApi();
+      try {
+         assertEquals(api.listEngines().size(), 1);
+         assertSent(server, "GET", "/api/engines");
+      } finally {
+         shipyardApi.close();
+         server.shutdown();
+      }
+   }
+   
+   public void testAddEngine() throws Exception {
+      MockWebServer server = mockShipyardWebServer();
+      server.enqueue(new MockResponse().setResponseCode(200));
+      ShipyardApi shipyardApi = api(server.getUrl("/"));
+      EnginesApi api = shipyardApi.enginesApi();
+      try {
+         AddEngine addEngine = AddEngine.create("local", 
+                                 "", 
+                                 "", 
+                                 "", 
+                                 EngineSettingsInfo.create("1234", 
+                                       "http://localhost:2375", 
+                                       1, 
+                                       1024, 
+                                       Lists.newArrayList("shipyard-test")));
+         
+         api.addEngine(addEngine);
+         assertSent(server, "POST", "/api/engines", new String(payloadFromResource("/engine-add.json")));
+      } finally {
+         shipyardApi.close();
+         server.shutdown();
+      }
+   }
+   
+   @Test (expectedExceptions = HttpResponseException.class)
+   public void testAddNonExistentEngine() throws Exception {
+      MockWebServer server = mockShipyardWebServer();
+      server.enqueue(new MockResponse().setResponseCode(500));
+      ShipyardApi shipyardApi = api(server.getUrl("/"));
+      EnginesApi api = shipyardApi.enginesApi();
+      try {
+         AddEngine addEngine = AddEngine.create("local", 
+                                 "", 
+                                 "", 
+                                 "", 
+                                 EngineSettingsInfo.create("9999", 
+                                       "http://shipyard.failure.com:9999", 
+                                       1, 
+                                       1024, 
+                                       Lists.newArrayList("shipyard-faiure")));
+         
+         api.addEngine(addEngine);
+         assertSent(server, "POST", "/api/engines", new String(payloadFromResource("/engine-add.json")));
+      } finally {
+         shipyardApi.close();
+         server.shutdown();
+      }
+   }
+
+   public void testRemoveEngine() throws Exception {
+      MockWebServer server = mockShipyardWebServer();
+      server.enqueue(new MockResponse().setResponseCode(200));
+      ShipyardApi shipyardApi = api(server.getUrl("/"));
+      EnginesApi api = shipyardApi.enginesApi();
+      try {
+         String specificEngineID = "e2059d20-e9df-44f3-8a9b-1bf2321b4eae";
+         api.removeEngine(specificEngineID);
+         assertSent(server, "DELETE", "/api/engines/" + specificEngineID);
+      } finally {
+         shipyardApi.close();
+         server.shutdown();
+      }
+   }
+   
+   public void testRemoveNonExistentEngine() throws Exception {
+      //TODO
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/02bc515d/shipyard/src/test/java/org/jclouds/shipyard/features/ImagesApiLiveTest.java
----------------------------------------------------------------------
diff --git a/shipyard/src/test/java/org/jclouds/shipyard/features/ImagesApiLiveTest.java b/shipyard/src/test/java/org/jclouds/shipyard/features/ImagesApiLiveTest.java
new file mode 100644
index 0000000..47af8e1
--- /dev/null
+++ b/shipyard/src/test/java/org/jclouds/shipyard/features/ImagesApiLiveTest.java
@@ -0,0 +1,42 @@
+/*
+ * 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.assertNotNull;
+
+import java.net.URI;
+import java.util.List;
+
+import org.jclouds.shipyard.domain.images.ImageInfo;
+import org.jclouds.shipyard.internal.BaseShipyardApiLiveTest;
+import org.testng.annotations.Test;
+
+@Test(groups = "live", testName = "ImagesApiLiveTest", singleThreaded = true)
+public class ImagesApiLiveTest extends BaseShipyardApiLiveTest {
+   
+   public void testGetAllImages() throws Exception {
+      // we only have to test that this does not fail. It's entirely possible we get 0 images back.
+      // This is a placeholder test-hack until Shipyard gets a proper images API expected in v2.10
+      String dockerEndpoint = System.getProperty("test.shipyard.docker.endpoint");
+      List<ImageInfo> images = api().listImages(URI.create(dockerEndpoint));
+      assertNotNull(images);
+   }
+   
+   private ImagesApi api() {
+      return api.imagesApi();
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/02bc515d/shipyard/src/test/java/org/jclouds/shipyard/features/ImagesApiMockTest.java
----------------------------------------------------------------------
diff --git a/shipyard/src/test/java/org/jclouds/shipyard/features/ImagesApiMockTest.java b/shipyard/src/test/java/org/jclouds/shipyard/features/ImagesApiMockTest.java
new file mode 100644
index 0000000..cbc5f1e
--- /dev/null
+++ b/shipyard/src/test/java/org/jclouds/shipyard/features/ImagesApiMockTest.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.shipyard.features;
+
+import static org.testng.Assert.assertEquals;
+
+import java.net.URI;
+
+import org.jclouds.http.HttpResponseException;
+import org.jclouds.shipyard.ShipyardApi;
+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.ImagesApi} class.
+ */
+@Test(groups = "unit", testName = "ImagesApiMockTest")
+public class ImagesApiMockTest extends BaseShipyardMockTest {
+
+   public void testGetAllImages() throws Exception {
+      MockWebServer server = mockShipyardWebServer();
+      server.enqueue(new MockResponse().
+                     setResponseCode(200).
+                     setBody(payloadFromResource("/images.json")));
+      ShipyardApi shipyardApi = api(server.getUrl("/"));
+      ImagesApi api = shipyardApi.imagesApi();
+      try {
+         assertEquals(api.listImages(URI.create("")).size(), 1);
+         assertSentIgnoreServiceKey(server, "GET", "/images/json");
+      } finally {
+         shipyardApi.close();
+         server.shutdown();
+      }
+   }
+   
+   @Test (expectedExceptions = HttpResponseException.class)
+   public void testGetNonExistentDockerDaemon() throws Exception {
+      MockWebServer server = mockShipyardWebServer();
+      server.enqueue(new MockResponse().
+                     setResponseCode(404).
+                     setBody(payloadFromResource("/images.json")));
+      ShipyardApi shipyardApi = api(server.getUrl("/"));
+      ImagesApi api = shipyardApi.imagesApi();
+      try {
+         assertEquals(api.listImages(URI.create("http://test-jclouds-ship:9999")).size(), 0);
+         assertSentIgnoreServiceKey(server, "GET", "/images/json");
+      } finally {
+         shipyardApi.close();
+         server.shutdown();
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/02bc515d/shipyard/src/test/java/org/jclouds/shipyard/internal/BaseShipyardApiLiveTest.java
----------------------------------------------------------------------
diff --git a/shipyard/src/test/java/org/jclouds/shipyard/internal/BaseShipyardApiLiveTest.java b/shipyard/src/test/java/org/jclouds/shipyard/internal/BaseShipyardApiLiveTest.java
new file mode 100644
index 0000000..fa29524
--- /dev/null
+++ b/shipyard/src/test/java/org/jclouds/shipyard/internal/BaseShipyardApiLiveTest.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.shipyard.internal;
+
+import java.util.Properties;
+
+import org.jclouds.Constants;
+import org.jclouds.apis.BaseApiLiveTest;
+import org.jclouds.shipyard.ShipyardApi;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.inject.Module;
+
+@Test(groups = "live")
+public class BaseShipyardApiLiveTest extends BaseApiLiveTest<ShipyardApi> {
+
+   public BaseShipyardApiLiveTest() {
+      provider = "shipyard";
+   }
+
+   @Override
+   protected Iterable<Module> setupModules() {
+      return ImmutableSet.<Module>of(getLoggingModule());
+   }
+
+   @Override
+   protected Properties setupProperties() {
+      Properties overrides = super.setupProperties();
+      overrides.setProperty(Constants.PROPERTY_MAX_RETRIES, "0");
+      return overrides;
+   }
+}