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:02 UTC

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

Repository: jclouds-labs
Updated Branches:
  refs/heads/master a34e62ca3 -> 02bc515dd


http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/02bc515d/shipyard/src/test/java/org/jclouds/shipyard/internal/BaseShipyardMockTest.java
----------------------------------------------------------------------
diff --git a/shipyard/src/test/java/org/jclouds/shipyard/internal/BaseShipyardMockTest.java b/shipyard/src/test/java/org/jclouds/shipyard/internal/BaseShipyardMockTest.java
new file mode 100644
index 0000000..e2e9405
--- /dev/null
+++ b/shipyard/src/test/java/org/jclouds/shipyard/internal/BaseShipyardMockTest.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.internal;
+
+import static com.google.common.base.Charsets.UTF_8;
+import static com.google.common.util.concurrent.MoreExecutors.sameThreadExecutor;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.jclouds.util.Strings2.toStringAndClose;
+import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
+import static org.testng.Assert.assertEquals;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Properties;
+import java.util.Set;
+
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+
+import org.jclouds.Constants;
+import org.jclouds.ContextBuilder;
+import org.jclouds.concurrent.config.ExecutorServiceModule;
+import org.jclouds.shipyard.ShipyardApi;
+
+import com.google.common.base.Charsets;
+import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableSet;
+import com.google.gson.JsonParser;
+import com.google.inject.Module;
+import com.squareup.okhttp.mockwebserver.MockWebServer;
+import com.squareup.okhttp.mockwebserver.RecordedRequest;
+
+/**
+ * Base class for all Shipyard mock tests.
+ */
+public class BaseShipyardMockTest {
+   private final Set<Module> modules = ImmutableSet.<Module> of(new ExecutorServiceModule(sameThreadExecutor()));
+
+   protected String provider;
+   private final JsonParser parser = new JsonParser();
+
+   public BaseShipyardMockTest() {
+      provider = "shipyard";
+   }
+
+   public ShipyardApi api(URL url) {
+      return ContextBuilder.newBuilder(provider)
+            .credentials("shipyard-service-key", "")
+            .endpoint(url.toString())
+            .modules(modules)
+            .overrides(setupProperties())
+            .buildApi(ShipyardApi.class);
+   }
+
+   protected Properties setupProperties() {
+      Properties properties = new Properties();
+      properties.setProperty(Constants.PROPERTY_MAX_RETRIES, "0");
+      return properties;
+   }
+
+   public static MockWebServer mockShipyardWebServer() throws IOException {
+      MockWebServer server = new MockWebServer();
+      server.play();
+      return server;
+   }
+
+   public byte[] payloadFromResource(String resource) {
+      try {
+         return toStringAndClose(getClass().getResourceAsStream(resource)).getBytes(Charsets.UTF_8);
+      } catch (IOException e) {
+         throw Throwables.propagate(e);
+      }
+   }
+
+   protected RecordedRequest assertSent(MockWebServer server, String method, String path) throws InterruptedException {
+      RecordedRequest request = server.takeRequest();
+      assertThat(request.getMethod()).isEqualTo(method);
+      assertThat(request.getPath()).isEqualTo(path);
+      assertThat(request.getHeader("X-Service-Key").equals("shipyard-service-key"));
+      assertThat(request.getHeader(HttpHeaders.ACCEPT)).isEqualTo(MediaType.APPLICATION_JSON);
+      return request;
+   }
+   
+   protected RecordedRequest assertSentIgnoreServiceKey(MockWebServer server, String method, String path) throws InterruptedException {
+      RecordedRequest request = server.takeRequest();
+      assertThat(request.getMethod()).isEqualTo(method);
+      assertThat(request.getPath()).isEqualTo(path);
+      assertThat(request.getHeader(HttpHeaders.ACCEPT)).isEqualTo(MediaType.APPLICATION_JSON);
+      return request;
+   }
+   
+   protected RecordedRequest assertSent(MockWebServer server, String method, String path, String json) throws InterruptedException {
+      RecordedRequest request = assertSent(server, method, path);
+      assertEquals(request.getHeader("Content-Type"), APPLICATION_JSON);
+      assertEquals(parser.parse(new String(request.getBody(), UTF_8)), parser.parse(json));
+      return request;
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/02bc515d/shipyard/src/test/java/org/jclouds/shipyard/internal/BaseShipyardParseTest.java
----------------------------------------------------------------------
diff --git a/shipyard/src/test/java/org/jclouds/shipyard/internal/BaseShipyardParseTest.java b/shipyard/src/test/java/org/jclouds/shipyard/internal/BaseShipyardParseTest.java
new file mode 100644
index 0000000..8bc9532
--- /dev/null
+++ b/shipyard/src/test/java/org/jclouds/shipyard/internal/BaseShipyardParseTest.java
@@ -0,0 +1,31 @@
+/*
+ * 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 org.jclouds.json.BaseItemParserTest;
+import org.jclouds.json.config.GsonModule;
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+
+public abstract class BaseShipyardParseTest<T>  extends BaseItemParserTest<T> {
+
+   @Override
+   protected Injector injector() {
+      return Guice.createInjector(new GsonModule());
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/02bc515d/shipyard/src/test/java/org/jclouds/shipyard/parse/ContainersParseTest.java
----------------------------------------------------------------------
diff --git a/shipyard/src/test/java/org/jclouds/shipyard/parse/ContainersParseTest.java b/shipyard/src/test/java/org/jclouds/shipyard/parse/ContainersParseTest.java
new file mode 100644
index 0000000..0db2407
--- /dev/null
+++ b/shipyard/src/test/java/org/jclouds/shipyard/parse/ContainersParseTest.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.shipyard.parse;
+
+import java.util.List;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.core.MediaType;
+
+import org.jclouds.shipyard.domain.containers.ContainerImageInfo;
+import org.jclouds.shipyard.domain.containers.ContainerInfo;
+import org.jclouds.shipyard.domain.engines.EngineSettingsInfo;
+import org.jclouds.shipyard.domain.images.ImagePortsInfo;
+import org.jclouds.shipyard.internal.BaseShipyardParseTest;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+@Test(groups = "unit")
+public class ContainersParseTest extends BaseShipyardParseTest<List<ContainerInfo>> {
+
+   @Override
+   public String resource() {
+      return "/containers.json";
+   }
+
+   @Override
+   @Consumes(MediaType.APPLICATION_JSON)
+   public List<ContainerInfo> expected() {
+      
+      return ImmutableList.of(
+              ContainerInfo.create("e2f6784b75ed8768e83b7ec46ca8ef784941f6ce4c53231023804277965da1d2",
+                    "/atlassian-stash",
+                    ContainerImageInfo.create("nkatsaros/atlassian-stash:3.5", 
+                             ImmutableMap.<String, String>of("STASH_HOME", "/var/atlassian/stash", "STASH_VERSION", "3.5.0"), 
+                             ImmutableList.<String>of("/docker-entrypoint.sh"), 
+                             "e2f6784b75ed", 
+                             ImmutableList.<ImagePortsInfo>of(ImagePortsInfo.create("tcp", "0.0.0.0", 8089, 8080)), 
+                             ImmutableList.<String>of("/var/atlassian/stash"), 
+                             ImmutableMap.<String, String>of(), 
+                             true, 
+                             "bridge"), 
+                    EngineSettingsInfo.create("sdrelnx150", 
+                          "http://sdrelnx150:2375", 
+                          8, 
+                          8096, 
+                          ImmutableList.<String>of("sdrelnx150")), 
+                    "stopped", 
+                    ImmutableList.<ImagePortsInfo>of(ImagePortsInfo.create("tcp", "0.0.0.0", 8089, 8080)))
+      );
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/02bc515d/shipyard/src/test/java/org/jclouds/shipyard/parse/EnginesParseTest.java
----------------------------------------------------------------------
diff --git a/shipyard/src/test/java/org/jclouds/shipyard/parse/EnginesParseTest.java b/shipyard/src/test/java/org/jclouds/shipyard/parse/EnginesParseTest.java
new file mode 100644
index 0000000..9e208fc
--- /dev/null
+++ b/shipyard/src/test/java/org/jclouds/shipyard/parse/EnginesParseTest.java
@@ -0,0 +1,51 @@
+/*
+ * 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.parse;
+
+import java.util.List;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.core.MediaType;
+
+import org.jclouds.shipyard.domain.engines.EngineInfo;
+import org.jclouds.shipyard.domain.engines.EngineSettingsInfo;
+import org.jclouds.shipyard.internal.BaseShipyardParseTest;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+
+@Test(groups = "unit")
+public class EnginesParseTest extends BaseShipyardParseTest<List<EngineInfo>> {
+
+   @Override
+   public String resource() {
+      return "/engines.json";
+   }
+
+   @Override
+   @Consumes(MediaType.APPLICATION_JSON)
+   public List<EngineInfo> expected() {
+      return ImmutableList.of(
+              EngineInfo.create("e2059d20-e9df-44f3-8a9b-1bf2321b4eae", 
+                    EngineSettingsInfo.create("sdrelnx150", 
+                          "http://sdrelnx150:2375", 
+                          8, 
+                          8096, 
+                          ImmutableList.<String>of("sdrelnx150")))
+      );
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/02bc515d/shipyard/src/test/java/org/jclouds/shipyard/parse/ImagesParseTest.java
----------------------------------------------------------------------
diff --git a/shipyard/src/test/java/org/jclouds/shipyard/parse/ImagesParseTest.java b/shipyard/src/test/java/org/jclouds/shipyard/parse/ImagesParseTest.java
new file mode 100644
index 0000000..359cf0b
--- /dev/null
+++ b/shipyard/src/test/java/org/jclouds/shipyard/parse/ImagesParseTest.java
@@ -0,0 +1,51 @@
+/*
+ * 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.parse;
+
+import java.util.List;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.core.MediaType;
+
+import org.jclouds.shipyard.domain.images.ImageInfo;
+import org.jclouds.shipyard.internal.BaseShipyardParseTest;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+
+@Test(groups = "unit")
+public class ImagesParseTest extends BaseShipyardParseTest<List<ImageInfo>> {
+
+   @Override
+   public String resource() {
+      return "/images.json";
+   }
+
+   @Override
+   @Consumes(MediaType.APPLICATION_JSON)
+   public List<ImageInfo> expected() {
+      return ImmutableList.of(
+              ImageInfo.create(1416370366, 
+                    "3f0d936caee4777872d6ad8dfae0077b6857d86f0232a240a95e748fb1c981f1", 
+                    "5cd3a141e0cc8523bf5d76b9187124bb9d43b874da2656abf1f417e4d4858643", 
+                    ImmutableList.<String>of("nkatsaros/atlassian-stash:3.4"), 
+                    6233, 
+                    480107370, 
+                    null)
+      );
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/02bc515d/shipyard/src/test/resources/container-deploy-response.json
----------------------------------------------------------------------
diff --git a/shipyard/src/test/resources/container-deploy-response.json b/shipyard/src/test/resources/container-deploy-response.json
new file mode 100644
index 0000000..acce569
--- /dev/null
+++ b/shipyard/src/test/resources/container-deploy-response.json
@@ -0,0 +1,49 @@
+[
+{
+    "id": "e2f6784b75ed8768e83b7ec46ca8ef784941f6ce4c53231023804277965da1d2",
+    "name": "/atlassian-stash",
+    "image": {
+        "name": "nkatsaros/atlassian-stash:3.5",
+        "entrypoint": [
+            "/docker-entrypoint.sh"
+        ],
+        "environment": {
+            "STASH_HOME": "/var/atlassian/stash",
+            "STASH_VERSION": "3.5.0"
+        },
+        "hostname": "e2f6784b75ed",
+        "bind_ports": [
+            {
+                "proto": "tcp",
+                "host_ip": "0.0.0.0",
+                "port": 8089,
+                "container_port": 8080
+            }
+        ],
+        "volumes": [
+            "/var/atlassian/stash"
+        ],
+        "restart_policy": {},
+        "publish": true,
+        "network_mode": "bridge"
+    },
+    "engine": {
+        "id": "localhost",
+        "addr": "http://localhost:2375",
+        "cpus": 8,
+        "memory": 8096,
+        "labels": [
+            "localhost"
+        ]
+    },
+    "state": "stopped",
+    "ports": [
+        {
+            "proto": "tcp",
+            "host_ip": "0.0.0.0",
+            "port": 8089,
+            "container_port": 8080
+        }
+    ]
+}
+]
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/02bc515d/shipyard/src/test/resources/container-deploy.json
----------------------------------------------------------------------
diff --git a/shipyard/src/test/resources/container-deploy.json b/shipyard/src/test/resources/container-deploy.json
new file mode 100644
index 0000000..74d391b
--- /dev/null
+++ b/shipyard/src/test/resources/container-deploy.json
@@ -0,0 +1,18 @@
+{
+    "name": "nkatsaros/atlassian-stash:3.5",
+    "container_name": "atlassian-stash",
+    "cpus": 8,
+    "memory": 8096,
+    "type" : "service",
+    "labels": [
+        "localhost"
+    ],
+    "args": [],
+    "environment": {
+        "STASH_HOME": "/var/atlassian/stash",
+        "STASH_VERSION": "3.5.0"
+    },
+    "restart_policy": {},
+    "bind_ports": [],
+    "links": {}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/02bc515d/shipyard/src/test/resources/container.json
----------------------------------------------------------------------
diff --git a/shipyard/src/test/resources/container.json b/shipyard/src/test/resources/container.json
new file mode 100644
index 0000000..1d60c11
--- /dev/null
+++ b/shipyard/src/test/resources/container.json
@@ -0,0 +1,47 @@
+{
+    "id": "e2f6784b75ed8768e83b7ec46ca8ef784941f6ce4c53231023804277965da1d2",
+    "name": "/atlassian-stash",
+    "image": {
+        "name": "nkatsaros/atlassian-stash:3.5",
+        "entrypoint": [
+            "/docker-entrypoint.sh"
+        ],
+        "environment": {
+            "STASH_HOME": "/var/atlassian/stash",
+            "STASH_VERSION": "3.5.0"
+        },
+        "hostname": "e2f6784b75ed",
+        "bind_ports": [
+            {
+                "proto": "tcp",
+                "host_ip": "0.0.0.0",
+                "port": 8089,
+                "container_port": 8080
+            }
+        ],
+        "volumes": [
+            "/var/atlassian/stash"
+        ],
+        "restart_policy": {},
+        "publish": true,
+        "network_mode": "bridge"
+    },
+    "engine": {
+        "id": "localhost",
+        "addr": "http://localhost:2375",
+        "cpus": 8,
+        "memory": 8096,
+        "labels": [
+            "localhost"
+        ]
+    },
+    "state": "stopped",
+    "ports": [
+        {
+            "proto": "tcp",
+            "host_ip": "0.0.0.0",
+            "port": 8089,
+            "container_port": 8080
+        }
+    ]
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/02bc515d/shipyard/src/test/resources/containers.json
----------------------------------------------------------------------
diff --git a/shipyard/src/test/resources/containers.json b/shipyard/src/test/resources/containers.json
new file mode 100644
index 0000000..2b838b2
--- /dev/null
+++ b/shipyard/src/test/resources/containers.json
@@ -0,0 +1,49 @@
+[
+    {
+        "id": "e2f6784b75ed8768e83b7ec46ca8ef784941f6ce4c53231023804277965da1d2",
+        "name": "/atlassian-stash",
+        "image": {
+            "name": "nkatsaros/atlassian-stash:3.5",
+            "entrypoint": [
+                "/docker-entrypoint.sh"
+            ],
+            "environment": {
+                "STASH_HOME": "/var/atlassian/stash",
+                "STASH_VERSION": "3.5.0"
+            },
+            "hostname": "e2f6784b75ed",
+            "bind_ports": [
+                {
+                    "proto": "tcp",
+                    "host_ip": "0.0.0.0",
+                    "port": 8089,
+                    "container_port": 8080
+                }
+            ],
+            "volumes": [
+                "/var/atlassian/stash"
+            ],
+            "restart_policy": {},
+            "publish": true,
+            "network_mode": "bridge"
+        },
+        "engine": {
+            "id": "sdrelnx150",
+            "addr": "http://sdrelnx150:2375",
+            "cpus": 8,
+            "memory": 8096,
+            "labels": [
+                "sdrelnx150"
+            ]
+        },
+        "state": "stopped",
+        "ports": [
+            {
+                "proto": "tcp",
+                "host_ip": "0.0.0.0",
+                "port": 8089,
+                "container_port": 8080
+            }
+        ]
+    }
+]
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/02bc515d/shipyard/src/test/resources/engine-add.json
----------------------------------------------------------------------
diff --git a/shipyard/src/test/resources/engine-add.json b/shipyard/src/test/resources/engine-add.json
new file mode 100644
index 0000000..11b0800
--- /dev/null
+++ b/shipyard/src/test/resources/engine-add.json
@@ -0,0 +1,15 @@
+{
+    "local": "local",
+    "ssl_cert": "",
+    "ssl_key": "",
+    "ca_cert": "",
+    "engine": {
+        "id": "1234",
+        "addr": "http://localhost:2375",
+        "cpus": 1.0,
+        "memory": 1024.0,
+        "labels": [
+            "shipyard-test"
+        ]
+    }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/02bc515d/shipyard/src/test/resources/engine.json
----------------------------------------------------------------------
diff --git a/shipyard/src/test/resources/engine.json b/shipyard/src/test/resources/engine.json
new file mode 100644
index 0000000..11f0099
--- /dev/null
+++ b/shipyard/src/test/resources/engine.json
@@ -0,0 +1,12 @@
+{
+    "id": "e2059d20-e9df-44f3-8a9b-1bf2321b4eae",
+    "engine": {
+        "id": "sdrelnx150",
+        "addr": "http://sdrelnx150:2375",
+        "cpus": 8,
+        "memory": 8096,
+        "labels": [
+            "sdrelnx150"
+        ]
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/02bc515d/shipyard/src/test/resources/engines.json
----------------------------------------------------------------------
diff --git a/shipyard/src/test/resources/engines.json b/shipyard/src/test/resources/engines.json
new file mode 100644
index 0000000..6cc205a
--- /dev/null
+++ b/shipyard/src/test/resources/engines.json
@@ -0,0 +1,14 @@
+[
+    {
+        "id": "e2059d20-e9df-44f3-8a9b-1bf2321b4eae",
+        "engine": {
+            "id": "sdrelnx150",
+            "addr": "http://sdrelnx150:2375",
+            "cpus": 8,
+            "memory": 8096,
+            "labels": [
+                "sdrelnx150"
+            ]
+        }
+    }
+]
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/02bc515d/shipyard/src/test/resources/image.json
----------------------------------------------------------------------
diff --git a/shipyard/src/test/resources/image.json b/shipyard/src/test/resources/image.json
new file mode 100644
index 0000000..0c27470
--- /dev/null
+++ b/shipyard/src/test/resources/image.json
@@ -0,0 +1,10 @@
+{
+    "Created": 1416370366,
+    "Id": "3f0d936caee4777872d6ad8dfae0077b6857d86f0232a240a95e748fb1c981f1",
+    "ParentId": "5cd3a141e0cc8523bf5d76b9187124bb9d43b874da2656abf1f417e4d4858643",
+    "RepoTags": [
+        "nkatsaros/atlassian-stash:3.4"
+    ],
+    "Size": 6233,
+    "VirtualSize": 480107370
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/02bc515d/shipyard/src/test/resources/images.json
----------------------------------------------------------------------
diff --git a/shipyard/src/test/resources/images.json b/shipyard/src/test/resources/images.json
new file mode 100644
index 0000000..8affec4
--- /dev/null
+++ b/shipyard/src/test/resources/images.json
@@ -0,0 +1,12 @@
+[
+    {
+        "Created": 1416370366,
+        "Id": "3f0d936caee4777872d6ad8dfae0077b6857d86f0232a240a95e748fb1c981f1",
+        "ParentId": "5cd3a141e0cc8523bf5d76b9187124bb9d43b874da2656abf1f417e4d4858643",
+        "RepoTags": [
+            "nkatsaros/atlassian-stash:3.4"
+        ],
+        "Size": 6233,
+        "VirtualSize": 480107370
+    }
+]
\ No newline at end of file


[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.

Posted by na...@apache.org.
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;
+   }
+}