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 2014/07/07 11:52:08 UTC

[1/3] [JCLOUDS-500] Initial commit for docker

Repository: jclouds-labs
Updated Branches:
  refs/heads/master 422ffe0f6 -> 9b124ee9f


http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9b124ee9/docker/src/main/java/org/jclouds/docker/options/RemoveContainerOptions.java
----------------------------------------------------------------------
diff --git a/docker/src/main/java/org/jclouds/docker/options/RemoveContainerOptions.java b/docker/src/main/java/org/jclouds/docker/options/RemoveContainerOptions.java
new file mode 100644
index 0000000..5c3abba
--- /dev/null
+++ b/docker/src/main/java/org/jclouds/docker/options/RemoveContainerOptions.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.docker.options;
+
+import org.jclouds.http.options.BaseHttpRequestOptions;
+
+/**
+ * Options to customize container removal.
+ */
+public class RemoveContainerOptions extends BaseHttpRequestOptions {
+
+   public static final RemoveContainerOptions NONE = new RemoveContainerOptions();
+
+   public RemoveContainerOptions verbose(Boolean verbose) {
+      this.queryParameters.put("verbose", verbose.toString());
+      return this;
+   }
+
+   public RemoveContainerOptions force(Boolean force) {
+      this.queryParameters.put("force", force.toString());
+      return this;
+   }
+
+   public static class Builder {
+      /**
+       * @see RemoveContainerOptions#verbose
+       */
+      public static RemoveContainerOptions verbose(Boolean verbose) {
+         RemoveContainerOptions options = new RemoveContainerOptions();
+         return options.verbose(verbose);
+      }
+
+      /**
+       * @see RemoveContainerOptions#force
+       */
+      public static RemoveContainerOptions force(Boolean force) {
+         RemoveContainerOptions options = new RemoveContainerOptions();
+         return options.force(force);
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9b124ee9/docker/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
----------------------------------------------------------------------
diff --git a/docker/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata b/docker/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
new file mode 100644
index 0000000..ca1a6cb
--- /dev/null
+++ b/docker/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
@@ -0,0 +1 @@
+org.jclouds.docker.DockerApiMetadata
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9b124ee9/docker/src/test/java/org/jclouds/docker/DockerApiMetadataTest.java
----------------------------------------------------------------------
diff --git a/docker/src/test/java/org/jclouds/docker/DockerApiMetadataTest.java b/docker/src/test/java/org/jclouds/docker/DockerApiMetadataTest.java
new file mode 100644
index 0000000..41d0067
--- /dev/null
+++ b/docker/src/test/java/org/jclouds/docker/DockerApiMetadataTest.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.docker;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+import org.jclouds.apis.ApiMetadata;
+import org.jclouds.apis.Apis;
+import org.jclouds.compute.internal.BaseComputeServiceApiMetadataTest;
+import org.testng.annotations.Test;
+
+/**
+ * Unit tests for the {@link DockerApiMetadata} class.
+ */
+@Test(groups = "unit", testName = "AbiquoApiMetadataTest")
+public class DockerApiMetadataTest extends BaseComputeServiceApiMetadataTest {
+
+   public DockerApiMetadataTest() {
+      super(new DockerApiMetadata());
+   }
+
+   public void testDockerApiRegistered() {
+      ApiMetadata api = Apis.withId("docker");
+
+      assertNotNull(api);
+      assertTrue(api instanceof DockerApiMetadata);
+      assertEquals(api.getId(), "docker");
+   }
+
+}
+

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9b124ee9/docker/src/test/java/org/jclouds/docker/binders/BindInputStreamToRequestTest.java
----------------------------------------------------------------------
diff --git a/docker/src/test/java/org/jclouds/docker/binders/BindInputStreamToRequestTest.java b/docker/src/test/java/org/jclouds/docker/binders/BindInputStreamToRequestTest.java
new file mode 100644
index 0000000..a21999c
--- /dev/null
+++ b/docker/src/test/java/org/jclouds/docker/binders/BindInputStreamToRequestTest.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.docker.binders;
+
+import com.google.common.io.CharStreams;
+import org.jclouds.http.HttpRequest;
+import org.testng.annotations.Test;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.AssertJUnit.assertTrue;
+
+@Test(groups = "unit", testName = "BindInputStreamToRequestTest")
+public class BindInputStreamToRequestTest {
+
+   @Test
+   public void testBindInputStreamToRequest() throws IOException {
+      BindInputStreamToRequest binder = new BindInputStreamToRequest();
+
+      HttpRequest request = HttpRequest.builder().method("GET").endpoint("http://test").build();
+      request = binder.bindToRequest(request, File.createTempFile("dockerfile", ""));
+      String rawContent = CharStreams.toString(new InputStreamReader((FileInputStream) request.getPayload().getRawContent(), "UTF-8"));
+      assertTrue(rawContent.startsWith("Dockerfile"));
+      assertEquals(request.getPayload().getContentMetadata().getContentType(), "application/tar");
+   }
+
+   @Test(expectedExceptions = IllegalArgumentException.class)
+   public void testBindInputStreamToRequestWithObjectAsInput() throws IOException {
+      BindInputStreamToRequest binder = new BindInputStreamToRequest();
+
+      HttpRequest request = HttpRequest.builder().method("GET").endpoint("http://test").build();
+      request = binder.bindToRequest(request, new Object());
+      String rawContent = CharStreams.toString(new InputStreamReader((FileInputStream) request.getPayload().getRawContent(), "UTF-8"));
+      assertTrue(rawContent.startsWith("Dockerfile"));
+      assertEquals(request.getPayload().getContentMetadata().getContentType(), "application/tar");
+   }
+
+   @Test(expectedExceptions = NullPointerException.class)
+   public void testBindInputStreamToRequestWithNullInput() throws IOException {
+      BindInputStreamToRequest binder = new BindInputStreamToRequest();
+
+      HttpRequest request = HttpRequest.builder().method("GET").endpoint("http://test").build();
+      request = binder.bindToRequest(request, null);
+      String rawContent = CharStreams.toString(new InputStreamReader((FileInputStream) request.getPayload().getRawContent(), "UTF-8"));
+      assertTrue(rawContent.startsWith("Dockerfile"));
+      assertEquals(request.getPayload().getContentMetadata().getContentType(), "application/tar");
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9b124ee9/docker/src/test/java/org/jclouds/docker/compute/BaseDockerApiLiveTest.java
----------------------------------------------------------------------
diff --git a/docker/src/test/java/org/jclouds/docker/compute/BaseDockerApiLiveTest.java b/docker/src/test/java/org/jclouds/docker/compute/BaseDockerApiLiveTest.java
new file mode 100644
index 0000000..77115f2
--- /dev/null
+++ b/docker/src/test/java/org/jclouds/docker/compute/BaseDockerApiLiveTest.java
@@ -0,0 +1,91 @@
+/*
+ * 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.docker.compute;
+
+import com.google.common.base.Charsets;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.io.CharStreams;
+import com.google.common.io.Closeables;
+import com.google.common.io.Files;
+import com.google.common.io.Resources;
+import com.google.inject.Module;
+import org.jclouds.Constants;
+import org.jclouds.apis.BaseApiLiveTest;
+import org.jclouds.docker.DockerApi;
+import org.jclouds.docker.features.internal.Archives;
+import org.jclouds.io.Payload;
+import org.jclouds.io.Payloads;
+import org.jclouds.sshj.config.SshjSshClientModule;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.util.Properties;
+
+@Test(groups = "live")
+public class BaseDockerApiLiveTest extends BaseApiLiveTest<DockerApi> {
+
+   public BaseDockerApiLiveTest() {
+      provider = "docker";
+   }
+
+   @Override
+   protected Iterable<Module> setupModules() {
+      return ImmutableSet.<Module>of(getLoggingModule(), new SshjSshClientModule());
+   }
+
+   @Override
+   protected Properties setupProperties() {
+      Properties overrides = super.setupProperties();
+      overrides.setProperty(Constants.PROPERTY_MAX_RETRIES, "15");
+      overrides.setProperty("jclouds.ssh.retry-auth", "true");
+      return overrides;
+   }
+
+   protected String consumeStream(InputStream stream, boolean swallowIOException) {
+      String result = null;
+      try {
+         result = CharStreams.toString(new InputStreamReader(stream, Charsets.UTF_8));
+         Closeables.close(stream, swallowIOException);
+      } catch (IOException e) {
+         Assert.fail();
+      }
+      return result;
+   }
+
+   protected Payload createPayload() throws IOException {
+      String folderPath = System.getProperty("user.dir") + "/docker/src/test/resources";
+      File parentDir = new File(folderPath + "/archive");
+      parentDir.mkdirs();
+      URL url = Resources.getResource("Dockerfile");
+      String content = Resources.toString(url, Charsets.UTF_8);
+      final File dockerfile = new File(parentDir.getAbsolutePath() + File.separator + "Dockerfile");
+      Files.write(content.getBytes(), dockerfile);
+      File archive = Archives.tar(parentDir, folderPath + "/archive/archive.tar");
+      FileInputStream data = new FileInputStream(archive);
+      Payload payload = Payloads.newInputStreamPayload(data);
+      payload.getContentMetadata().setContentLength(data.getChannel().size());
+      payload.getContentMetadata().setContentType("application/tar");
+      return payload;
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9b124ee9/docker/src/test/java/org/jclouds/docker/compute/DockerComputeServiceAdapterLiveTest.java
----------------------------------------------------------------------
diff --git a/docker/src/test/java/org/jclouds/docker/compute/DockerComputeServiceAdapterLiveTest.java b/docker/src/test/java/org/jclouds/docker/compute/DockerComputeServiceAdapterLiveTest.java
new file mode 100644
index 0000000..faee982
--- /dev/null
+++ b/docker/src/test/java/org/jclouds/docker/compute/DockerComputeServiceAdapterLiveTest.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.docker.compute;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotNull;
+import java.util.Properties;
+import java.util.Random;
+
+import org.jclouds.compute.ComputeServiceAdapter.NodeAndInitialCredentials;
+import org.jclouds.compute.domain.Hardware;
+import org.jclouds.compute.domain.Template;
+import org.jclouds.compute.domain.TemplateBuilder;
+import org.jclouds.docker.DockerApi;
+import org.jclouds.docker.compute.strategy.DockerComputeServiceAdapter;
+import org.jclouds.docker.domain.Container;
+import org.jclouds.sshj.config.SshjSshClientModule;
+import org.testng.annotations.AfterGroups;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.inject.Injector;
+import com.google.inject.Module;
+
+@Test(groups = "live", singleThreaded = true, testName = "DockerComputeServiceAdapterLiveTest")
+public class DockerComputeServiceAdapterLiveTest extends BaseDockerApiLiveTest {
+
+   private DockerComputeServiceAdapter adapter;
+   private TemplateBuilder templateBuilder;
+   private NodeAndInitialCredentials<Container> guest;
+
+   @Override
+   protected DockerApi create(Properties props, Iterable<Module> modules) {
+      Injector injector = newBuilder().modules(modules).overrides(props).buildInjector();
+      adapter = injector.getInstance(DockerComputeServiceAdapter.class);
+      templateBuilder = injector.getInstance(TemplateBuilder.class);
+      return injector.getInstance(DockerApi.class);
+   }
+
+   public void testCreateNodeWithGroupEncodedIntoNameThenStoreCredentials() {
+      String group = "foo";
+      String name = "container-" + new Random().nextInt();
+
+      Template template = templateBuilder.smallest()
+              .osDescriptionMatches("jclouds/default:latest").build();
+
+      guest = adapter.createNodeWithGroupEncodedIntoName(group, name, template);
+      assertEquals(guest.getNodeId(), guest.getNode().getId() + "");
+   }
+
+   public void testListHardwareProfiles() {
+      Iterable<Hardware> profiles = adapter.listHardwareProfiles();
+      assertFalse(Iterables.isEmpty(profiles));
+
+      for (Hardware profile : profiles) {
+         assertNotNull(profile);
+      }
+   }
+
+   @AfterGroups(groups = "live")
+   protected void tearDown() {
+      if (guest != null) {
+         adapter.destroyNode(guest.getNode().getId() + "");
+      }
+      super.tearDown();
+   }
+
+   @Override
+   protected Iterable<Module> setupModules() {
+      return ImmutableSet.<Module>of(getLoggingModule(), new SshjSshClientModule());
+   }
+
+   @Override
+   protected Properties setupProperties() {
+      Properties properties = super.setupProperties();
+      properties.setProperty("jclouds.ssh.max-retries", "10");
+      return properties;
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9b124ee9/docker/src/test/java/org/jclouds/docker/compute/DockerComputeServiceLiveTest.java
----------------------------------------------------------------------
diff --git a/docker/src/test/java/org/jclouds/docker/compute/DockerComputeServiceLiveTest.java b/docker/src/test/java/org/jclouds/docker/compute/DockerComputeServiceLiveTest.java
new file mode 100644
index 0000000..cc460c3
--- /dev/null
+++ b/docker/src/test/java/org/jclouds/docker/compute/DockerComputeServiceLiveTest.java
@@ -0,0 +1,142 @@
+/*
+ * 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.docker.compute;
+
+import org.jclouds.compute.domain.Image;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.domain.Template;
+import org.jclouds.compute.domain.TemplateBuilder;
+import org.jclouds.compute.internal.BaseComputeServiceLiveTest;
+import org.jclouds.sshj.config.SshjSshClientModule;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.inject.Module;
+
+/**
+ * Live tests for the {@link org.jclouds.compute.ComputeService} integration.
+ */
+@Test(groups = "live", singleThreaded = true, testName = "DockerComputeServiceLiveTest")
+public class DockerComputeServiceLiveTest extends BaseComputeServiceLiveTest {
+
+   private static final String DEFAULT_JCLOUDS_IMAGE = "jclouds/default";
+   private Image defaultImage;
+
+   public DockerComputeServiceLiveTest() {
+      provider = "docker";
+   }
+
+   @Override
+   protected Module getSshModule() {
+      return new SshjSshClientModule();
+   }
+
+   @Override
+   protected void initializeContext() {
+      super.initializeContext();
+      Optional<? extends Image> optionalImage = Iterables.tryFind(client.listImages(), new Predicate<Image>() {
+         @Override
+         public boolean apply(Image image) {
+            return image.getName().equals(DEFAULT_JCLOUDS_IMAGE);
+         }
+      });
+      if (optionalImage.isPresent()) {
+         defaultImage = optionalImage.get();
+      } else {
+         Assert.fail("Please create an ssh-able image called " + DEFAULT_JCLOUDS_IMAGE);
+      }
+   }
+
+   @Override
+   protected Template buildTemplate(TemplateBuilder templateBuilder) {
+      return templateBuilder.imageId(defaultImage.getId()).build();
+   }
+
+   @Override
+   public void testOptionToNotBlock() throws Exception {
+      // Docker ComputeService implementation has to block until the node
+      // is provisioned, to be able to return it.
+   }
+
+   @Override
+   protected void checkTagsInNodeEquals(NodeMetadata node, ImmutableSet<String> tags) {
+      // Docker does not support tags
+   }
+
+   @Override
+   protected void checkUserMetadataContains(NodeMetadata node, ImmutableMap<String, String> userMetadata) {
+      // Docker does not support user metadata
+   }
+
+   @Override
+   public void testCreateAndRunAService() throws Exception {
+      // Docker does not support blockOnPort
+   }
+
+   @Override
+   @Test(enabled = true, dependsOnMethods = { "testCompareSizes" })
+   public void testAScriptExecutionAfterBootWithBasicTemplate() throws Exception {
+      super.testAScriptExecutionAfterBootWithBasicTemplate();
+   }
+
+   @Override
+   @Test(enabled = true, dependsOnMethods = "testReboot", expectedExceptions = UnsupportedOperationException.class)
+   public void testSuspendResume() throws Exception {
+      super.testSuspendResume();
+   }
+
+   @Override
+   @Test(enabled = true, dependsOnMethods = "testSuspendResume")
+   public void testGetNodesWithDetails() throws Exception {
+      super.testGetNodesWithDetails();
+   }
+
+   @Override
+   @Test(enabled = true, dependsOnMethods = "testSuspendResume")
+   public void testListNodes() throws Exception {
+      super.testListNodes();
+   }
+
+   @Override
+   @Test(enabled = true, dependsOnMethods = "testSuspendResume")
+   public void testListNodesByIds() throws Exception {
+      super.testListNodesByIds();
+   }
+
+   @Override
+   @Test(enabled = true, dependsOnMethods = { "testListNodes", "testGetNodesWithDetails", "testListNodesByIds" })
+   public void testDestroyNodes() {
+      super.testDestroyNodes();
+   }
+
+   @Test(enabled = true, expectedExceptions = NullPointerException.class)
+   public void testCorrectExceptionRunningNodesNotFound() throws Exception {
+      super.testCorrectExceptionRunningNodesNotFound();
+   }
+
+   @Test(enabled = true, expectedExceptions = NullPointerException.class)
+   public void testCorrectAuthException() throws Exception {
+      // Docker does not support authentication yet
+      super.testCorrectAuthException();
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9b124ee9/docker/src/test/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadataTest.java
----------------------------------------------------------------------
diff --git a/docker/src/test/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadataTest.java b/docker/src/test/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadataTest.java
new file mode 100644
index 0000000..62a7943
--- /dev/null
+++ b/docker/src/test/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadataTest.java
@@ -0,0 +1,204 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ *  work for additional information regarding copyright ownership.
+ * The ASF licenses  file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use  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.docker.compute.functions;
+
+import static org.easymock.EasyMock.anyObject;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+import static org.testng.Assert.assertEquals;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.easymock.EasyMock;
+import org.jclouds.compute.domain.Image;
+import org.jclouds.compute.domain.ImageBuilder;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.domain.OperatingSystem;
+import org.jclouds.compute.domain.OsFamily;
+import org.jclouds.compute.functions.GroupNamingConvention;
+import org.jclouds.docker.domain.Config;
+import org.jclouds.docker.domain.Container;
+import org.jclouds.docker.domain.HostConfig;
+import org.jclouds.docker.domain.NetworkSettings;
+import org.jclouds.docker.domain.Port;
+import org.jclouds.docker.domain.State;
+import org.jclouds.domain.Location;
+import org.jclouds.domain.LocationBuilder;
+import org.jclouds.domain.LocationScope;
+import org.jclouds.providers.ProviderMetadata;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Function;
+import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.inject.Guice;
+
+/**
+ * Unit tests for the {@link org.jclouds.docker.compute.functions.ContainerToNodeMetadata} class.
+ */
+@Test(groups = "unit", testName = "ContainerToNodeMetadataTest")
+public class ContainerToNodeMetadataTest {
+   private ContainerToNodeMetadata function;
+
+   private Container container;
+
+   @BeforeMethod
+   public void setup() {
+      Config containerConfig = Config.builder()
+              .hostname("6d35806c1bd2")
+              .domainName("")
+              .user("")
+              .memory(0)
+              .memorySwap(0)
+              .cpuShares(0)
+              .attachStdin(false)
+              .attachStdout(false)
+              .attachStderr(false)
+              .exposedPorts(ImmutableMap.of("22/tcp", ImmutableMap.of()))
+              .tty(false)
+              .openStdin(false)
+              .stdinOnce(false)
+              .env(null)
+              .cmd(ImmutableList.of("/usr/sbin/sshd", "-D"))
+              .imageId("jclouds/ubuntu")
+              .volumesFrom("")
+              .workingDir("")
+              .entrypoint(null)
+              .networkDisabled(false)
+              .build();
+      State state = State.builder()
+              .pid(3626)
+              .running(true)
+              .exitCode(0)
+              .startedAt("2014-03-24T20:28:37.537659054Z")
+              .finishedAt("0001-01-01T00:00:00Z")
+              .ghost(false)
+              .build();
+      container = Container.builder()
+              .id("6d35806c1bd2b25cd92bba2d2c2c5169dc2156f53ab45c2b62d76e2d2fee14a9")
+              .name("/hopeful_mclean")
+              .created("2014-03-22T07:16:45.784120972Z")
+              .path("/usr/sbin/sshd")
+              .args(new String[] {"-D"})
+              .containerConfig(containerConfig)
+              .state(state)
+              .image("af0f59f1c19eef9471c3b8c8d587c39b8f130560b54f3766931b37d76d5de4b6")
+              .networkSettings(NetworkSettings.builder()
+                      .ipAddress("172.17.0.2")
+                      .ipPrefixLen(16)
+                      .gateway("172.17.42.1")
+                      .bridge("docker0")
+                      .ports(ImmutableMap.<String, List<Map<String, String>>>of("22/tcp",
+                              ImmutableList.<Map<String, String>>of(ImmutableMap.of("HostIp", "0.0.0.0", "HostPort",
+                                      "49199"))))
+                      .build())
+              .resolvConfPath("/etc/resolv.conf")
+              .driver("aufs")
+              .execDriver("native-0.1")
+              .volumes(ImmutableMap.<String, String>of())
+              .volumesRW(ImmutableMap.<String, Boolean>of())
+              .command("")
+              .status("")
+              .hostConfig(HostConfig.builder().publishAllPorts(true).build())
+              .ports(ImmutableList.<Port>of())
+              .build();
+      ProviderMetadata providerMetadata = EasyMock.createMock(ProviderMetadata.class);
+      expect(providerMetadata.getEndpoint()).andReturn("http://127.0.0.1:4243");
+      replay(providerMetadata);
+
+      GroupNamingConvention.Factory namingConvention = Guice.createInjector().getInstance(GroupNamingConvention.Factory.class);
+
+      Supplier<Map<String, ? extends Image>> images = new Supplier<Map<String, ? extends Image>>() {
+         @Override
+         public Map<String, ? extends Image> get() {
+            OperatingSystem os = OperatingSystem.builder()
+                    .description("Ubuntu 12.04 64bit")
+                    .family(OsFamily.UBUNTU)
+                    .version("12.04")
+                    .is64Bit(true)
+                    .build();
+
+            return ImmutableMap.of("af0f59f1c19eef9471c3b8c8d587c39b8f130560b54f3766931b37d76d5de4b6",
+                    new ImageBuilder()
+                            .ids("af0f59f1c19eef9471c3b8c8d587c39b8f130560b54f3766931b37d76d5de4b6")
+                            .name("ubuntu")
+                            .description("Ubuntu 12.04 64bit")
+                            .operatingSystem(os)
+                            .status(Image.Status.AVAILABLE)
+                            .build());
+         }
+      };
+
+      Supplier<Set<? extends Location>> locations = new Supplier<Set< ? extends Location>>() {
+         @Override
+         public Set<? extends Location> get() {
+
+            return ImmutableSet.of(
+                    new LocationBuilder()
+                            .id("docker")
+                            .description("http://localhost:2375")
+                            .scope(LocationScope.PROVIDER)
+                            .build()
+            );
+         }
+      };
+
+      function = new ContainerToNodeMetadata(providerMetadata, toPortableStatus(), namingConvention, images, locations);
+   }
+
+   private Function<State, NodeMetadata.Status> toPortableStatus() {
+      StateToStatus function = EasyMock.createMock(StateToStatus.class);
+         expect(function.apply(anyObject(State.class))).andReturn(NodeMetadata.Status.RUNNING);
+         replay(function);
+         return function;
+   }
+
+   public void testVirtualMachineToNodeMetadata() {
+      Container mockContainer = mockContainer();
+
+      NodeMetadata node = function.apply(mockContainer);
+
+      verify(mockContainer);
+
+      assertEquals(node.getId(), "6d35806c1bd2b25cd92bba2d2c2c5169dc2156f53ab45c2b62d76e2d2fee14a9");
+      assertEquals(node.getGroup(), "hopeful_mclean");
+      assertEquals(node.getImageId(), "af0f59f1c19eef9471c3b8c8d587c39b8f130560b54f3766931b37d76d5de4b6");
+      assertEquals(node.getLoginPort(), 49199);
+      assertEquals(node.getPrivateAddresses().size(), 1);
+      assertEquals(node.getPublicAddresses().size(), 1);
+   }
+
+   @SuppressWarnings("unchecked")
+   private Container mockContainer() {
+      Container mockContainer = EasyMock.createMock(Container.class);
+
+      expect(mockContainer.getId()).andReturn(container.getId());
+      expect(mockContainer.getName()).andReturn(container.getName());
+      expect(mockContainer.getContainerConfig()).andReturn(container.getContainerConfig()).anyTimes();
+      expect(mockContainer.getNetworkSettings()).andReturn(container.getNetworkSettings()).anyTimes();
+      expect(mockContainer.getState()).andReturn(container.getState());
+      expect(mockContainer.getImage()).andReturn(container.getImage()).anyTimes();
+      replay(mockContainer);
+
+      return mockContainer;
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9b124ee9/docker/src/test/java/org/jclouds/docker/compute/functions/ImageToImageTest.java
----------------------------------------------------------------------
diff --git a/docker/src/test/java/org/jclouds/docker/compute/functions/ImageToImageTest.java b/docker/src/test/java/org/jclouds/docker/compute/functions/ImageToImageTest.java
new file mode 100644
index 0000000..f295a38
--- /dev/null
+++ b/docker/src/test/java/org/jclouds/docker/compute/functions/ImageToImageTest.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ *  work for additional information regarding copyright ownership.
+ * The ASF licenses  file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use  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.docker.compute.functions;
+
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+import static org.testng.Assert.assertEquals;
+
+import org.easymock.EasyMock;
+import org.jclouds.compute.domain.Image;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+
+/**
+ * Unit tests for the {@link org.jclouds.docker.compute.functions.ImageToImage} class.
+ */
+@Test(groups = "unit", testName = "ImageToImageTest")
+public class ImageToImageTest {
+   private ImageToImage function;
+
+   private org.jclouds.docker.domain.Image image;
+
+   @BeforeMethod
+   public void setup() {
+      image = org.jclouds.docker.domain.Image.builder()
+                                             .id("id")
+                                             .parent("parent")
+                                             .created("created")
+                                             .architecture("x86_64")
+                                             .repoTags(ImmutableList.of("repoTag1:version"))
+                                             .size(0l)
+                                             .build();
+      function = new ImageToImage();
+   }
+
+   public void testImageToImage() {
+      org.jclouds.docker.domain.Image mockImage = mockImage();
+
+      Image image = function.apply(mockImage);
+
+      verify(mockImage);
+
+      assertEquals(mockImage.getId(), image.getId().toString());
+   }
+
+   @SuppressWarnings("unchecked")
+   private org.jclouds.docker.domain.Image mockImage() {
+      org.jclouds.docker.domain.Image mockImage = EasyMock.createMock(org.jclouds.docker.domain.Image.class);
+
+      expect(mockImage.getId()).andReturn(image.getId()).anyTimes();
+      expect(mockImage.getRepoTags()).andReturn(image.getRepoTags()).anyTimes();
+      expect(mockImage.getArchitecture()).andReturn(image.getArchitecture()).anyTimes();
+      replay(mockImage);
+
+      return mockImage;
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9b124ee9/docker/src/test/java/org/jclouds/docker/compute/functions/StateToStatusTest.java
----------------------------------------------------------------------
diff --git a/docker/src/test/java/org/jclouds/docker/compute/functions/StateToStatusTest.java b/docker/src/test/java/org/jclouds/docker/compute/functions/StateToStatusTest.java
new file mode 100644
index 0000000..899a66c
--- /dev/null
+++ b/docker/src/test/java/org/jclouds/docker/compute/functions/StateToStatusTest.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
+ *  work for additional information regarding copyright ownership.
+ * The ASF licenses  file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use  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.docker.compute.functions;
+
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+import static org.testng.Assert.assertEquals;
+
+import org.easymock.EasyMock;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.docker.domain.State;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+/**
+ * Unit tests for the {@link StateToStatus} class.
+ */
+@Test(groups = "unit", testName = "StateToStatusTest")
+public class StateToStatusTest {
+   private StateToStatus function;
+
+   @BeforeMethod
+   public void setup() {
+      function = new StateToStatus();
+   }
+
+   public void testStateRunningToStatusRunning() {
+      State mockState = mockStateRunning();
+
+      NodeMetadata.Status status = function.apply(mockState);
+
+      verify(mockState);
+
+      assertEquals(mockState.isRunning(), true);
+      assertEquals(status, NodeMetadata.Status.RUNNING);
+   }
+
+   public void testStateNotRunningToStatusTerminated() {
+      State mockState = mockStateNotRunning();
+
+      NodeMetadata.Status status = function.apply(mockState);
+
+      verify(mockState);
+
+      assertEquals(mockState.isRunning(), false);
+      assertEquals(status, NodeMetadata.Status.TERMINATED);
+   }
+
+   private State mockStateRunning() {
+      State mockState = EasyMock.createMock(State.class);
+
+      expect(mockState.isRunning()).andReturn(true).anyTimes();
+      replay(mockState);
+
+      return mockState;
+   }
+
+   private State mockStateNotRunning() {
+      State mockState = EasyMock.createMock(State.class);
+
+      expect(mockState.isRunning()).andReturn(false).anyTimes();
+      replay(mockState);
+
+      return mockState;
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9b124ee9/docker/src/test/java/org/jclouds/docker/compute/options/DockerTemplateOptionsTest.java
----------------------------------------------------------------------
diff --git a/docker/src/test/java/org/jclouds/docker/compute/options/DockerTemplateOptionsTest.java b/docker/src/test/java/org/jclouds/docker/compute/options/DockerTemplateOptionsTest.java
new file mode 100644
index 0000000..3a98228
--- /dev/null
+++ b/docker/src/test/java/org/jclouds/docker/compute/options/DockerTemplateOptionsTest.java
@@ -0,0 +1,62 @@
+/*
+ * 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.docker.compute.options;
+
+import static org.testng.Assert.assertEquals;
+
+import org.jclouds.compute.options.TemplateOptions;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableMap;
+
+/**
+ * Unit tests for the {@link DockerTemplateOptions} class.
+ */
+@Test(groups = "unit", testName = "DockerTemplateOptionsTest")
+public class DockerTemplateOptionsTest {
+
+   @Test
+   public void testHostname() {
+      TemplateOptions options = new DockerTemplateOptions().hostname("hostname");
+      assertEquals(options.as(DockerTemplateOptions.class).getHostname(), Optional.of("hostname"));
+   }
+
+   @Test
+   public void testMemory() {
+      TemplateOptions options = new DockerTemplateOptions().memory(1024);
+      assertEquals(options.as(DockerTemplateOptions.class).getMemory(), Optional.of(1024));
+   }
+
+   @Test
+   public void testCpuShares() {
+      TemplateOptions options = new DockerTemplateOptions().cpuShares(2);
+      assertEquals(options.as(DockerTemplateOptions.class).getCpuShares(), Optional.of(2));
+   }
+
+   @Test
+   public void testVolumes() {
+      TemplateOptions options = new DockerTemplateOptions().volumes(ImmutableMap.of("/tmp", "/tmp"));
+      assertEquals(options.as(DockerTemplateOptions.class).getVolumes(), Optional.of(ImmutableMap.of("/tmp", "/tmp")));
+   }
+
+   @Test
+   public void testDns() {
+      TemplateOptions options = new DockerTemplateOptions().dns("8.8.8.8");
+      assertEquals(options.as(DockerTemplateOptions.class).getDns(), Optional.of("8.8.8.8"));
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9b124ee9/docker/src/test/java/org/jclouds/docker/config/DockerParserModuleTest.java
----------------------------------------------------------------------
diff --git a/docker/src/test/java/org/jclouds/docker/config/DockerParserModuleTest.java b/docker/src/test/java/org/jclouds/docker/config/DockerParserModuleTest.java
new file mode 100644
index 0000000..4f5ba75
--- /dev/null
+++ b/docker/src/test/java/org/jclouds/docker/config/DockerParserModuleTest.java
@@ -0,0 +1,52 @@
+/*
+ * 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.docker.config;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import org.jclouds.docker.domain.Container;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import static org.jclouds.docker.config.DockerParserModule.ContainerTypeAdapter;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+
+/**
+ * Unit tests for the {@link org.jclouds.docker.config.DockerParserModule} class.
+ */
+@Test(groups = "unit", testName = "DockerParserModuleTest")
+public class DockerParserModuleTest {
+
+   private Gson gson;
+
+   @BeforeMethod
+   public void setup() {
+      gson = new GsonBuilder()
+              .registerTypeAdapter(Container.class, new ContainerTypeAdapter())
+              .create();
+   }
+
+   @Test
+   public void testContainerWithVolumesNull() {
+      Container container = gson.fromJson(
+              "{ \"Volumes\": null }", Container.class);
+      assertNotNull(container);
+      assertEquals(container.getVolumes(), null);
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9b124ee9/docker/src/test/java/org/jclouds/docker/features/RemoteApiLiveTest.java
----------------------------------------------------------------------
diff --git a/docker/src/test/java/org/jclouds/docker/features/RemoteApiLiveTest.java b/docker/src/test/java/org/jclouds/docker/features/RemoteApiLiveTest.java
new file mode 100644
index 0000000..34feddd
--- /dev/null
+++ b/docker/src/test/java/org/jclouds/docker/features/RemoteApiLiveTest.java
@@ -0,0 +1,121 @@
+/*
+ * 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.docker.features;
+
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.io.Resources;
+import org.jclouds.docker.compute.BaseDockerApiLiveTest;
+import org.jclouds.docker.domain.Config;
+import org.jclouds.docker.domain.Container;
+import org.jclouds.docker.domain.Image;
+import org.jclouds.docker.options.BuildOptions;
+import org.jclouds.docker.options.CreateImageOptions;
+import org.jclouds.docker.options.DeleteImageOptions;
+import org.jclouds.rest.ResourceNotFoundException;
+import org.testng.annotations.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URISyntaxException;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+
+@Test(groups = "live", testName = "RemoteApiLiveTest", singleThreaded = true)
+public class RemoteApiLiveTest extends BaseDockerApiLiveTest {
+
+   private static final String BUSYBOX_IMAGE = "busybox";
+   private Container container = null;
+   private Image image = null;
+
+   @Test
+   public void testVersion() {
+      assertEquals(api().getVersion().getVersion(), "1.0.0");
+   }
+
+   @Test(dependsOnMethods = "testVersion")
+   public void testCreateImage() throws IOException, InterruptedException {
+      CreateImageOptions options = CreateImageOptions.Builder.fromImage(BUSYBOX_IMAGE);
+      InputStream createImageStream = api().createImage(options);
+      consumeStream(createImageStream, false);
+      image = api().inspectImage(BUSYBOX_IMAGE);
+      assertNotNull(image);
+   }
+
+   @Test(dependsOnMethods = "testCreateImage")
+   public void testListImages() {
+      assertNotNull(api().listImages());
+   }
+
+   @Test(dependsOnMethods = "testListImages")
+   public void testCreateContainer() throws IOException, InterruptedException {
+      Config containerConfig = Config.builder().imageId(image.getId())
+              .cmd(ImmutableList.of("/bin/sh", "-c", "while true; do echo hello world; sleep 1; done"))
+              .build();
+      container = api().createContainer("testCreateContainer", containerConfig);
+      assertNotNull(container);
+      assertNotNull(container.getId());
+   }
+
+   @Test(dependsOnMethods = "testCreateContainer")
+   public void testStartContainer() throws IOException, InterruptedException {
+      api().startContainer(container.getId());
+      assertTrue(api().inspectContainer(container.getId()).getState().isRunning());
+   }
+
+   @Test(dependsOnMethods = "testStartContainer")
+   public void testStopContainer() {
+      api().stopContainer(container.getId());
+      assertFalse(api().inspectContainer(container.getId()).getState().isRunning());
+   }
+
+   @Test(dependsOnMethods = "testStopContainer", expectedExceptions = NullPointerException.class)
+   public void testRemoveContainer() {
+      api().removeContainer(container.getId());
+      assertFalse(api().inspectContainer(container.getId()).getState().isRunning());
+   }
+
+   @Test(dependsOnMethods = "testRemoveContainer", expectedExceptions = ResourceNotFoundException.class)
+   public void testDeleteImage() {
+      InputStream deleteImageStream = api().deleteImage(image.getId());
+      consumeStream(deleteImageStream, false);
+      assertNull(api().inspectImage(image.getId()));
+   }
+
+   public void testBuildImage() throws IOException, InterruptedException, URISyntaxException {
+      BuildOptions options = BuildOptions.Builder.tag("testBuildImage").verbose(false).nocache(false);
+      InputStream buildImageStream = api().build(new File(Resources.getResource("Dockerfile").toURI()), options);
+      String buildStream = consumeStream(buildImageStream, false);
+      Iterable<String> splitted = Splitter.on("\n").split(buildStream.replace("\r", "").trim());
+      String lastStreamedLine = Iterables.getLast(splitted).trim();
+      String rawImageId = Iterables.getLast(Splitter.on("Successfully built ").split(lastStreamedLine));
+      String imageId = rawImageId.substring(0, 11);
+      Image image = api().inspectImage(imageId);
+      api().deleteImage(image.getId(), DeleteImageOptions.Builder.force(true));
+   }
+
+   private RemoteApi api() {
+      return api.getRemoteApi();
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9b124ee9/docker/src/test/java/org/jclouds/docker/features/RemoteApiMockTest.java
----------------------------------------------------------------------
diff --git a/docker/src/test/java/org/jclouds/docker/features/RemoteApiMockTest.java b/docker/src/test/java/org/jclouds/docker/features/RemoteApiMockTest.java
new file mode 100644
index 0000000..760f723
--- /dev/null
+++ b/docker/src/test/java/org/jclouds/docker/features/RemoteApiMockTest.java
@@ -0,0 +1,376 @@
+/*
+ * 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.docker.features;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMultimap;
+import com.squareup.okhttp.mockwebserver.MockResponse;
+import com.squareup.okhttp.mockwebserver.MockWebServer;
+import org.jclouds.docker.DockerApi;
+import org.jclouds.docker.domain.Config;
+import org.jclouds.docker.domain.Container;
+import org.jclouds.docker.internal.BaseDockerMockTest;
+import org.jclouds.docker.options.BuildOptions;
+import org.jclouds.docker.options.CreateImageOptions;
+import org.jclouds.docker.options.ListContainerOptions;
+import org.jclouds.io.Payload;
+import org.jclouds.io.Payloads;
+import org.jclouds.rest.ResourceNotFoundException;
+import org.testng.annotations.Test;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.util.Set;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+
+/**
+ * Mock tests for the {@link org.jclouds.docker.DockerApi} class.
+ */
+@Test(groups = "unit", testName = "RemoteApiMockTest")
+public class RemoteApiMockTest extends BaseDockerMockTest {
+
+   public void testListContainers() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setBody(payloadFromResource("/containers.json")));
+
+      DockerApi api = api(server.getUrl("/"));
+      RemoteApi remoteApi = api.getRemoteApi();
+
+      try {
+         Set<Container> containers = remoteApi.listContainers();
+         assertRequestHasCommonFields(server.takeRequest(), "/containers/json");
+         assertEquals(containers.size(), 1);
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   public void testListNonexistentContainers() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setResponseCode(404));
+
+      DockerApi api = api(server.getUrl("/"));
+      RemoteApi remoteApi = api.getRemoteApi();
+
+      try {
+         Set<Container> containers = remoteApi.listContainers();
+         assertRequestHasCommonFields(server.takeRequest(), "/containers/json");
+         assertTrue(containers.isEmpty());
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+
+   @Test(timeOut = 10000l)
+   public void testListAllContainers() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setBody(payloadFromResource("/containers.json")));
+      DockerApi api = api(server.getUrl("/"));
+      RemoteApi remoteApi = api.getRemoteApi();
+      try {
+         Set<Container> containers = remoteApi.listContainers(ListContainerOptions.Builder.all(true));
+         assertRequestHasParameters(server.takeRequest(), "/containers/json", ImmutableMultimap.of("all", "true"));
+         assertEquals(containers.size(), 1);
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   public void testGetContainer() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setBody(payloadFromResource("/container.json")));
+      DockerApi api = api(server.getUrl("/"));
+      RemoteApi remoteApi = api.getRemoteApi();
+      String containerId = "b03d4cd15b76f8876110615cdeed15eadf77c9beb408d62f1687dcc69192cd6d";
+      try {
+         Container container = remoteApi.inspectContainer(containerId);
+         assertRequestHasCommonFields(server.takeRequest(), "/containers/" + containerId + "/json");
+         assertNotNull(container);
+         assertNotNull(container.getId(), containerId);
+         assertNotNull(container.getContainerConfig());
+         assertNotNull(container.getHostConfig());
+         assertEquals(container.getName(), "/tender_lumiere");
+         assertEquals(container.getState().isRunning(), true);
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   public void testGetNonExistingContainer() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setResponseCode(404));
+      DockerApi api = api(server.getUrl("/"));
+      RemoteApi remoteApi = api.getRemoteApi();
+      String containerId = "notExisting";
+      try {
+         Container container = remoteApi.inspectContainer(containerId);
+         assertRequestHasCommonFields(server.takeRequest(), "/containers/" + containerId + "/json");
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   public void testCreateContainer() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setBody(payloadFromResource("/container-creation.json")));
+
+      DockerApi api = api(server.getUrl("/"));
+      RemoteApi remoteApi = api.getRemoteApi();
+      Config containerConfig = Config.builder().cmd(ImmutableList.of("date"))
+              .attachStdin(false)
+              .attachStderr(true)
+              .attachStdout(true)
+              .tty(false)
+              .imageId("base")
+              .build();
+      try {
+         Container container = remoteApi.createContainer("test", containerConfig);
+         assertRequestHasCommonFields(server.takeRequest(), "POST", "/containers/create?name=test");
+         assertNotNull(container);
+         assertEquals(container.getId(), "c6c74153ae4b1d1633d68890a68d89c40aa5e284a1ea016cbc6ef0e634ee37b2");
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   public void testRemoveContainer() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setResponseCode(204));
+
+      DockerApi api = api(server.getUrl("/"));
+      RemoteApi remoteApi = api.getRemoteApi();
+      String containerId = "6d35806c1bd2b25cd92bba2d2c2c5169dc2156f53ab45c2b62d76e2d2fee14a9";
+
+      try {
+         remoteApi.removeContainer(containerId);
+         assertRequestHasCommonFields(server.takeRequest(), "DELETE", "/containers/" + containerId);
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   public void testRemoveNonExistingContainer() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setResponseCode(404));
+      DockerApi api = api(server.getUrl("/"));
+      RemoteApi remoteApi = api.getRemoteApi();
+      String containerId = "nonExisting";
+      try {
+         remoteApi.removeContainer(containerId);
+         fail("Remove container must fail on 404");
+      } catch (ResourceNotFoundException ex) {
+         // Expected exception
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   public void testStartContainer() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setResponseCode(200));
+      DockerApi api = api(server.getUrl("/"));
+      RemoteApi remoteApi = api.getRemoteApi();
+      try {
+         remoteApi.startContainer("1");
+         assertRequestHasCommonFields(server.takeRequest(), "POST", "/containers/1/start");
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   public void testStartNonExistingContainer() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setResponseCode(404));
+      DockerApi api = api(server.getUrl("/"));
+      RemoteApi remoteApi = api.getRemoteApi();
+      try {
+         try {
+            remoteApi.startContainer("1");
+            fail("Start container must fail on 404");
+         } catch (ResourceNotFoundException ex) {
+            // Expected exception
+         }
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   public void testStopContainer() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setResponseCode(200));
+      DockerApi api = api(server.getUrl("/"));
+      RemoteApi remoteApi = api.getRemoteApi();
+      try {
+         remoteApi.stopContainer("1");
+         assertRequestHasCommonFields(server.takeRequest(), "POST", "/containers/1/stop");
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   public void testStopNonExistingContainer() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setResponseCode(404));
+      DockerApi api = api(server.getUrl("/"));
+      RemoteApi remoteApi = api.getRemoteApi();
+      try {
+         remoteApi.stopContainer("1");
+         fail("Stop container must fail on 404");
+      } catch (ResourceNotFoundException ex) {
+         // Expected exception
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   public void testCreateImage() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setResponseCode(200));
+      DockerApi api = api(server.getUrl("/"));
+      RemoteApi remoteApi = api.getRemoteApi();
+      try {
+         remoteApi.createImage(CreateImageOptions.Builder.fromImage("base"));
+         assertRequestHasParameters(server.takeRequest(), "POST", "/images/create", ImmutableMultimap.of("fromImage",
+                 "base"));
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   public void testCreateImageFailure() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setResponseCode(404));
+      DockerApi api = api(server.getUrl("/"));
+      RemoteApi remoteApi = api.getRemoteApi();
+      try {
+         remoteApi.createImage(CreateImageOptions.Builder.fromImage("base"));
+         fail("Create image must fail on 404");
+      } catch (ResourceNotFoundException ex) {
+         // Expected exception
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   public void testDeleteImage() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setResponseCode(204));
+      DockerApi api = api(server.getUrl("/"));
+      RemoteApi remoteApi = api.getRemoteApi();
+      try {
+         remoteApi.deleteImage("1");
+         assertRequestHasCommonFields(server.takeRequest(), "DELETE", "/images/1");
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   public void testDeleteNotExistingImage() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setResponseCode(404));
+      DockerApi api = api(server.getUrl("/"));
+      RemoteApi remoteApi = api.getRemoteApi();
+      try {
+         remoteApi.deleteImage("1");
+         fail("Delete image must fail on 404");
+      } catch (ResourceNotFoundException ex) {
+         // Expected exception
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   public void testBuildContainer() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setResponseCode(200));
+      DockerApi api = api(server.getUrl("/"));
+      RemoteApi remoteApi = api.getRemoteApi();
+      File dockerFile = File.createTempFile("docker", "tmp");
+      try {
+         remoteApi.build(dockerFile, BuildOptions.NONE);
+         assertRequestHasCommonFields(server.takeRequest(), "POST", "/build");
+      } finally {
+         dockerFile.delete();
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   public void testBuildContainerUsingPayload() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setResponseCode(200));
+      DockerApi api = api(server.getUrl("/"));
+      RemoteApi remoteApi = api.getRemoteApi();
+
+      File file = File.createTempFile("docker", "tmp");
+      FileInputStream data = new FileInputStream(file);
+      Payload payload = Payloads.newInputStreamPayload(data);
+      payload.getContentMetadata().setContentLength(file.length());
+
+      try {
+         remoteApi.build(payload, BuildOptions.NONE);
+         assertRequestHasCommonFields(server.takeRequest(), "POST", "/build");
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   public void testBuildNonexistentContainer() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setResponseCode(404));
+
+      DockerApi api = api(server.getUrl("/"));
+      RemoteApi remoteApi = api.getRemoteApi();
+
+      File dockerFile = File.createTempFile("docker", "tmp");
+      try {
+         try {
+            remoteApi.build(dockerFile, BuildOptions.NONE);
+            fail("Build container must fail on 404");
+         } catch (ResourceNotFoundException ex) {
+            // Expected exception
+         }
+      } finally {
+         dockerFile.delete();
+         api.close();
+         server.shutdown();
+      }
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9b124ee9/docker/src/test/java/org/jclouds/docker/features/internal/ArchivesTest.java
----------------------------------------------------------------------
diff --git a/docker/src/test/java/org/jclouds/docker/features/internal/ArchivesTest.java b/docker/src/test/java/org/jclouds/docker/features/internal/ArchivesTest.java
new file mode 100644
index 0000000..15eb3ff
--- /dev/null
+++ b/docker/src/test/java/org/jclouds/docker/features/internal/ArchivesTest.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.docker.features.internal;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.List;
+
+import javax.annotation.Resource;
+import javax.inject.Named;
+
+import org.apache.commons.compress.archivers.ArchiveStreamFactory;
+import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
+import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
+import org.apache.commons.compress.archivers.tar.TarUtils;
+import org.jclouds.compute.reference.ComputeServiceConstants;
+import org.jclouds.logging.Logger;
+import org.testng.Assert;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import com.beust.jcommander.internal.Lists;
+import com.google.common.io.ByteStreams;
+import com.google.common.io.Files;
+
+@Test(groups = "unit", testName = "ArchivesTest")
+public class ArchivesTest {
+
+   @Resource
+   @Named(ComputeServiceConstants.COMPUTE_LOGGER)
+   protected Logger logger = Logger.NULL;
+
+   private File tmpDir;
+   private File outputDir;
+   private long checkSum;
+
+   @BeforeClass
+   private void init() throws IOException {
+      tmpDir = Files.createTempDir();
+      outputDir = Files.createTempDir();
+      File sampleFile = writeSampleFile("test", "this is a test to tar a hierarchy of folders and files\n");
+      checkSum = TarUtils.computeCheckSum(Files.asByteSource(sampleFile).read());
+   }
+
+   public void testTarSingleFile() throws Exception {
+      File archive = Archives.tar(tmpDir, new File(outputDir + File.separator + "test.tar.gz"));
+      List<File> untarredFiles = unTar(archive, outputDir);
+      File untarredSampleFile = getOnlyElement(untarredFiles, null);
+      assertNotNull(untarredSampleFile);
+      assertTrue(checkSum == TarUtils.computeCheckSum(Files.asByteSource(untarredSampleFile).read()));
+   }
+
+   private List<File> unTar(final File inputFile, final File outputDir) throws Exception {
+      final List<File> untarredFiles = Lists.newArrayList();
+      final InputStream is = new FileInputStream(inputFile);
+      final TarArchiveInputStream tarArchiveInputStream = (TarArchiveInputStream)
+              new ArchiveStreamFactory().createArchiveInputStream("tar", is);
+      TarArchiveEntry entry;
+      while ((entry = (TarArchiveEntry) tarArchiveInputStream.getNextEntry()) != null) {
+         final File outputFile = new File(outputDir, entry.getName());
+         if (entry.isDirectory()) {
+            if (!outputFile.exists()) {
+               if (!outputFile.mkdirs()) {
+                  throw new IllegalStateException(String.format("Couldn't create directory %s.", outputFile.getAbsolutePath()));
+               }
+            }
+         } else {
+            OutputStream outputFileStream = new FileOutputStream(outputFile);
+            ByteStreams.copy(tarArchiveInputStream, outputFileStream);
+            outputFileStream.close();
+         }
+         untarredFiles.add(outputFile);
+      }
+      tarArchiveInputStream.close();
+      return untarredFiles;
+   }
+
+   private File writeSampleFile(String fileName, final String contents) {
+      checkNotNull(fileName, "Provided file name for writing must NOT be null.");
+      checkNotNull(contents, "Unable to write null contents.");
+      File sampleFile = new File(tmpDir + File.separator + fileName);
+      try {
+         Files.write(contents.getBytes(), sampleFile);
+      } catch (IOException e) {
+         logger.error("ERROR trying to write to file '" + fileName + "' - " + e.toString());
+         Assert.fail();
+      }
+      return sampleFile;
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9b124ee9/docker/src/test/java/org/jclouds/docker/internal/BaseDockerMockTest.java
----------------------------------------------------------------------
diff --git a/docker/src/test/java/org/jclouds/docker/internal/BaseDockerMockTest.java b/docker/src/test/java/org/jclouds/docker/internal/BaseDockerMockTest.java
new file mode 100644
index 0000000..146b2a0
--- /dev/null
+++ b/docker/src/test/java/org/jclouds/docker/internal/BaseDockerMockTest.java
@@ -0,0 +1,118 @@
+/*
+ * 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.docker.internal;
+
+import static com.google.common.util.concurrent.MoreExecutors.sameThreadExecutor;
+import static org.jclouds.http.utils.Queries.encodeQueryLine;
+import static org.jclouds.util.Strings2.toStringAndClose;
+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.ContextBuilder;
+import org.jclouds.concurrent.config.ExecutorServiceModule;
+import org.jclouds.docker.DockerApi;
+
+import com.google.common.base.Charsets;
+import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Multimap;
+import com.google.inject.Module;
+import com.squareup.okhttp.mockwebserver.MockWebServer;
+import com.squareup.okhttp.mockwebserver.RecordedRequest;
+
+/**
+ * Base class for all Docker mock tests.
+ */
+public class BaseDockerMockTest {
+   private final Set<Module> modules = ImmutableSet.<Module> of(new ExecutorServiceModule(sameThreadExecutor(),
+         sameThreadExecutor()));
+
+   protected String provider;
+
+   public BaseDockerMockTest() {
+      provider = "docker";
+   }
+
+   public DockerApi api(URL url) {
+      return ContextBuilder.newBuilder(provider)
+            .credentials("clientid", "apikey")
+            .endpoint(url.toString())
+            .modules(modules) 
+            .overrides(setupProperties()) 
+            .buildApi(DockerApi.class);
+   }
+
+   protected Properties setupProperties() {
+      return new Properties();
+   }
+
+   public static MockWebServer mockWebServer() 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 static void assertRequestHasCommonFields(final RecordedRequest request, final String path)
+           throws InterruptedException {
+      assertRequestHasParameters(request, "GET", path, ImmutableMultimap.<String, String> of());
+   }
+
+   protected static void assertRequestHasCommonFields(final RecordedRequest request,
+                                                      final String verb, final String path)
+         throws InterruptedException {
+      assertRequestHasParameters(request, verb, path, ImmutableMultimap.<String, String> of());
+   }
+
+   protected static void assertRequestHasParameters(final RecordedRequest request, final String path,
+                                                    Multimap<String, String> parameters) throws InterruptedException {
+      assertRequestHasParameters(request, "GET", path, parameters);
+   }
+
+   protected static void assertRequestHasParameters(final RecordedRequest request, String verb, final String path,
+                                                    Multimap<String, String> parameters) throws InterruptedException {
+      String queryParameters = "";
+      if (!parameters.isEmpty()) {
+         Multimap<String, String> allparams = ImmutableMultimap.<String, String>builder()
+                 .putAll(parameters)
+                 .build();
+
+         assertRequestHasAcceptHeader(request);
+         queryParameters = "?" + encodeQueryLine(allparams);
+      }
+      assertEquals(request.getRequestLine(), verb + " " + path + queryParameters + " HTTP/1.1");
+   }
+
+   protected static void assertRequestHasAcceptHeader(final RecordedRequest request) throws InterruptedException {
+      assertEquals(request.getHeader(HttpHeaders.ACCEPT), MediaType.APPLICATION_JSON);
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9b124ee9/docker/src/test/resources/Dockerfile
----------------------------------------------------------------------
diff --git a/docker/src/test/resources/Dockerfile b/docker/src/test/resources/Dockerfile
new file mode 100644
index 0000000..1318715
--- /dev/null
+++ b/docker/src/test/resources/Dockerfile
@@ -0,0 +1,29 @@
+#
+# 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.
+#
+
+FROM centos:6.4
+MAINTAINER Andrea Turli <an...@gmail.com>
+
+# RUN yum -y groupinstall 'Development Tools'
+RUN yum -y install openssh-server openssh-clients
+
+RUN chkconfig sshd on
+RUN service sshd start
+RUN echo 'root:password' | chpasswd
+
+EXPOSE 22
+CMD ["/usr/sbin/sshd", "-D"]
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9b124ee9/docker/src/test/resources/container-creation.json
----------------------------------------------------------------------
diff --git a/docker/src/test/resources/container-creation.json b/docker/src/test/resources/container-creation.json
new file mode 100644
index 0000000..3e34e0b
--- /dev/null
+++ b/docker/src/test/resources/container-creation.json
@@ -0,0 +1 @@
+{"Id":"c6c74153ae4b1d1633d68890a68d89c40aa5e284a1ea016cbc6ef0e634ee37b2","Warnings":null}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9b124ee9/docker/src/test/resources/container.json
----------------------------------------------------------------------
diff --git a/docker/src/test/resources/container.json b/docker/src/test/resources/container.json
new file mode 100644
index 0000000..b353012
--- /dev/null
+++ b/docker/src/test/resources/container.json
@@ -0,0 +1,81 @@
+{
+  "Args": [
+    "-c",
+    "yum -y install openssh-server openssh-clients"
+  ],
+  "Config": {
+    "AttachStderr": false,
+    "AttachStdin": false,
+    "AttachStdout": false,
+    "Cmd": [
+      "/bin/sh",
+      "-c",
+      "yum -y install openssh-server openssh-clients"
+    ],
+    "CpuShares": 0,
+    "Cpuset": "",
+    "Domainname": "",
+    "Entrypoint": null,
+    "Env": [
+      "HOME=/",
+      "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
+    ],
+    "ExposedPorts": null,
+    "Hostname": "9088c45a9592",
+    "Image": "1e2d12a45cd57ae3fe3c31ede149d800aaf6a711c61646fad340080f531775c8",
+    "Memory": 0,
+    "MemorySwap": 0,
+    "NetworkDisabled": false,
+    "OnBuild": [],
+    "OpenStdin": false,
+    "PortSpecs": null,
+    "StdinOnce": false,
+    "Tty": false,
+    "User": "",
+    "Volumes": null,
+    "WorkingDir": ""
+  },
+  "Created": "2014-06-18T08:49:25.36448943Z",
+  "Driver": "aufs",
+  "ExecDriver": "native-0.2",
+  "HostConfig": {
+    "Binds": null,
+    "ContainerIDFile": "",
+    "Dns": null,
+    "DnsSearch": null,
+    "Links": null,
+    "LxcConf": null,
+    "NetworkMode": "",
+    "PortBindings": null,
+    "Privileged": false,
+    "PublishAllPorts": false,
+    "VolumesFrom": null
+  },
+  "HostnamePath": "/mnt/sda1/var/lib/docker/containers/be1d295c091720abc9a3105219ab75a0a7367d74156cc6048aa599fcc7d650e2/hostname",
+  "HostsPath": "/mnt/sda1/var/lib/docker/containers/be1d295c091720abc9a3105219ab75a0a7367d74156cc6048aa599fcc7d650e2/hosts",
+  "Id": "be1d295c091720abc9a3105219ab75a0a7367d74156cc6048aa599fcc7d650e2",
+  "Image": "1e2d12a45cd57ae3fe3c31ede149d800aaf6a711c61646fad340080f531775c8",
+  "MountLabel": "",
+  "Name": "/tender_lumiere",
+  "NetworkSettings": {
+    "Bridge": "docker0",
+    "Gateway": "172.17.42.1",
+    "IPAddress": "172.17.0.100",
+    "IPPrefixLen": 16,
+    "PortMapping": null,
+    "Ports": {}
+  },
+  "Path": "/bin/sh",
+  "ProcessLabel": "",
+  "ResolvConfPath": "/mnt/sda1/var/lib/docker/containers/be1d295c091720abc9a3105219ab75a0a7367d74156cc6048aa599fcc7d650e2/resolv.conf",
+  "State": {
+    "ExitCode": 0,
+    "FinishedAt": "0001-01-01T00:00:00Z",
+    "Paused": false,
+    "Pid": 16422,
+    "Running": true,
+    "StartedAt": "2014-06-18T08:49:25.63685385Z"
+  },
+  "Volumes": {},
+  "VolumesRW": {}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9b124ee9/docker/src/test/resources/containers.json
----------------------------------------------------------------------
diff --git a/docker/src/test/resources/containers.json b/docker/src/test/resources/containers.json
new file mode 100644
index 0000000..8f789b7
--- /dev/null
+++ b/docker/src/test/resources/containers.json
@@ -0,0 +1,20 @@
+[
+  {
+    "Command": "/usr/sbin/sshd -D",
+    "Created": 1395472605,
+    "Id": "6d35806c1bd2b25cd92bba2d2c2c5169dc2156f53ab45c2b62d76e2d2fee14a9",
+    "Image": "jclouds/ubuntu:latest",
+    "Names": [
+      "/hopeful_mclean"
+    ],
+    "Ports": [
+      {
+        "IP": "0.0.0.0",
+        "PrivatePort": 22,
+        "PublicPort": 49231,
+        "Type": "tcp"
+      }
+    ],
+    "Status": "Up 55 seconds"
+  }
+]
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9b124ee9/docker/src/test/resources/logback.xml
----------------------------------------------------------------------
diff --git a/docker/src/test/resources/logback.xml b/docker/src/test/resources/logback.xml
new file mode 100644
index 0000000..bbb5d7f
--- /dev/null
+++ b/docker/src/test/resources/logback.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0"?>
+<!--
+
+    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.
+
+-->
+<configuration>
+    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+        <encoder>
+            <pattern>- %msg%n</pattern>
+        </encoder>
+    </appender>
+    <root level="info">
+        <appender-ref ref="STDOUT"/>
+    </root>
+    <logger name="jclouds.compute" level="debug"/>
+    <logger name="net.schmizz" level="warn"/>
+    <logger name="jclouds.wire" level="debug"/>
+    <logger name="jclouds.headers" level="debug"/>
+    <logger name="jclouds.ssh" level="debug"/>
+</configuration>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9b124ee9/dockerFile
----------------------------------------------------------------------
diff --git a/dockerFile b/dockerFile
new file mode 100644
index 0000000..1318715
--- /dev/null
+++ b/dockerFile
@@ -0,0 +1,29 @@
+#
+# 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.
+#
+
+FROM centos:6.4
+MAINTAINER Andrea Turli <an...@gmail.com>
+
+# RUN yum -y groupinstall 'Development Tools'
+RUN yum -y install openssh-server openssh-clients
+
+RUN chkconfig sshd on
+RUN service sshd start
+RUN echo 'root:password' | chpasswd
+
+EXPOSE 22
+CMD ["/usr/sbin/sshd", "-D"]
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9b124ee9/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index badf621..d4e7ca8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -65,6 +65,7 @@
   </repositories>
 
   <modules>
+    <module>docker</module>
     <module>virtualbox</module>
     <module>vcloud-director</module>
     <module>dmtf</module>


[3/3] git commit: [JCLOUDS-500] Initial commit for docker

Posted by na...@apache.org.
[JCLOUDS-500] Initial commit for docker


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

Branch: refs/heads/master
Commit: 9b124ee9f12e0392b6d2f083308297bfcca8ea79
Parents: 422ffe0
Author: Andrea Turli <an...@gmail.com>
Authored: Tue Jan 28 10:42:43 2014 +0100
Committer: Ignasi Barrera <na...@apache.org>
Committed: Mon Jul 7 11:48:24 2014 +0200

----------------------------------------------------------------------
 docker/README.md                                |  48 ++
 docker/pom.xml                                  | 143 ++++++
 .../main/java/org/jclouds/docker/DockerApi.java |  37 ++
 .../org/jclouds/docker/DockerApiMetadata.java   |  98 ++++
 .../binders/BindInputStreamToRequest.java       |  68 +++
 .../DockerComputeServiceContextModule.java      |  58 +++
 .../functions/ContainerToNodeMetadata.java      | 126 +++++
 .../docker/compute/functions/ImageToImage.java  |  98 ++++
 .../docker/compute/functions/StateToStatus.java |  38 ++
 .../compute/options/DockerTemplateOptions.java  | 485 +++++++++++++++++++
 .../strategy/DockerComputeServiceAdapter.java   | 232 +++++++++
 .../docker/config/DockerHttpApiModule.java      |  40 ++
 .../docker/config/DockerParserModule.java       |  62 +++
 .../jclouds/docker/config/DockerProperties.java |  26 +
 .../java/org/jclouds/docker/domain/Config.java  | 457 +++++++++++++++++
 .../org/jclouds/docker/domain/Container.java    | 389 +++++++++++++++
 .../org/jclouds/docker/domain/ExposedPorts.java | 106 ++++
 .../org/jclouds/docker/domain/HostConfig.java   | 242 +++++++++
 .../java/org/jclouds/docker/domain/Image.java   | 239 +++++++++
 .../jclouds/docker/domain/NetworkSettings.java  | 175 +++++++
 .../java/org/jclouds/docker/domain/Port.java    |  88 ++++
 .../java/org/jclouds/docker/domain/State.java   | 166 +++++++
 .../java/org/jclouds/docker/domain/Version.java | 166 +++++++
 .../org/jclouds/docker/features/RemoteApi.java  | 272 +++++++++++
 .../docker/features/internal/Archives.java      |  60 +++
 .../docker/handlers/DockerErrorHandler.java     | 106 ++++
 .../jclouds/docker/options/BuildOptions.java    |  71 +++
 .../jclouds/docker/options/CommitOptions.java   | 109 +++++
 .../docker/options/CreateImageOptions.java      |  95 ++++
 .../docker/options/DeleteImageOptions.java      |  44 ++
 .../docker/options/ListContainerOptions.java    |  97 ++++
 .../docker/options/ListImageOptions.java        |  43 ++
 .../docker/options/RemoveContainerOptions.java  |  55 +++
 .../services/org.jclouds.apis.ApiMetadata       |   1 +
 .../jclouds/docker/DockerApiMetadataTest.java   |  47 ++
 .../binders/BindInputStreamToRequestTest.java   |  66 +++
 .../docker/compute/BaseDockerApiLiveTest.java   |  91 ++++
 .../DockerComputeServiceAdapterLiveTest.java    |  95 ++++
 .../compute/DockerComputeServiceLiveTest.java   | 142 ++++++
 .../functions/ContainerToNodeMetadataTest.java  | 204 ++++++++
 .../compute/functions/ImageToImageTest.java     |  74 +++
 .../compute/functions/StateToStatusTest.java    |  81 ++++
 .../options/DockerTemplateOptionsTest.java      |  62 +++
 .../docker/config/DockerParserModuleTest.java   |  52 ++
 .../docker/features/RemoteApiLiveTest.java      | 121 +++++
 .../docker/features/RemoteApiMockTest.java      | 376 ++++++++++++++
 .../docker/features/internal/ArchivesTest.java  | 112 +++++
 .../docker/internal/BaseDockerMockTest.java     | 118 +++++
 docker/src/test/resources/Dockerfile            |  29 ++
 .../src/test/resources/container-creation.json  |   1 +
 docker/src/test/resources/container.json        |  81 ++++
 docker/src/test/resources/containers.json       |  20 +
 docker/src/test/resources/logback.xml           |  34 ++
 dockerFile                                      |  29 ++
 pom.xml                                         |   1 +
 55 files changed, 6376 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9b124ee9/docker/README.md
----------------------------------------------------------------------
diff --git a/docker/README.md b/docker/README.md
new file mode 100644
index 0000000..1c4394a
--- /dev/null
+++ b/docker/README.md
@@ -0,0 +1,48 @@
+# Docker as a local cloud provider
+jclouds-docker is a local cloud provider modelled on [docker](http://www.docker.io). Similar to other jclouds supported
+providers, it supports the same portable abstractions offered by jclouds.
+
+##Setup
+
+Please follow these steps to configure your workstation for jclouds-docker:
+
+- install the latest Docker release (please visit https://docs.docker.com/installation/)
+
+#How it works
+
+
+                                               ---------------   -------------
+                                              |   Image(s)    | |   Node(s)   |
+                                              ---------------   -------------
+     ---------    docker remote api           ----------------------------------------
+    | jclouds | ---------------------------> |              DOCKER_HOST              |
+     ---------                               ----------------------------------------
+
+##Components
+
+- jclouds \- acts as a java client to access to docker features
+- DOCKER_HOST \- hosts Docker API, NB: jclouds-docker assumes that the latest Docker is installed
+- Image \- it is a docker image that can be started.
+- Node \- is a docker container
+
+## Assumptions
+
+- jclouds-docker assumes that the images specified using the template are ssh'able.
+
+--------------
+
+#Notes:
+- jclouds-docker is still at alpha stage please report any issues you find at [jclouds issues](https://issues.apache.org/jira/browse/JCLOUDS)
+- jclouds-docker has been tested on Mac OSX, it might work on Linux iff vbox is running and set up correctly. However, it has never been tried on Windows.
+
+--------------
+
+#Troubleshooting
+As jclouds docker support is quite new, issues may occasionally arise. Please follow these steps to get things going again:
+
+1. Remove all containers
+
+      `$ docker ps -a -q | xargs docker stop | xargs docker rm`
+2. remove all the images
+
+    `$ docker images -q | xargs docker rmi`

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9b124ee9/docker/pom.xml
----------------------------------------------------------------------
diff --git a/docker/pom.xml b/docker/pom.xml
new file mode 100644
index 0000000..42b9d4d
--- /dev/null
+++ b/docker/pom.xml
@@ -0,0 +1,143 @@
+<?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>1.8.0-SNAPSHOT</version>
+  </parent>
+
+  <!-- TODO: when out of labs, switch to org.jclouds.provider -->
+  <groupId>org.apache.jclouds.labs</groupId>
+  <artifactId>docker</artifactId>
+  <name>jclouds docker API</name>
+  <description>ComputeService binding to the Docker API</description>
+  <packaging>bundle</packaging>
+
+  <properties>
+    <test.docker.endpoint>https://localhost:4243</test.docker.endpoint>
+    <test.docker.api-version>1.10</test.docker.api-version>
+    <test.docker.identity>FIXME</test.docker.identity>
+    <test.docker.credential>FIXME</test.docker.credential>
+    <jclouds.osgi.export>org.jclouds.docker*;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>org.apache.commons</groupId>
+      <artifactId>commons-compress</artifactId>
+      <version>1.5</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.jclouds.driver</groupId>
+      <artifactId>jclouds-sshj</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>org.apache.jclouds.driver</groupId>
+      <artifactId>jclouds-slf4j</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>ch.qos.logback</groupId>
+      <artifactId>logback-classic</artifactId>
+      <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>
+                  <systemPropertyVariables>
+                    <test.docker.endpoint>${test.docker.endpoint}</test.docker.endpoint>
+                    <test.docker.api-version>${test.docker.api-version}</test.docker.api-version>
+                    <test.docker.credential>${test.docker.identity}</test.docker.credential>
+                    <test.docker.credential>${test.docker.credential}</test.docker.credential>
+                  </systemPropertyVariables>
+                </configuration>
+              </execution>
+            </executions>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+  </profiles>
+
+</project>

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9b124ee9/docker/src/main/java/org/jclouds/docker/DockerApi.java
----------------------------------------------------------------------
diff --git a/docker/src/main/java/org/jclouds/docker/DockerApi.java b/docker/src/main/java/org/jclouds/docker/DockerApi.java
new file mode 100644
index 0000000..54bf5c9
--- /dev/null
+++ b/docker/src/main/java/org/jclouds/docker/DockerApi.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.docker;
+
+import org.jclouds.docker.features.RemoteApi;
+import org.jclouds.rest.annotations.Delegate;
+
+import java.io.Closeable;
+
+/**
+ * Provides synchronous access to Docker Remote API.
+ *
+ * @see <a href="https://docs.docker.com/reference/api/docker_remote_api/"></a>
+ */
+public interface DockerApi extends Closeable {
+
+   /**
+    * Provides synchronous access to Docker Remote API features.
+    */
+   @Delegate
+   RemoteApi getRemoteApi();
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9b124ee9/docker/src/main/java/org/jclouds/docker/DockerApiMetadata.java
----------------------------------------------------------------------
diff --git a/docker/src/main/java/org/jclouds/docker/DockerApiMetadata.java b/docker/src/main/java/org/jclouds/docker/DockerApiMetadata.java
new file mode 100644
index 0000000..b281eb7
--- /dev/null
+++ b/docker/src/main/java/org/jclouds/docker/DockerApiMetadata.java
@@ -0,0 +1,98 @@
+/*
+ * 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.docker;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.inject.Module;
+import org.jclouds.Constants;
+import org.jclouds.apis.ApiMetadata;
+import org.jclouds.compute.ComputeServiceContext;
+import org.jclouds.compute.config.ComputeServiceProperties;
+import org.jclouds.docker.compute.config.DockerComputeServiceContextModule;
+import org.jclouds.docker.config.DockerHttpApiModule;
+import org.jclouds.docker.config.DockerParserModule;
+import org.jclouds.rest.internal.BaseHttpApiMetadata;
+
+import java.net.URI;
+import java.util.Properties;
+
+import static org.jclouds.compute.config.ComputeServiceProperties.TEMPLATE;
+import static org.jclouds.reflect.Reflection2.typeToken;
+
+/**
+ * Implementation of {@link BaseHttpApiMetadata} for the Docker API
+ */
+public class DockerApiMetadata extends BaseHttpApiMetadata<DockerApi> {
+
+   @Override
+   public Builder toBuilder() {
+      return new Builder().fromApiMetadata(this);
+   }
+
+   public DockerApiMetadata() {
+      this(new Builder());
+   }
+
+   protected DockerApiMetadata(Builder builder) {
+      super(builder);
+   }
+
+   public static Properties defaultProperties() {
+      Properties properties = BaseHttpApiMetadata.defaultProperties();
+      properties.setProperty(Constants.PROPERTY_MAX_RETRIES, "15");
+      properties.setProperty("jclouds.ssh.retry-auth", "true");
+      properties.setProperty(Constants.PROPERTY_CONNECTION_TIMEOUT, "1200000"); // 15 minutes
+      properties.setProperty(ComputeServiceProperties.IMAGE_LOGIN_USER, "root:password");
+      properties.setProperty(TEMPLATE, "osFamily=UBUNTU,os64Bit=true,osVersionMatches=1[012].[01][04]");
+      return properties;
+   }
+
+   public static class Builder extends BaseHttpApiMetadata.Builder<DockerApi, Builder> {
+
+      protected Builder() {
+         super(DockerApi.class);
+         id("docker")
+                 .name("Docker API")
+                 .identityName("user")
+                 .credentialName("password")
+                 .documentation(URI.create("https://docs.docker.com/reference/api/docker_remote_api/"))
+                 .version("1.12")
+                 .defaultEndpoint("http://127.0.0.1:2375")
+                 .defaultProperties(DockerApiMetadata.defaultProperties())
+                 .view(typeToken(ComputeServiceContext.class))
+                 .defaultModules(ImmutableSet.<Class<? extends Module>>of(
+                         DockerHttpApiModule.class,
+                         DockerParserModule.class,
+                         DockerComputeServiceContextModule.class));
+      }
+
+      @Override
+      public DockerApiMetadata build() {
+         return new DockerApiMetadata(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/9b124ee9/docker/src/main/java/org/jclouds/docker/binders/BindInputStreamToRequest.java
----------------------------------------------------------------------
diff --git a/docker/src/main/java/org/jclouds/docker/binders/BindInputStreamToRequest.java b/docker/src/main/java/org/jclouds/docker/binders/BindInputStreamToRequest.java
new file mode 100644
index 0000000..855a2e5
--- /dev/null
+++ b/docker/src/main/java/org/jclouds/docker/binders/BindInputStreamToRequest.java
@@ -0,0 +1,68 @@
+/*
+ * 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.docker.binders;
+
+import com.google.common.base.Throwables;
+import com.google.common.io.Files;
+import org.jclouds.compute.reference.ComputeServiceConstants;
+import org.jclouds.docker.features.internal.Archives;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.io.Payload;
+import org.jclouds.io.Payloads;
+import org.jclouds.logging.Logger;
+import org.jclouds.rest.Binder;
+
+import javax.annotation.Resource;
+import javax.inject.Named;
+import javax.inject.Singleton;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+@Singleton
+public class BindInputStreamToRequest implements Binder {
+
+   @Resource
+   @Named(ComputeServiceConstants.COMPUTE_LOGGER)
+   protected Logger logger = Logger.NULL;
+
+   @Override
+   public <R extends HttpRequest> R bindToRequest(R request, Object input) {
+      checkArgument(checkNotNull(input, "input") instanceof File, "this binder is only valid for File!");
+      checkNotNull(request, "request");
+
+      File dockerFile = (File) input;
+      File tmpDir = Files.createTempDir();
+      final File targetFile = new File(tmpDir + File.separator + "Dockerfile");
+      try {
+         Files.copy(dockerFile, targetFile);
+         File archive = Archives.tar(tmpDir, File.createTempFile("archive", ".tar"));
+         FileInputStream data = new FileInputStream(archive);
+         Payload payload = Payloads.newInputStreamPayload(data);
+         payload.getContentMetadata().setContentLength(data.getChannel().size());
+         payload.getContentMetadata().setContentType("application/tar");
+         request.setPayload(payload);
+      } catch (IOException e) {
+         logger.error(e, "Couldn't create a tarball for %s", targetFile);
+         throw Throwables.propagate(e);
+      }
+      return request;
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9b124ee9/docker/src/main/java/org/jclouds/docker/compute/config/DockerComputeServiceContextModule.java
----------------------------------------------------------------------
diff --git a/docker/src/main/java/org/jclouds/docker/compute/config/DockerComputeServiceContextModule.java b/docker/src/main/java/org/jclouds/docker/compute/config/DockerComputeServiceContextModule.java
new file mode 100644
index 0000000..b1cfee2
--- /dev/null
+++ b/docker/src/main/java/org/jclouds/docker/compute/config/DockerComputeServiceContextModule.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.docker.compute.config;
+
+import com.google.common.base.Function;
+import com.google.inject.TypeLiteral;
+import org.jclouds.compute.ComputeServiceAdapter;
+import org.jclouds.compute.config.ComputeServiceAdapterContextModule;
+import org.jclouds.compute.domain.Hardware;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.options.TemplateOptions;
+import org.jclouds.docker.compute.functions.ContainerToNodeMetadata;
+import org.jclouds.docker.compute.functions.ImageToImage;
+import org.jclouds.docker.compute.functions.StateToStatus;
+import org.jclouds.docker.compute.options.DockerTemplateOptions;
+import org.jclouds.docker.compute.strategy.DockerComputeServiceAdapter;
+import org.jclouds.docker.domain.Container;
+import org.jclouds.docker.domain.Image;
+import org.jclouds.docker.domain.State;
+import org.jclouds.domain.Location;
+import org.jclouds.functions.IdentityFunction;
+
+public class DockerComputeServiceContextModule extends
+        ComputeServiceAdapterContextModule<Container, Hardware, Image, Location> {
+
+   @Override
+   protected void configure() {
+      super.configure();
+      bind(new TypeLiteral<ComputeServiceAdapter<Container, Hardware, Image, Location>>() {
+      }).to(DockerComputeServiceAdapter.class);
+      bind(new TypeLiteral<Function<Container, NodeMetadata>>() {
+      }).to(ContainerToNodeMetadata.class);
+      bind(new TypeLiteral<Function<Image, org.jclouds.compute.domain.Image>>() {
+      }).to(ImageToImage.class);
+      bind(new TypeLiteral<Function<Hardware, Hardware>>() {
+      }).to(Class.class.cast(IdentityFunction.class));
+      bind(new TypeLiteral<Function<Location, Location>>() {
+      }).to(Class.class.cast(IdentityFunction.class));
+      bind(new TypeLiteral<Function<State, NodeMetadata.Status>>() {
+      }).to(StateToStatus.class);
+      bind(TemplateOptions.class).to(DockerTemplateOptions.class);
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9b124ee9/docker/src/main/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadata.java
----------------------------------------------------------------------
diff --git a/docker/src/main/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadata.java b/docker/src/main/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadata.java
new file mode 100644
index 0000000..72c16af
--- /dev/null
+++ b/docker/src/main/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadata.java
@@ -0,0 +1,126 @@
+/*
+ * 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.docker.compute.functions;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import java.net.URI;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.inject.Inject;
+
+import org.jclouds.collect.Memoized;
+import org.jclouds.compute.domain.HardwareBuilder;
+import org.jclouds.compute.domain.Image;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.domain.NodeMetadataBuilder;
+import org.jclouds.compute.domain.Processor;
+import org.jclouds.compute.functions.GroupNamingConvention;
+import org.jclouds.docker.domain.Container;
+import org.jclouds.docker.domain.Port;
+import org.jclouds.docker.domain.State;
+import org.jclouds.domain.Location;
+import org.jclouds.providers.ProviderMetadata;
+
+import com.google.common.base.Function;
+import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.inject.Singleton;
+
+@Singleton
+public class ContainerToNodeMetadata implements Function<Container, NodeMetadata> {
+
+   private final ProviderMetadata providerMetadata;
+   private final Function<State, NodeMetadata.Status> toPortableStatus;
+   private final GroupNamingConvention nodeNamingConvention;
+   private final Supplier<Map<String, ? extends Image>> images;
+   private final Supplier<Set<? extends Location>> locations;
+
+   @Inject
+   public ContainerToNodeMetadata(ProviderMetadata providerMetadata, Function<State,
+           NodeMetadata.Status> toPortableStatus, GroupNamingConvention.Factory namingConvention,
+                                  Supplier<Map<String, ? extends Image>> images,
+                                  @Memoized Supplier<Set<? extends Location>> locations) {
+      this.providerMetadata = checkNotNull(providerMetadata, "providerMetadata");
+      this.toPortableStatus = checkNotNull(toPortableStatus, "toPortableStatus cannot be null");
+      this.nodeNamingConvention = checkNotNull(namingConvention, "namingConvention").createWithoutPrefix();
+      this.images = checkNotNull(images, "images cannot be null");
+      this.locations = checkNotNull(locations, "locations");
+   }
+
+   @Override
+   public NodeMetadata apply(Container container) {
+      String name = cleanUpName(container.getName());
+      String group = nodeNamingConvention.extractGroup(name);
+      NodeMetadataBuilder builder = new NodeMetadataBuilder();
+      builder.ids(container.getId())
+              .name(name)
+              .group(group)
+              .hostname(container.getContainerConfig().getHostname())
+               // TODO Set up hardware
+              .hardware(new HardwareBuilder()
+                      .id("")
+                      .ram(container.getContainerConfig().getMemory())
+                      .processor(new Processor(container.getContainerConfig().getCpuShares(), container.getContainerConfig().getCpuShares()))
+                      .build());
+      builder.status(toPortableStatus.apply(container.getState()));
+      builder.imageId(container.getImage());
+      builder.loginPort(getLoginPort(container));
+      builder.publicAddresses(getPublicIpAddresses());
+      builder.privateAddresses(getPrivateIpAddresses(container));
+      builder.location(Iterables.getOnlyElement(locations.get()));
+      Image image = images.get().get(container.getImage());
+      builder.imageId(image.getId());
+      builder.operatingSystem(image.getOperatingSystem());
+
+      return builder.build();
+   }
+
+   private String cleanUpName(String name) {
+      return name.startsWith("/") ? name.substring(1) : name;
+   }
+
+   private Iterable<String> getPrivateIpAddresses(Container container) {
+      if (container.getNetworkSettings() == null) return ImmutableList.of();
+      return ImmutableList.of(container.getNetworkSettings().getIpAddress());
+   }
+
+   private List<String> getPublicIpAddresses() {
+      String dockerIpAddress = URI.create(providerMetadata.getEndpoint()).getHost();
+      return ImmutableList.of(dockerIpAddress);
+   }
+
+   protected static int getLoginPort(Container container) {
+      if (container.getNetworkSettings() != null) {
+          Map<String, List<Map<String, String>>> ports = container.getNetworkSettings().getPorts();
+          if (ports != null) {
+            return Integer.parseInt(getOnlyElement(ports.get("22/tcp")).get("HostPort"));
+          }
+      // this is needed in case the container list is coming from listContainers
+      } else if (container.getPorts() != null) {
+         for (Port port : container.getPorts()) {
+            if (port.getPrivatePort() == 22) {
+               return port.getPublicPort();
+            }
+         }
+      }
+      throw new IllegalStateException("Cannot determine the login port for " + container.getId());
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9b124ee9/docker/src/main/java/org/jclouds/docker/compute/functions/ImageToImage.java
----------------------------------------------------------------------
diff --git a/docker/src/main/java/org/jclouds/docker/compute/functions/ImageToImage.java b/docker/src/main/java/org/jclouds/docker/compute/functions/ImageToImage.java
new file mode 100644
index 0000000..4e3358d
--- /dev/null
+++ b/docker/src/main/java/org/jclouds/docker/compute/functions/ImageToImage.java
@@ -0,0 +1,98 @@
+/*
+ * 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.docker.compute.functions;
+
+import com.google.common.base.Function;
+import com.google.common.base.Splitter;
+import com.google.common.collect.Iterables;
+import org.jclouds.compute.domain.Image;
+import org.jclouds.compute.domain.ImageBuilder;
+import org.jclouds.compute.domain.OperatingSystem;
+import org.jclouds.compute.domain.OsFamily;
+import org.jclouds.compute.reference.ComputeServiceConstants;
+import org.jclouds.logging.Logger;
+
+import javax.annotation.Resource;
+import javax.inject.Named;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Iterables.get;
+
+public class ImageToImage implements Function<org.jclouds.docker.domain.Image, org.jclouds.compute.domain.Image> {
+
+   private static final String CENTOS = "centos";
+   private static final String UBUNTU = "ubuntu";
+
+   @Resource
+   @Named(ComputeServiceConstants.COMPUTE_LOGGER)
+   protected Logger logger = Logger.NULL;
+
+   @Override
+   public Image apply(org.jclouds.docker.domain.Image from) {
+      checkNotNull(from, "image");
+      String description = checkNotNull(Iterables.getFirst(from.getRepoTags(), "image must have at least one repo tag"));
+
+      OsFamily osFamily = osFamily().apply(description);
+      String osVersion = parseVersion(description);
+
+      OperatingSystem os = OperatingSystem.builder()
+              .description(description)
+              .family(osFamily)
+              .version(osVersion)
+              .is64Bit(is64bit(from))
+              .build();
+
+      return new ImageBuilder()
+              .ids(from.getId())
+              .name(get(Splitter.on(":").split(description), 0))
+              .description(description)
+              .operatingSystem(os)
+              .status(Image.Status.AVAILABLE)
+              .build();
+   }
+
+   private boolean is64bit(org.jclouds.docker.domain.Image inspectedImage) {
+      if (inspectedImage.getArchitecture() == null) return true;
+      return inspectedImage.getArchitecture().matches("x86_64|amd64");
+   }
+
+   /**
+    * Parses the item description to determine the OSFamily
+    *
+    * @return the @see OsFamily or OsFamily.UNRECOGNIZED
+    */
+   private Function<String, OsFamily> osFamily() {
+      return new Function<String, OsFamily>() {
+
+         @Override
+         public OsFamily apply(final String description) {
+            if (description != null) {
+               if (description.contains(CENTOS)) return OsFamily.CENTOS;
+               else if (description.contains(UBUNTU)) return OsFamily.UBUNTU;
+            }
+            return OsFamily.UNRECOGNIZED;
+         }
+      };
+   }
+
+   private String parseVersion(String description) {
+      String version = get(Splitter.on(":").split(description), 1);
+      logger.debug("os version for item: %s is %s", description, version);
+      return version;
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9b124ee9/docker/src/main/java/org/jclouds/docker/compute/functions/StateToStatus.java
----------------------------------------------------------------------
diff --git a/docker/src/main/java/org/jclouds/docker/compute/functions/StateToStatus.java b/docker/src/main/java/org/jclouds/docker/compute/functions/StateToStatus.java
new file mode 100644
index 0000000..2d87384
--- /dev/null
+++ b/docker/src/main/java/org/jclouds/docker/compute/functions/StateToStatus.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.docker.compute.functions;
+
+import javax.inject.Singleton;
+
+import org.jclouds.compute.domain.NodeMetadata.Status;
+import org.jclouds.docker.domain.State;
+
+import com.google.common.base.Function;
+
+/**
+ * Transforms an {@link org.jclouds.docker.domain.Container} to the jclouds portable model.
+ */
+@Singleton
+public class StateToStatus implements Function<State, Status> {
+
+   @Override
+   public Status apply(final State state) {
+      if (state == null) return Status.UNRECOGNIZED;
+      return state.isRunning() ? Status.RUNNING : Status.TERMINATED;
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9b124ee9/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java
----------------------------------------------------------------------
diff --git a/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java b/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java
new file mode 100644
index 0000000..5e4669b
--- /dev/null
+++ b/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java
@@ -0,0 +1,485 @@
+/*
+ * 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.docker.compute.options;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import org.jclouds.compute.options.TemplateOptions;
+import org.jclouds.domain.LoginCredentials;
+import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.scriptbuilder.domain.Statement;
+
+import java.util.List;
+import java.util.Map;
+
+import static com.google.common.base.Objects.equal;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Contains options supported in the {@code ComputeService#runNode} operation on the
+ * "docker" provider. <h2>Usage</h2> The recommended way to instantiate a
+ * DockerTemplateOptions object is to statically import DockerTemplateOptions.* and invoke a static
+ * creation method followed by an instance mutator (if needed):
+ * <p/>
+ * <code>
+ * import static org.jclouds.docker.compute.options.DockerTemplateOptions.Builder.*;
+ * <p/>
+ * ComputeService api = // get connection
+ * templateBuilder.options(inboundPorts(22, 80, 8080, 443));
+ * Set<? extends NodeMetadata> set = api.createNodesInGroup(tag, 2, templateBuilder.build());
+ * <code>
+ */
+public class DockerTemplateOptions extends TemplateOptions implements Cloneable {
+
+   protected Optional<String> dns = Optional.absent();
+   protected Optional<String> hostname = Optional.absent();
+   protected Optional<Integer> memory = Optional.absent();
+   protected Optional<Integer> cpuShares = Optional.absent();
+   protected Optional<List<String>> commands = Optional.absent();
+   protected Optional<Map<String, String>> volumes = Optional.absent();
+
+   @Override
+   public DockerTemplateOptions clone() {
+      DockerTemplateOptions options = new DockerTemplateOptions();
+      copyTo(options);
+      return options;
+   }
+
+   @Override
+   public void copyTo(TemplateOptions to) {
+      super.copyTo(to);
+      if (to instanceof DockerTemplateOptions) {
+         DockerTemplateOptions eTo = DockerTemplateOptions.class.cast(to);
+         if (volumes.isPresent()) {
+            eTo.volumes(getVolumes().get());
+         }
+         if (hostname.isPresent()) {
+            eTo.hostname(hostname.get());
+         }
+         if (dns.isPresent()) {
+            eTo.dns(dns.get());
+         }
+         if (memory.isPresent()) {
+            eTo.memory(memory.get());
+         }
+         if (commands.isPresent()) {
+            eTo.commands(commands.get());
+         }
+         if (cpuShares.isPresent()) {
+            eTo.cpuShares(cpuShares.get());
+         }
+      }
+   }
+
+   @Override
+   public boolean equals(Object o) {
+      if (this == o)
+         return true;
+      if (o == null || getClass() != o.getClass())
+         return false;
+      DockerTemplateOptions that = DockerTemplateOptions.class.cast(o);
+      return super.equals(that) && equal(this.volumes, that.volumes) &&
+              equal(this.hostname, that.hostname) &&
+              equal(this.dns, that.dns) &&
+              equal(this.memory, that.memory) &&
+              equal(this.commands, that.commands) &&
+              equal(this.cpuShares, that.cpuShares);
+   }
+
+   @Override
+   public int hashCode() {
+      return Objects.hashCode(super.hashCode(), volumes, hostname, dns, memory, commands, cpuShares);
+   }
+
+   @Override
+   public String toString() {
+      return Objects.toStringHelper(this)
+              .add("dns", dns)
+              .add("hostname", hostname)
+              .add("memory", memory)
+              .add("cpuShares", cpuShares)
+              .add("commands", commands)
+              .add("volumes", volumes)
+              .toString();
+   }
+
+   public static final DockerTemplateOptions NONE = new DockerTemplateOptions();
+
+   public DockerTemplateOptions volumes(Map<String, String> volumes) {
+      this.volumes = Optional.<Map<String, String>> of(ImmutableMap.copyOf(volumes));
+      return this;
+   }
+
+   public TemplateOptions dns(@Nullable String dns) {
+      this.dns = Optional.fromNullable(dns);
+      return this;
+   }
+
+   public TemplateOptions hostname(@Nullable String hostname) {
+      this.hostname = Optional.fromNullable(hostname);
+      return this;
+   }
+
+   public TemplateOptions memory(@Nullable Integer memory) {
+      this.memory = Optional.fromNullable(memory);
+      return this;
+   }
+
+   public TemplateOptions commands(Iterable<String> commands) {
+      for (String command : checkNotNull(commands, "commands"))
+         checkNotNull(command, "all commands must be non-empty");
+      this.commands = Optional.<List<String>> of(ImmutableList.copyOf(commands));
+      return this;
+   }
+
+   public TemplateOptions commands(String... commands) {
+      return commands(ImmutableList.copyOf(checkNotNull(commands, "commands")));
+   }
+
+   public TemplateOptions cpuShares(@Nullable Integer cpuShares) {
+      this.cpuShares = Optional.fromNullable(cpuShares);
+      return this;
+   }
+
+   public Optional<Map<String, String>> getVolumes() {
+      return volumes;
+   }
+
+   public Optional<String> getDns() { return dns; }
+
+   public Optional<String> getHostname() { return hostname; }
+
+   public Optional<Integer> getMemory() { return memory; }
+
+   public Optional<List<String>> getCommands() {
+      return commands;
+   }
+
+   public Optional<Integer> getCpuShares() { return cpuShares; }
+
+   public static class Builder {
+
+      /**
+       * @see DockerTemplateOptions#volumes(java.util.Map)
+       */
+      public static DockerTemplateOptions volumes(Map<String, String> volumes) {
+         DockerTemplateOptions options = new DockerTemplateOptions();
+         return DockerTemplateOptions.class.cast(options.volumes(volumes));
+      }
+
+      /**
+       * @see DockerTemplateOptions#dns(String)
+       */
+      public static DockerTemplateOptions dns(String dns) {
+         DockerTemplateOptions options = new DockerTemplateOptions();
+         return DockerTemplateOptions.class.cast(options.dns(dns));
+      }
+
+      /**
+       * @see DockerTemplateOptions#hostname(String)
+       */
+      public static DockerTemplateOptions hostname(String hostname) {
+         DockerTemplateOptions options = new DockerTemplateOptions();
+         return DockerTemplateOptions.class.cast(options.hostname(hostname));
+      }
+
+      /**
+       * @see DockerTemplateOptions#memory(int)
+       */
+      public static DockerTemplateOptions memory(int memory) {
+         DockerTemplateOptions options = new DockerTemplateOptions();
+         return DockerTemplateOptions.class.cast(options.memory(memory));
+      }
+
+      /**
+       * @see DockerTemplateOptions#commands(Iterable)
+       */
+      public static DockerTemplateOptions commands(String... commands) {
+         DockerTemplateOptions options = new DockerTemplateOptions();
+         return DockerTemplateOptions.class.cast(options.commands(commands));
+      }
+
+      public static DockerTemplateOptions commands(Iterable<String> commands) {
+         DockerTemplateOptions options = new DockerTemplateOptions();
+         return DockerTemplateOptions.class.cast(options.commands(commands));
+      }
+
+      /**
+       * @see DockerTemplateOptions#cpuShares(int)
+       */
+      public static DockerTemplateOptions cpuShares(int cpuShares) {
+         DockerTemplateOptions options = new DockerTemplateOptions();
+         return DockerTemplateOptions.class.cast(options.cpuShares(cpuShares));
+      }
+
+      // methods that only facilitate returning the correct object type
+
+      /**
+       * @see TemplateOptions#inboundPorts
+       */
+      public static DockerTemplateOptions inboundPorts(int... ports) {
+         DockerTemplateOptions options = new DockerTemplateOptions();
+         return DockerTemplateOptions.class.cast(options.inboundPorts(ports));
+      }
+
+      /**
+       * @see TemplateOptions#port
+       */
+      public static DockerTemplateOptions blockOnPort(int port, int seconds) {
+         DockerTemplateOptions options = new DockerTemplateOptions();
+         return DockerTemplateOptions.class.cast(options.blockOnPort(port, seconds));
+      }
+
+      /**
+       * @see TemplateOptions#installPrivateKey
+       */
+      public static DockerTemplateOptions installPrivateKey(String rsaKey) {
+         DockerTemplateOptions options = new DockerTemplateOptions();
+         return DockerTemplateOptions.class.cast(options.installPrivateKey(rsaKey));
+      }
+
+      /**
+       * @see TemplateOptions#authorizePublicKey
+       */
+      public static DockerTemplateOptions authorizePublicKey(String rsaKey) {
+         DockerTemplateOptions options = new DockerTemplateOptions();
+         return DockerTemplateOptions.class.cast(options.authorizePublicKey(rsaKey));
+      }
+
+      /**
+       * @see TemplateOptions#userMetadata
+       */
+      public static DockerTemplateOptions userMetadata(Map<String, String> userMetadata) {
+         DockerTemplateOptions options = new DockerTemplateOptions();
+         return DockerTemplateOptions.class.cast(options.userMetadata(userMetadata));
+      }
+
+      /**
+       * @see TemplateOptions#nodeNames(Iterable)
+       */
+      public static DockerTemplateOptions nodeNames(Iterable<String> nodeNames) {
+         DockerTemplateOptions options = new DockerTemplateOptions();
+         return DockerTemplateOptions.class.cast(options.nodeNames(nodeNames));
+      }
+
+      /**
+       * @see TemplateOptions#networks(Iterable)
+       */
+      public static DockerTemplateOptions networks(Iterable<String> networks) {
+         DockerTemplateOptions options = new DockerTemplateOptions();
+         return DockerTemplateOptions.class.cast(options.networks(networks));
+      }
+
+      /**
+       * @see TemplateOptions#overrideLoginUser
+       */
+      public static DockerTemplateOptions overrideLoginUser(String user) {
+         DockerTemplateOptions options = new DockerTemplateOptions();
+         return options.overrideLoginUser(user);
+      }
+
+      /**
+       * @see TemplateOptions#overrideLoginPassword
+       */
+      public static DockerTemplateOptions overrideLoginPassword(String password) {
+         DockerTemplateOptions options = new DockerTemplateOptions();
+         return options.overrideLoginPassword(password);
+      }
+
+      /**
+       * @see TemplateOptions#overrideLoginPrivateKey
+       */
+      public static DockerTemplateOptions overrideLoginPrivateKey(String privateKey) {
+         DockerTemplateOptions options = new DockerTemplateOptions();
+         return options.overrideLoginPrivateKey(privateKey);
+      }
+
+      /**
+       * @see TemplateOptions#overrideAuthenticateSudo
+       */
+      public static DockerTemplateOptions overrideAuthenticateSudo(boolean authenticateSudo) {
+         DockerTemplateOptions options = new DockerTemplateOptions();
+         return options.overrideAuthenticateSudo(authenticateSudo);
+      }
+
+      /**
+       * @see TemplateOptions#overrideLoginCredentials
+       */
+      public static DockerTemplateOptions overrideLoginCredentials(LoginCredentials credentials) {
+         DockerTemplateOptions options = new DockerTemplateOptions();
+         return options.overrideLoginCredentials(credentials);
+      }
+
+      /**
+       * @see TemplateOptions#blockUntilRunning
+       */
+      public static DockerTemplateOptions blockUntilRunning(boolean blockUntilRunning) {
+         DockerTemplateOptions options = new DockerTemplateOptions();
+         return options.blockUntilRunning(blockUntilRunning);
+      }
+
+   }
+
+   // methods that only facilitate returning the correct object type
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public DockerTemplateOptions blockOnPort(int port, int seconds) {
+      return DockerTemplateOptions.class.cast(super.blockOnPort(port, seconds));
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public DockerTemplateOptions inboundPorts(int... ports) {
+      return DockerTemplateOptions.class.cast(super.inboundPorts(ports));
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public DockerTemplateOptions authorizePublicKey(String publicKey) {
+      return DockerTemplateOptions.class.cast(super.authorizePublicKey(publicKey));
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public DockerTemplateOptions installPrivateKey(String privateKey) {
+      return DockerTemplateOptions.class.cast(super.installPrivateKey(privateKey));
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public DockerTemplateOptions blockUntilRunning(boolean blockUntilRunning) {
+      return DockerTemplateOptions.class.cast(super.blockUntilRunning(blockUntilRunning));
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public DockerTemplateOptions dontAuthorizePublicKey() {
+      return DockerTemplateOptions.class.cast(super.dontAuthorizePublicKey());
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public DockerTemplateOptions nameTask(String name) {
+      return DockerTemplateOptions.class.cast(super.nameTask(name));
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public DockerTemplateOptions runAsRoot(boolean runAsRoot) {
+      return DockerTemplateOptions.class.cast(super.runAsRoot(runAsRoot));
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public DockerTemplateOptions runScript(Statement script) {
+      return DockerTemplateOptions.class.cast(super.runScript(script));
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public DockerTemplateOptions overrideLoginCredentials(LoginCredentials overridingCredentials) {
+      return DockerTemplateOptions.class.cast(super.overrideLoginCredentials(overridingCredentials));
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public DockerTemplateOptions overrideLoginPassword(String password) {
+      return DockerTemplateOptions.class.cast(super.overrideLoginPassword(password));
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public DockerTemplateOptions overrideLoginPrivateKey(String privateKey) {
+      return DockerTemplateOptions.class.cast(super.overrideLoginPrivateKey(privateKey));
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public DockerTemplateOptions overrideLoginUser(String loginUser) {
+      return DockerTemplateOptions.class.cast(super.overrideLoginUser(loginUser));
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public DockerTemplateOptions overrideAuthenticateSudo(boolean authenticateSudo) {
+      return DockerTemplateOptions.class.cast(super.overrideAuthenticateSudo(authenticateSudo));
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public DockerTemplateOptions userMetadata(Map<String, String> userMetadata) {
+      return DockerTemplateOptions.class.cast(super.userMetadata(userMetadata));
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public DockerTemplateOptions userMetadata(String key, String value) {
+      return DockerTemplateOptions.class.cast(super.userMetadata(key, value));
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public DockerTemplateOptions nodeNames(Iterable<String> nodeNames) {
+      return DockerTemplateOptions.class.cast(super.nodeNames(nodeNames));
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public DockerTemplateOptions networks(Iterable<String> networks) {
+      return DockerTemplateOptions.class.cast(super.networks(networks));
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9b124ee9/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java
----------------------------------------------------------------------
diff --git a/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java b/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java
new file mode 100644
index 0000000..7bbead3
--- /dev/null
+++ b/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java
@@ -0,0 +1,232 @@
+/*
+ * 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.docker.compute.strategy;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Iterables.find;
+
+import java.util.Map;
+import java.util.Set;
+
+import javax.annotation.Resource;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import org.jclouds.compute.ComputeServiceAdapter;
+import org.jclouds.compute.domain.Hardware;
+import org.jclouds.compute.domain.HardwareBuilder;
+import org.jclouds.compute.domain.Processor;
+import org.jclouds.compute.domain.Template;
+import org.jclouds.compute.reference.ComputeServiceConstants;
+import org.jclouds.docker.DockerApi;
+import org.jclouds.docker.compute.options.DockerTemplateOptions;
+import org.jclouds.docker.domain.Config;
+import org.jclouds.docker.domain.Container;
+import org.jclouds.docker.domain.HostConfig;
+import org.jclouds.docker.domain.Image;
+import org.jclouds.docker.options.ListContainerOptions;
+import org.jclouds.docker.options.RemoveContainerOptions;
+import org.jclouds.domain.Location;
+import org.jclouds.domain.LoginCredentials;
+import org.jclouds.logging.Logger;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+
+/**
+ * defines the connection between the {@link org.jclouds.docker.DockerApi} implementation and
+ * the jclouds {@link org.jclouds.compute.ComputeService}
+ */
+@Singleton
+public class DockerComputeServiceAdapter implements
+        ComputeServiceAdapter<Container, Hardware, Image, Location> {
+
+   @Resource
+   @Named(ComputeServiceConstants.COMPUTE_LOGGER)
+   protected Logger logger = Logger.NULL;
+
+   private final DockerApi api;
+
+   @Inject
+   public DockerComputeServiceAdapter(DockerApi api) {
+      this.api = checkNotNull(api, "api");
+   }
+
+   @Override
+   public NodeAndInitialCredentials<Container> createNodeWithGroupEncodedIntoName(String group, String name,
+                                                                                  Template template) {
+      checkNotNull(template, "template was null");
+      checkNotNull(template.getOptions(), "template options was null");
+
+      String imageId = checkNotNull(template.getImage().getId(), "template image id must not be null");
+      String loginUser = template.getImage().getDefaultCredentials().getUser();
+      String loginUserPassword = template.getImage().getDefaultCredentials().getPassword();
+
+      DockerTemplateOptions templateOptions = DockerTemplateOptions.class.cast(template.getOptions());
+      int[] inboundPorts = templateOptions.getInboundPorts();
+
+      Map<String, Object> exposedPorts = Maps.newHashMap();
+      for (int inboundPort : inboundPorts) {
+         exposedPorts.put(inboundPort + "/tcp", Maps.newHashMap());
+      }
+
+      Config.Builder containerConfigBuilder = Config.builder()
+              .imageId(imageId)
+              .exposedPorts(exposedPorts);
+
+      if (templateOptions.getCommands().isPresent()) {
+         containerConfigBuilder.cmd(templateOptions.getCommands().get());
+      }
+
+      if (templateOptions.getMemory().isPresent()) {
+         containerConfigBuilder.memory(templateOptions.getMemory().get());
+      }
+
+      if (templateOptions.getCpuShares().isPresent()) {
+         containerConfigBuilder.cpuShares(templateOptions.getCpuShares().get());
+      }
+
+      if (templateOptions.getVolumes().isPresent()) {
+         Map<String, Object> volumes = Maps.newLinkedHashMap();
+         for (String containerDir : templateOptions.getVolumes().get().values()) {
+            volumes.put(containerDir, Maps.newHashMap());
+         }
+         containerConfigBuilder.volumes(volumes);
+      }
+      Config containerConfig = containerConfigBuilder.build();
+
+      logger.debug(">> creating new container with containerConfig(%s)", containerConfig);
+      Container container = api.getRemoteApi().createContainer(name, containerConfig);
+      logger.trace("<< container(%s)", container.getId());
+
+      HostConfig.Builder hostConfigBuilder = HostConfig.builder()
+              .publishAllPorts(true)
+              .privileged(true);
+
+      if (templateOptions.getDns().isPresent()) {
+         hostConfigBuilder.dns(templateOptions.getDns().get());
+      }
+      // set up for volume bindings
+      if (templateOptions.getVolumes().isPresent()) {
+         for (Map.Entry<String, String> entry : templateOptions.getVolumes().get().entrySet()) {
+            hostConfigBuilder.binds(ImmutableList.of(entry.getKey() + ":" + entry.getValue()));
+         }
+      }
+      HostConfig hostConfig = hostConfigBuilder.build();
+
+      api.getRemoteApi().startContainer(container.getId(), hostConfig);
+      container = api.getRemoteApi().inspectContainer(container.getId());
+      if (container.getState().getExitCode() != 0) {
+         destroyNode(container.getId());
+         throw new IllegalStateException(String.format("Container %s has not started correctly", container.getId()));
+      }
+      return new NodeAndInitialCredentials<Container>(container, container.getId(),
+              LoginCredentials.builder().user(loginUser).password(loginUserPassword).build());
+   }
+
+   @Override
+   public Iterable<Hardware> listHardwareProfiles() {
+      Set<Hardware> hardware = Sets.newLinkedHashSet();
+      // todo they are only placeholders at the moment
+      hardware.add(new HardwareBuilder().ids("micro").hypervisor("lxc").name("micro").processor(new Processor(1, 1)).ram(512).build());
+      hardware.add(new HardwareBuilder().ids("small").hypervisor("lxc").name("small").processor(new Processor(1, 1)).ram(1024).build());
+      hardware.add(new HardwareBuilder().ids("medium").hypervisor("lxc").name("medium").processor(new Processor(1, 1)).ram(2048).build());
+      hardware.add(new HardwareBuilder().ids("large").hypervisor("lxc").name("large").processor(new Processor(1, 1)).ram(3072).build());
+      return hardware;
+   }
+
+   @Override
+   public Set<Image> listImages() {
+      Set<Image> images = Sets.newHashSet();
+      for (Image image : api.getRemoteApi().listImages()) {
+         // less efficient than just listImages but returns richer json that needs repoTags coming from listImages
+         Image inspected = api.getRemoteApi().inspectImage(image.getId());
+         if (inspected.getRepoTags().isEmpty()) {
+            inspected = Image.builder().fromImage(inspected).repoTags(image.getRepoTags()).build();
+         }
+         images.add(inspected);
+      }
+      return images;
+   }
+
+   @Override
+   public Image getImage(final String imageId) {
+      // less efficient than just inspectImage but listImages return repoTags
+      return find(listImages(), new Predicate<Image>() {
+
+         @Override
+         public boolean apply(Image input) {
+            return input.getId().equals(imageId);
+         }
+      }, null);
+   }
+
+   @Override
+   public Iterable<Container> listNodes() {
+      Set<Container> containers = Sets.newHashSet();
+      for (Container container : api.getRemoteApi().listContainers(ListContainerOptions.Builder.all(true))) {
+         // less efficient than just listNodes but returns richer json
+         containers.add(api.getRemoteApi().inspectContainer(container.getId()));
+      }
+      return containers;
+   }
+
+   @Override
+   public Iterable<Container> listNodesByIds(final Iterable<String> ids) {
+      Set<Container> containers = Sets.newHashSet();
+      for (String id : ids) {
+         containers.add(api.getRemoteApi().inspectContainer(id));
+      }
+      return containers;
+   }
+
+   @Override
+   public Iterable<Location> listLocations() {
+      return ImmutableSet.of();
+   }
+
+   @Override
+   public Container getNode(String id) {
+      return api.getRemoteApi().inspectContainer(id);
+   }
+
+   @Override
+   public void destroyNode(String id) {
+      api.getRemoteApi().removeContainer(id, RemoveContainerOptions.Builder.force(true));
+   }
+
+   @Override
+   public void rebootNode(String id) {
+      api.getRemoteApi().stopContainer(id);
+      api.getRemoteApi().startContainer(id);
+   }
+
+   @Override
+   public void resumeNode(String id) {
+      throw new UnsupportedOperationException("resume not supported");
+   }
+
+   @Override
+   public void suspendNode(String id) {
+      throw new UnsupportedOperationException("suspend not supported");
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9b124ee9/docker/src/main/java/org/jclouds/docker/config/DockerHttpApiModule.java
----------------------------------------------------------------------
diff --git a/docker/src/main/java/org/jclouds/docker/config/DockerHttpApiModule.java b/docker/src/main/java/org/jclouds/docker/config/DockerHttpApiModule.java
new file mode 100644
index 0000000..e6da554
--- /dev/null
+++ b/docker/src/main/java/org/jclouds/docker/config/DockerHttpApiModule.java
@@ -0,0 +1,40 @@
+/*
+ * 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.docker.config;
+
+import org.jclouds.docker.DockerApi;
+import org.jclouds.docker.handlers.DockerErrorHandler;
+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.rest.ConfiguresHttpApi;
+import org.jclouds.rest.config.HttpApiModule;
+
+/**
+ * Configures the Docker connection.
+ */
+@ConfiguresHttpApi
+public class DockerHttpApiModule extends HttpApiModule<DockerApi> {
+
+   @Override
+   protected void bindErrorHandlers() {
+      bind(HttpErrorHandler.class).annotatedWith(Redirection.class).to(DockerErrorHandler.class);
+      bind(HttpErrorHandler.class).annotatedWith(ClientError.class).to(DockerErrorHandler.class);
+      bind(HttpErrorHandler.class).annotatedWith(ServerError.class).to(DockerErrorHandler.class);
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9b124ee9/docker/src/main/java/org/jclouds/docker/config/DockerParserModule.java
----------------------------------------------------------------------
diff --git a/docker/src/main/java/org/jclouds/docker/config/DockerParserModule.java b/docker/src/main/java/org/jclouds/docker/config/DockerParserModule.java
new file mode 100644
index 0000000..533184f
--- /dev/null
+++ b/docker/src/main/java/org/jclouds/docker/config/DockerParserModule.java
@@ -0,0 +1,62 @@
+/*
+ * 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.docker.config;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonDeserializationContext;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParseException;
+import com.google.inject.AbstractModule;
+import com.google.inject.Provides;
+import org.jclouds.docker.domain.Container;
+import org.jclouds.json.config.GsonModule;
+
+import javax.inject.Singleton;
+import java.lang.reflect.Type;
+import java.util.Map;
+
+public class DockerParserModule extends AbstractModule {
+
+   @Override
+   protected void configure() {
+      bind(GsonModule.DateAdapter.class).to(GsonModule.Iso8601DateAdapter.class);
+   }
+
+   @Provides
+   @Singleton
+   public Map<Type, Object> provideCustomAdapterBindings() {
+      return new ImmutableMap.Builder<Type, Object>()
+              .put(Container.class, new ContainerTypeAdapter())
+              .build();
+   }
+
+   protected static class ContainerTypeAdapter implements JsonDeserializer<Container> {
+
+      @Override
+      public Container deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws
+              JsonParseException {
+         Gson gson = new GsonBuilder().serializeNulls().create();
+         final JsonObject jsonObject = json.getAsJsonObject();
+         return gson.fromJson(jsonObject, Container.class);
+      }
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9b124ee9/docker/src/main/java/org/jclouds/docker/config/DockerProperties.java
----------------------------------------------------------------------
diff --git a/docker/src/main/java/org/jclouds/docker/config/DockerProperties.java b/docker/src/main/java/org/jclouds/docker/config/DockerProperties.java
new file mode 100644
index 0000000..b870fea
--- /dev/null
+++ b/docker/src/main/java/org/jclouds/docker/config/DockerProperties.java
@@ -0,0 +1,26 @@
+/*
+ * 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.docker.config;
+
+public class DockerProperties {
+
+   /**
+    * default Docker host password
+    */
+   public static final String HOST_PASSWORD = "jclouds.docker.host.password";
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9b124ee9/docker/src/main/java/org/jclouds/docker/domain/Config.java
----------------------------------------------------------------------
diff --git a/docker/src/main/java/org/jclouds/docker/domain/Config.java b/docker/src/main/java/org/jclouds/docker/domain/Config.java
new file mode 100644
index 0000000..9c80472
--- /dev/null
+++ b/docker/src/main/java/org/jclouds/docker/domain/Config.java
@@ -0,0 +1,457 @@
+/*
+ * 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.docker.domain;
+
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.gson.annotations.SerializedName;
+import org.jclouds.javax.annotation.Nullable;
+
+import java.beans.ConstructorProperties;
+import java.util.List;
+import java.util.Map;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+public class Config {
+
+   @SerializedName("Hostname")
+   private final String hostname;
+   @SerializedName("Domainname")
+   private final String domainName;
+   @SerializedName("User")
+   private final String user;
+   @SerializedName("Memory")
+   private final int memory;
+   @SerializedName("MemorySwap")
+   private final int memorySwap;
+   @SerializedName("CpuShares")
+   private final int cpuShares;
+   @SerializedName("AttachStdin")
+   private final boolean attachStdin;
+   @SerializedName("AttachStdout")
+   private final boolean attachStdout;
+   @SerializedName("AttachStderr")
+   private final boolean attachStderr;
+   @SerializedName("ExposedPorts")
+   private final Map<String, ?> exposedPorts;
+   @SerializedName("Tty")
+   private final boolean tty;
+   @SerializedName("OpenStdin")
+   private final boolean openStdin;
+   @SerializedName("StdinOnce")
+   private final boolean stdinOnce;
+   @SerializedName("Env")
+   private final List<String> env;
+   @SerializedName("Cmd")
+   private final List<String> cmd;
+   @SerializedName("Dns")
+   private final List<String> dns;
+   @SerializedName("Image")
+   private final String imageId;
+   @SerializedName("Volumes")
+   private final Map<String, ?> volumes;
+   @SerializedName("VolumesFrom")
+   private final String volumesFrom;
+   @SerializedName("WorkingDir")
+   private final String workingDir;
+   @SerializedName("Entrypoint")
+   private final String entrypoint;
+   @SerializedName("NetworkDisabled")
+   private final boolean networkDisabled;
+   @SerializedName("OnBuild")
+   private final List<String> onBuild;
+
+
+   @ConstructorProperties({ "Hostname", "Domainname", "User", "Memory", "MemorySwap", "CpuShares", "AttachStdin",
+           "AttachStdout", "AttachStderr", "ExposedPorts", "Tty", "OpenStdin", "StdinOnce", "Env", "Cmd",
+           "Dns", "Image", "Volumes", "VolumesFrom", "WorkingDir", "Entrypoint", "NetworkDisabled", "OnBuild" })
+   protected Config(@Nullable String hostname, @Nullable String domainName, @Nullable String user,
+                             int memory, int memorySwap, int cpuShares, boolean attachStdin, boolean attachStdout,
+                             boolean attachStderr, Map<String, ?> exposedPorts, boolean tty, boolean openStdin,
+                             boolean stdinOnce, @Nullable List<String> env, @Nullable List<String> cmd,
+                             @Nullable List<String> dns, String imageId, @Nullable Map<String, ?> volumes,
+                             @Nullable String volumesFrom, @Nullable String workingDir, @Nullable String entrypoint,
+                             @Nullable boolean networkDisabled, @Nullable List<String> onBuild) {
+      this.hostname = hostname;
+      this.domainName = domainName;
+      this.user = user;
+      this.memory = checkNotNull(memory, "memory");
+      this.memorySwap = checkNotNull(memorySwap, "memorySwap");
+      this.cpuShares = checkNotNull(cpuShares, "cpuShares");
+      this.attachStdin = checkNotNull(attachStdin, "attachStdin");
+      this.attachStdout = checkNotNull(attachStdout, "attachStdout");
+      this.attachStderr = checkNotNull(attachStderr, "attachStderr");
+      this.exposedPorts = exposedPorts != null ? ImmutableMap.copyOf(exposedPorts) : ImmutableMap.<String, Object> of();
+      this.tty = checkNotNull(tty, "tty");
+      this.openStdin = checkNotNull(openStdin, "openStdin");
+      this.stdinOnce = checkNotNull(stdinOnce, "stdinOnce");
+      this.env = env != null ? ImmutableList.copyOf(env) : ImmutableList.<String> of();
+      this.cmd = cmd != null ? ImmutableList.copyOf(cmd) : ImmutableList.<String> of();
+      this.dns = dns != null ? ImmutableList.copyOf(dns) : ImmutableList.<String> of();
+      this.imageId = checkNotNull(imageId, "imageId");
+      this.volumes = volumes != null ? ImmutableMap.copyOf(volumes) : ImmutableMap.<String, Object> of();
+      this.volumesFrom = volumesFrom;
+      this.workingDir = workingDir;
+      this.entrypoint = entrypoint;
+      this.networkDisabled = networkDisabled;
+      this.onBuild = onBuild != null ? ImmutableList.copyOf(onBuild) : ImmutableList.<String> of();
+   }
+
+   public String getHostname() {
+      return hostname;
+   }
+
+   public String getDomainName() {
+      return domainName;
+   }
+
+   public String getUser() {
+      return user;
+   }
+
+   public int getMemory() {
+      return memory;
+   }
+
+   public int getMemorySwap() {
+      return memorySwap;
+   }
+
+   public int getCpuShares() {
+      return cpuShares;
+   }
+
+   public boolean isAttachStdin() {
+      return attachStdin;
+   }
+
+   public boolean isAttachStdout() {
+      return attachStdout;
+   }
+
+   public boolean isAttachStderr() {
+      return attachStderr;
+   }
+
+   public Map<String, ?> getExposedPorts() {
+      return exposedPorts;
+   }
+
+   public boolean isTty() {
+      return tty;
+   }
+
+   public boolean isOpenStdin() {
+      return openStdin;
+   }
+
+   public boolean isStdinOnce() {
+      return stdinOnce;
+   }
+
+   public List<String> getEnv() {
+      return env;
+   }
+
+   public List<String> getCmd() {
+      return cmd;
+   }
+
+   public List<String> getDns() {
+      return dns;
+   }
+
+   public String getImageId() {
+      return imageId;
+   }
+
+   public Map<String, ?> getVolumes() {
+      return volumes;
+   }
+
+   public String getVolumesFrom() {
+      return volumesFrom;
+   }
+
+   public String getWorkingDir() {
+      return workingDir;
+   }
+
+   public String getEntrypoint() {
+      return entrypoint;
+   }
+
+   public boolean isNetworkDisabled() {
+      return networkDisabled;
+   }
+
+   public List<String> getOnBuild() {
+      return onBuild;
+   }
+
+   @Override
+   public boolean equals(Object o) {
+      if (this == o) return true;
+      if (o == null || getClass() != o.getClass()) return false;
+
+      Config that = (Config) o;
+
+      return Objects.equal(this.hostname, that.hostname) &&
+              Objects.equal(this.domainName, that.domainName) &&
+              Objects.equal(this.user, that.user) &&
+              Objects.equal(this.memory, that.memory) &&
+              Objects.equal(this.memorySwap, that.memorySwap) &&
+              Objects.equal(this.cpuShares, that.cpuShares) &&
+              Objects.equal(this.attachStdin, that.attachStdin) &&
+              Objects.equal(this.attachStdout, that.attachStdout) &&
+              Objects.equal(this.attachStderr, that.attachStderr) &&
+              Objects.equal(this.exposedPorts, that.exposedPorts) &&
+              Objects.equal(this.tty, that.tty) &&
+              Objects.equal(this.openStdin, that.openStdin) &&
+              Objects.equal(this.stdinOnce, that.stdinOnce) &&
+              Objects.equal(this.env, that.env) &&
+              Objects.equal(this.cmd, that.cmd) &&
+              Objects.equal(this.dns, that.dns) &&
+              Objects.equal(this.imageId, that.imageId) &&
+              Objects.equal(this.volumes, that.volumes) &&
+              Objects.equal(this.volumesFrom, that.volumesFrom) &&
+              Objects.equal(this.workingDir, that.workingDir) &&
+              Objects.equal(this.entrypoint, that.entrypoint) &&
+              Objects.equal(this.onBuild, that.onBuild);
+   }
+
+   @Override
+   public int hashCode() {
+      return Objects.hashCode(hostname, domainName, user, memory, memorySwap, cpuShares, attachStdin, attachStdout,
+              attachStderr, exposedPorts, tty, openStdin, stdinOnce, env, cmd, dns, imageId, volumes,
+              volumesFrom, workingDir, entrypoint, networkDisabled, onBuild);
+   }
+
+   @Override
+   public String toString() {
+      return Objects.toStringHelper(this)
+              .add("hostname", hostname)
+              .add("domainName", domainName)
+              .add("user", user)
+              .add("memory", memory)
+              .add("memorySwap", memorySwap)
+              .add("cpuShares", cpuShares)
+              .add("attachStdin", attachStdin)
+              .add("attachStdout", attachStdout)
+              .add("attachStderr", attachStderr)
+              .add("exposedPorts", exposedPorts)
+              .add("tty", tty)
+              .add("openStdin", openStdin)
+              .add("stdinOnce", stdinOnce)
+              .add("env", env)
+              .add("cmd", cmd)
+              .add("dns", dns)
+              .add("imageId", imageId)
+              .add("volumes", volumes)
+              .add("volumesFrom", volumesFrom)
+              .add("workingDir", workingDir)
+              .add("entrypoint", entrypoint)
+              .add("networkDisabled", networkDisabled)
+              .add("onBuild", onBuild)
+              .toString();
+   }
+
+   public static Builder builder() {
+      return new Builder();
+   }
+
+   public Builder toBuilder() {
+      return builder().fromConfig(this);
+   }
+
+   public static final class Builder {
+      private String hostname;
+      private String domainName;
+      private String user;
+      private int memory;
+      private int memorySwap;
+      private int cpuShares;
+      private boolean attachStdin;
+      private boolean attachStdout;
+      private boolean attachStderr;
+      private Map<String, ?> exposedPorts = ImmutableMap.of();
+      private boolean tty;
+      private boolean openStdin;
+      private boolean stdinOnce;
+      private List<String> env = ImmutableList.of();
+      private List<String> cmd = ImmutableList.of();
+      private List<String> dns = ImmutableList.of();
+      private String imageId;
+      private Map<String, ?> volumes = ImmutableMap.of();
+      private String volumesFrom;
+      private String workingDir;
+      private String entrypoint;
+      private boolean networkDisabled;
+      private List<String> onBuild = ImmutableList.of();
+
+      public Builder hostname(String hostname) {
+         this.hostname = hostname;
+         return this;
+      }
+
+      public Builder domainName(String domainName) {
+         this.domainName = domainName;
+         return this;
+      }
+
+      public Builder user(String user) {
+         this.user = user;
+         return this;
+      }
+
+      public Builder memory(int memory) {
+         this.memory = memory;
+         return this;
+      }
+
+      public Builder memorySwap(int memorySwap) {
+         this.memorySwap = memorySwap;
+         return this;
+      }
+
+      public Builder cpuShares(int cpuShares) {
+         this.cpuShares = cpuShares;
+         return this;
+      }
+
+      public Builder attachStdin(boolean attachStdin) {
+         this.attachStdin = attachStdin;
+         return this;
+      }
+
+      public Builder attachStdout(boolean attachStdout) {
+         this.attachStdout = attachStdout;
+         return this;
+      }
+
+      public Builder attachStderr(boolean attachStderr) {
+         this.attachStderr = attachStderr;
+         return this;
+      }
+
+      public Builder exposedPorts(Map<String, ?> exposedPorts) {
+         this.exposedPorts = ImmutableMap.copyOf(checkNotNull(exposedPorts, "exposedPorts"));
+         return this;
+      }
+
+      public Builder tty(boolean tty) {
+         this.tty = tty;
+         return this;
+      }
+
+      public Builder openStdin(boolean openStdin) {
+         this.openStdin = openStdin;
+         return this;
+      }
+
+      public Builder stdinOnce(boolean stdinOnce) {
+         this.stdinOnce = stdinOnce;
+         return this;
+      }
+
+      public Builder env(List<String> env) {
+         this.env = env;
+         return this;
+      }
+
+      public Builder cmd(List<String> cmd) {
+         this.cmd = ImmutableList.copyOf(checkNotNull(cmd, "cmd"));
+         return this;
+      }
+
+      public Builder dns(List<String> dns) {
+         this.dns = ImmutableList.copyOf(checkNotNull(dns, "dns"));
+         return this;
+      }
+
+      public Builder imageId(String imageId) {
+         this.imageId = imageId;
+         return this;
+      }
+
+      public Builder volumes(Map<String, ?> volumes) {
+         this.volumes = ImmutableMap.copyOf(checkNotNull(volumes, "volumes"));
+         return this;
+      }
+
+      public Builder volumesFrom(String volumesFrom) {
+         this.volumesFrom = volumesFrom;
+         return this;
+      }
+
+      public Builder workingDir(String workingDir) {
+         this.workingDir = workingDir;
+         return this;
+      }
+
+      public Builder entrypoint(String entrypoint) {
+         this.entrypoint = entrypoint;
+         return this;
+      }
+
+      public Builder networkDisabled(boolean networkDisabled) {
+         this.networkDisabled = networkDisabled;
+         return this;
+      }
+
+      public Builder onBuild(List<String> onBuild) {
+         this.onBuild = ImmutableList.copyOf(checkNotNull(onBuild, "onBuild"));
+         return this;
+      }
+
+      public Config build() {
+         return new Config(hostname, domainName, user, memory, memorySwap, cpuShares, attachStdin, attachStdout,
+                 attachStderr, exposedPorts, tty, openStdin, stdinOnce, env, cmd, dns, imageId, volumes,
+                 volumesFrom, workingDir, entrypoint, networkDisabled, onBuild);
+      }
+
+      public Builder fromConfig(Config in) {
+         return this
+                 .hostname(in.getHostname())
+                 .domainName(in.getDomainName())
+                 .user(in.getUser())
+                 .memory(in.getMemory())
+                 .memorySwap(in.getMemorySwap())
+                 .cpuShares(in.getCpuShares())
+                 .attachStdin(in.isAttachStdin())
+                 .attachStdout(in.isAttachStdout())
+                 .attachStderr(in.isAttachStderr())
+                 .exposedPorts(in.getExposedPorts())
+                 .tty(in.isTty())
+                 .openStdin(in.isOpenStdin())
+                 .stdinOnce(in.isStdinOnce())
+                 .env(in.getEnv())
+                 .cmd(in.getCmd())
+                 .dns(in.getDns())
+                 .imageId(in.getImageId())
+                 .volumes(in.getVolumes())
+                 .volumesFrom(in.getVolumesFrom())
+                 .workingDir(in.getWorkingDir())
+                 .entrypoint(in.getEntrypoint())
+                 .networkDisabled(in.isNetworkDisabled())
+                 .onBuild(in.getOnBuild());
+      }
+
+   }
+}


[2/3] [JCLOUDS-500] Initial commit for docker

Posted by na...@apache.org.
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9b124ee9/docker/src/main/java/org/jclouds/docker/domain/Container.java
----------------------------------------------------------------------
diff --git a/docker/src/main/java/org/jclouds/docker/domain/Container.java b/docker/src/main/java/org/jclouds/docker/domain/Container.java
new file mode 100644
index 0000000..2752529
--- /dev/null
+++ b/docker/src/main/java/org/jclouds/docker/domain/Container.java
@@ -0,0 +1,389 @@
+/*
+ * 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.docker.domain;
+
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.gson.annotations.SerializedName;
+import org.jclouds.javax.annotation.Nullable;
+
+import java.beans.ConstructorProperties;
+import java.util.List;
+import java.util.Map;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+public class Container {
+
+   @SerializedName("Id")
+   private final String id;
+   @SerializedName("Name")
+   private final String name;
+   @SerializedName("Created")
+   private final String created;
+   @SerializedName("Path")
+   private final String path;
+   @SerializedName("Args")
+   private final String[] args;
+   @SerializedName("Config")
+   private final Config containerConfig;
+   @SerializedName("State")
+   private final State state;
+   @SerializedName("Image")
+   private final String image;
+   @SerializedName("NetworkSettings")
+   private final NetworkSettings networkSettings;
+   @SerializedName("ResolvConfPath")
+   private final String resolvConfPath;
+   @SerializedName("Driver")
+   private final String driver;
+   @SerializedName("ExecDriver")
+   private final String execDriver;
+   @SerializedName("Volumes")
+   private final Map<String, String> volumes;
+   @SerializedName("VolumesRW")
+   private final Map<String, Boolean> volumesRW;
+   @SerializedName("Command")
+   private final String command;
+   @SerializedName("Status")
+   private final String status;
+   @SerializedName("HostConfig")
+   private final HostConfig hostConfig;
+   @SerializedName("Ports")
+   private final List<Port> ports;
+   @SerializedName("HostnamePath")
+   private final String hostnamePath;
+
+   @ConstructorProperties({ "Id", "Name", "Created", "Path", "Args", "Config", "State", "Image", "NetworkSettings",
+           "ResolvConfPath", "Driver", "ExecDriver", "Volumes", "VolumesRW", "Command", "Status", "HostConfig",
+           "Ports", "HostnamePath" })
+   protected Container(String id, @Nullable String name, @Nullable String created, @Nullable String path, @Nullable String[] args,
+                       @Nullable Config containerConfig, @Nullable State state, @Nullable String image, @Nullable NetworkSettings networkSettings,
+                       @Nullable String resolvConfPath, @Nullable String driver, @Nullable String execDriver, @Nullable Map<String, String> volumes,
+                       @Nullable Map<String, Boolean> volumesRW, @Nullable String command, @Nullable String status,
+                       @Nullable HostConfig hostConfig, @Nullable List<Port> ports, @Nullable String hostnamePath) {
+      this.id = checkNotNull(id, "id");
+      this.name = name;
+      this.created = created;
+      this.path = path;
+      this.args = args;
+      this.containerConfig = containerConfig;
+      this.state = state;
+      this.image = image;
+      this.networkSettings = networkSettings;
+      this.resolvConfPath = resolvConfPath;
+      this.driver = driver;
+      this.execDriver = execDriver;
+      this.volumes = volumes != null ? ImmutableMap.copyOf(volumes) : ImmutableMap.<String, String>of();
+      this.volumesRW = volumesRW != null ? ImmutableMap.copyOf(volumesRW) : ImmutableMap.<String, Boolean>of();
+      this.command = command;
+      this.status = status;
+      this.hostConfig = hostConfig;
+      this.ports = ports != null ? ImmutableList.copyOf(ports) : ImmutableList.<Port>of();
+      this.hostnamePath = hostnamePath;
+   }
+
+   public String getId() {
+      return id;
+   }
+
+   public String getName() {
+      return name;
+   }
+
+   public String getCreated() {
+      return created;
+   }
+
+   public String getPath() {
+      return path;
+   }
+
+   public String[] getArgs() {
+      return args;
+   }
+
+   public Config getContainerConfig() {
+      return containerConfig;
+   }
+
+   public State getState() {
+      return state;
+   }
+
+   public String getImage() {
+      return image;
+   }
+
+   public NetworkSettings getNetworkSettings() {
+      return networkSettings;
+   }
+
+   public String getResolvConfPath() {
+      return resolvConfPath;
+   }
+
+   public String getDriver() {
+      return driver;
+   }
+
+   public String getExecDriver() {
+      return execDriver;
+   }
+
+   public Map<String, String> getVolumes() {
+      return volumes;
+   }
+
+   public Map<String, Boolean> getvolumesRW() {
+      return volumesRW;
+   }
+
+   public String getCommand() {
+      return command;
+   }
+
+   public String getStatus() {
+      return status;
+   }
+
+   public HostConfig getHostConfig() {
+      return hostConfig;
+   }
+
+   public List<Port> getPorts() {
+      return ports;
+   }
+
+   public String getHostnamePath() {
+      return hostnamePath;
+   }
+
+   @Override
+   public boolean equals(Object o) {
+      if (this == o) return true;
+      if (o == null || getClass() != o.getClass()) return false;
+
+      Container that = (Container) o;
+
+      return Objects.equal(this.id, that.id) &&
+              Objects.equal(this.name, that.name) &&
+              Objects.equal(this.created, that.created) &&
+              Objects.equal(this.path, that.path) &&
+              Objects.equal(this.args, that.args) &&
+              Objects.equal(this.containerConfig, that.containerConfig) &&
+              Objects.equal(this.state, that.state) &&
+              Objects.equal(this.image, that.image) &&
+              Objects.equal(this.networkSettings, that.networkSettings) &&
+              Objects.equal(this.resolvConfPath, that.resolvConfPath) &&
+              Objects.equal(this.driver, that.driver) &&
+              Objects.equal(this.execDriver, that.execDriver) &&
+              Objects.equal(this.volumes, that.volumes) &&
+              Objects.equal(this.volumesRW, that.volumesRW) &&
+              Objects.equal(this.command, that.command) &&
+              Objects.equal(this.status, that.status) &&
+              Objects.equal(this.hostConfig, that.hostConfig) &&
+              Objects.equal(this.ports, that.ports) &&
+              Objects.equal(this.hostnamePath, that.hostnamePath);
+   }
+
+   @Override
+   public int hashCode() {
+      return Objects.hashCode(id, name, created, path, args, containerConfig, state, image, networkSettings, resolvConfPath,
+              driver, execDriver, volumes, volumesRW, command, status, hostConfig, ports, hostnamePath);
+   }
+
+   @Override
+   public String toString() {
+      return Objects.toStringHelper(this)
+              .add("id", id)
+              .add("name", name)
+              .add("created", created)
+              .add("path", path)
+              .add("args", args)
+              .add("containerConfig", containerConfig)
+              .add("state", state)
+              .add("image", image)
+              .add("networkSettings", networkSettings)
+              .add("resolvConfPath", resolvConfPath)
+              .add("driver", driver)
+              .add("execDriver", execDriver)
+              .add("volumes", volumes)
+              .add("volumesRW", volumesRW)
+              .add("command", command)
+              .add("status", status)
+              .add("hostConfig", hostConfig)
+              .add("ports", ports)
+              .add("hostnamePath", hostnamePath)
+              .toString();
+   }
+
+   public static Builder builder() {
+      return new Builder();
+   }
+
+   public Builder toBuilder() {
+      return builder().fromContainer(this);
+   }
+
+   public static final class Builder {
+
+      private String id;
+      private String name;
+      private String created;
+      private String path;
+      private String[] args;
+      private Config containerConfig;
+      private State state;
+      private String image;
+      private NetworkSettings networkSettings;
+      private String resolvConfPath;
+      private String driver;
+      private String execDriver;
+      private Map<String, String> volumes = ImmutableMap.of();
+      private Map<String, Boolean> volumesRW = ImmutableMap.of();
+      private String command;
+      private String status;
+      private HostConfig hostConfig;
+      private List<Port> ports = ImmutableList.of();
+      private String hostnamePath;
+
+      public Builder id(String id) {
+         this.id = id;
+         return this;
+      }
+
+      public Builder name(String name) {
+         this.name = name;
+         return this;
+      }
+
+      public Builder created(String created) {
+         this.created = created;
+         return this;
+      }
+
+      public Builder path(String path) {
+         this.path = path;
+         return this;
+      }
+
+      public Builder args(String[] args) {
+         this.args = args;
+         return this;
+      }
+
+      public Builder containerConfig(Config containerConfig) {
+         this.containerConfig = containerConfig;
+         return this;
+      }
+
+      public Builder state(State state) {
+         this.state = state;
+         return this;
+      }
+
+      public Builder image(String imageName) {
+         this.image = imageName;
+         return this;
+      }
+
+      public Builder networkSettings(NetworkSettings networkSettings) {
+         this.networkSettings = networkSettings;
+         return this;
+      }
+
+      public Builder resolvConfPath(String resolvConfPath) {
+         this.resolvConfPath = resolvConfPath;
+         return this;
+      }
+
+      public Builder driver(String driver) {
+         this.driver = driver;
+         return this;
+      }
+
+      public Builder execDriver(String execDriver) {
+         this.execDriver = execDriver;
+         return this;
+      }
+
+      public Builder volumes(Map<String, String> volumes) {
+         this.volumes = volumes;
+         return this;
+      }
+
+      public Builder volumesRW(Map<String, Boolean> volumesRW) {
+         this.volumesRW = volumesRW;
+         return this;
+      }
+
+      public Builder command(String command) {
+         this.command = command;
+         return this;
+      }
+
+      public Builder status(String status) {
+         this.status = status;
+         return this;
+      }
+
+      public Builder hostConfig(HostConfig hostConfig) {
+         this.hostConfig = hostConfig;
+         return this;
+      }
+
+      public Builder ports(List<Port> ports) {
+         this.ports = ports;
+         return this;
+      }
+
+      public Builder hostnamePath(String hostnamePath) {
+         this.hostnamePath = hostnamePath;
+         return this;
+      }
+
+      public Container build() {
+         return new Container(id, name, created, path, args, containerConfig, state, image, networkSettings, resolvConfPath,
+                 driver, execDriver, volumes, volumesRW, command, status, hostConfig, ports, hostnamePath);
+      }
+
+      public Builder fromContainer(Container in) {
+         return this
+                 .id(in.getId())
+                 .name(in.getName())
+                 .created(in.getCreated())
+                 .path(in.getPath())
+                 .args(in.getArgs())
+                 .containerConfig(in.getContainerConfig())
+                 .state(in.getState())
+                 .image(in.getImage())
+                 .networkSettings(in.getNetworkSettings())
+                 .resolvConfPath(in.getResolvConfPath())
+                 .driver(in.getDriver())
+                 .execDriver(in.getExecDriver())
+                 .volumes(in.getVolumes())
+                 .volumesRW(in.getvolumesRW())
+                 .command(in.getCommand())
+                 .status(in.getStatus())
+                 .hostConfig(in.getHostConfig())
+                 .ports(in.getPorts())
+                 .hostnamePath(in.getHostnamePath());
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9b124ee9/docker/src/main/java/org/jclouds/docker/domain/ExposedPorts.java
----------------------------------------------------------------------
diff --git a/docker/src/main/java/org/jclouds/docker/domain/ExposedPorts.java b/docker/src/main/java/org/jclouds/docker/domain/ExposedPorts.java
new file mode 100644
index 0000000..b7a831a
--- /dev/null
+++ b/docker/src/main/java/org/jclouds/docker/domain/ExposedPorts.java
@@ -0,0 +1,106 @@
+/*
+ * 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.docker.domain;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import java.beans.ConstructorProperties;
+import java.util.Set;
+
+import org.jclouds.javax.annotation.Nullable;
+
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableSet;
+import com.google.gson.annotations.SerializedName;
+
+public class ExposedPorts {
+
+   @SerializedName("PortAndProtocol")
+   private final String portAndProtocol;
+   @SerializedName("HostPorts")
+   private final Set<String> hostPorts;
+
+   @ConstructorProperties({ "PortAndProtocol", "HostPorts" })
+   protected ExposedPorts(String portAndProtocol, @Nullable Set<String> hostPorts) {
+      this.portAndProtocol = checkNotNull(portAndProtocol, "portAndProtocol");
+      this.hostPorts = hostPorts != null ? ImmutableSet.copyOf(hostPorts) : ImmutableSet.<String> of();
+   }
+
+   public String getPortAndProtocol() {
+      return portAndProtocol;
+   }
+
+   public Set<String> getHostPorts() {
+      return hostPorts;
+   }
+
+   @Override
+   public boolean equals(Object o) {
+      if (this == o) return true;
+      if (o == null || getClass() != o.getClass()) return false;
+
+      ExposedPorts that = (ExposedPorts) o;
+
+      return Objects.equal(this.portAndProtocol, that.portAndProtocol) &&
+             Objects.equal(this.hostPorts, that.hostPorts);
+   }
+
+   @Override
+   public int hashCode() {
+      return Objects.hashCode(portAndProtocol, hostPorts);
+   }
+
+   @Override
+   public String toString() {
+      return Objects.toStringHelper(this)
+              .add("portAndProtocol", portAndProtocol)
+              .add("hostPorts", hostPorts)
+              .toString();
+   }
+
+   public static Builder builder() {
+      return new Builder();
+   }
+
+   public Builder toBuilder() {
+      return builder().fromExposedPorts(this);
+   }
+
+   public static final class Builder {
+
+      private String portAndProtocol;
+      private Set<String> hostPorts = ImmutableSet.of();
+
+      public Builder portAndProtocol(String portAndProtocol) {
+         this.portAndProtocol = portAndProtocol;
+         return this;
+      }
+
+      public Builder hostPorts(Set<String> hostPorts) {
+         this.hostPorts = ImmutableSet.copyOf(checkNotNull(hostPorts, "hostPorts"));
+         return this;
+      }
+
+      public ExposedPorts build() {
+         return new ExposedPorts(portAndProtocol, hostPorts);
+      }
+
+      public Builder fromExposedPorts(ExposedPorts in) {
+         return this.portAndProtocol(in.getPortAndProtocol())
+                 .hostPorts(in.getHostPorts());
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9b124ee9/docker/src/main/java/org/jclouds/docker/domain/HostConfig.java
----------------------------------------------------------------------
diff --git a/docker/src/main/java/org/jclouds/docker/domain/HostConfig.java b/docker/src/main/java/org/jclouds/docker/domain/HostConfig.java
new file mode 100644
index 0000000..275961f
--- /dev/null
+++ b/docker/src/main/java/org/jclouds/docker/domain/HostConfig.java
@@ -0,0 +1,242 @@
+/*
+ * 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.docker.domain;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import java.beans.ConstructorProperties;
+import java.util.List;
+import java.util.Map;
+
+import org.jclouds.javax.annotation.Nullable;
+
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.gson.annotations.SerializedName;
+
+public class HostConfig {
+
+   @SerializedName("ContainerIDFile")
+   private final String containerIDFile;
+   @SerializedName("Binds")
+   private final List<String> binds;
+   @SerializedName("LxcConf")
+   private final Map<String, String> lxcConf;
+   @SerializedName("Privileged")
+   private final boolean privileged;
+   @SerializedName("Dns")
+   private final String dns;
+   @SerializedName("DnsSearch")
+   private final String dnsSearch;
+   @SerializedName("PortBindings")
+   private final Map<String, List<Map<String, String>>> portBindings;
+   @SerializedName("Links")
+   private final List<String> links;
+   @SerializedName("PublishAllPorts")
+   private final boolean publishAllPorts;
+   @SerializedName("VolumesFrom")
+   private final List<String> volumesFrom;
+
+   @ConstructorProperties({ "ContainerIDFile", "Binds", "LxcConf", "Privileged", "Dns", "DnsSearch", "PortBindings",
+           "Links", "PublishAllPorts", "VolumesFrom" })
+   protected HostConfig(@Nullable String containerIDFile, @Nullable List<String> binds,
+                        Map<String, String> lxcConf, boolean privileged, @Nullable String dns,
+                        @Nullable String dnsSearch, @Nullable Map<String, List<Map<String, String>>> portBindings,
+                        @Nullable List<String> links, boolean publishAllPorts, @Nullable List<String> volumesFrom) {
+      this.containerIDFile = containerIDFile;
+      this.binds = binds != null ? ImmutableList.copyOf(binds) : ImmutableList.<String> of();
+      this.lxcConf = lxcConf != null ? ImmutableMap.copyOf(lxcConf) : ImmutableMap.<String, String> of();
+      this.privileged = checkNotNull(privileged, "privileged");
+      this.dns = dns;
+      this.dnsSearch = dnsSearch;
+      this.portBindings = portBindings != null ? ImmutableMap.copyOf(portBindings) : ImmutableMap.<String, List<Map<String, String>>> of();
+      this.links = links != null ? ImmutableList.copyOf(links) : ImmutableList.<String> of();
+      this.publishAllPorts = checkNotNull(publishAllPorts, "publishAllPorts");
+      this.volumesFrom = volumesFrom != null ? ImmutableList.copyOf(volumesFrom) : ImmutableList.<String> of();
+   }
+
+   public String getContainerIDFile() {
+      return containerIDFile;
+   }
+
+   public List<String> getBinds() {
+      return binds;
+   }
+
+   public Map<String, String> getLxcConf() {
+      return lxcConf;
+   }
+
+   public boolean isPrivileged() {
+      return privileged;
+   }
+
+   public String getDns() { return dns; }
+
+   public String getDnsSearch() { return dnsSearch; }
+
+   public Map<String, List<Map<String, String>>> getPortBindings() {
+      return portBindings;
+   }
+
+   @Nullable
+   public List<String> getLinks() {
+      return links;
+   }
+
+   public boolean isPublishAllPorts() {
+      return publishAllPorts;
+   }
+
+   public List<String> getVolumesFrom() {
+      return volumesFrom;
+   }
+
+   @Override
+   public boolean equals(Object o) {
+      if (this == o) return true;
+      if (o == null || getClass() != o.getClass()) return false;
+
+      HostConfig that = (HostConfig) o;
+
+      return Objects.equal(this.containerIDFile, that.containerIDFile) &&
+              Objects.equal(this.binds, that.binds) &&
+              Objects.equal(this.lxcConf, that.lxcConf) &&
+              Objects.equal(this.privileged, that.privileged) &&
+              Objects.equal(this.dns, that.dns) &&
+              Objects.equal(this.dnsSearch, that.dnsSearch) &&
+              Objects.equal(this.portBindings, that.portBindings) &&
+              Objects.equal(this.links, that.links) &&
+              Objects.equal(this.publishAllPorts, that.publishAllPorts) &&
+              Objects.equal(this.volumesFrom, that.volumesFrom);
+   }
+
+   @Override
+   public int hashCode() {
+      return Objects.hashCode(containerIDFile, binds, lxcConf, privileged, dns, dnsSearch, portBindings, links,
+              publishAllPorts, volumesFrom);
+   }
+
+   @Override
+   public String toString() {
+      return Objects.toStringHelper(this)
+              .add("containerIDFile", containerIDFile)
+              .add("binds", binds)
+              .add("lxcConf", lxcConf)
+              .add("privileged", privileged)
+              .add("dns", dns)
+              .add("dnsSearch", dnsSearch)
+              .add("portBindings", portBindings)
+              .add("links", links)
+              .add("publishAllPorts", publishAllPorts)
+              .add("volumesFrom", volumesFrom)
+              .toString();
+   }
+
+   public static Builder builder() {
+      return new Builder();
+   }
+
+   public Builder toBuilder() {
+      return builder().fromHostConfig(this);
+   }
+
+   public static final class Builder {
+
+      private String containerIDFile;
+      private List<String> binds = Lists.newArrayList();
+      private Map<String, String> lxcConf = Maps.newLinkedHashMap();
+      private boolean privileged;
+      private String dns;
+      private String dnsSearch;
+      private Map<String, List<Map<String, String>>> portBindings = Maps.newLinkedHashMap();
+      private List<String> links = Lists.newArrayList();
+      private boolean publishAllPorts;
+      private List<String> volumesFrom = Lists.newArrayList();
+
+      public Builder containerIDFile(String containerIDFile) {
+         this.containerIDFile = containerIDFile;
+         return this;
+      }
+
+      public Builder binds(List<String> binds) {
+         this.binds.addAll(checkNotNull(binds, "binds"));
+         return this;
+      }
+
+      public Builder lxcConf(Map<String, String> lxcConf) {
+         this.lxcConf.putAll(checkNotNull(lxcConf, "lxcConf"));
+         return this;
+      }
+
+      public Builder privileged(boolean privileged) {
+         this.privileged = privileged;
+         return this;
+      }
+
+      public Builder dns(String dns) {
+         this.dns = dns;
+         return this;
+      }
+
+      public Builder dnsSearch(String dnsSearch) {
+         this.dnsSearch = dnsSearch;
+         return this;
+      }
+
+      public Builder links(List<String> links) {
+         this.links.addAll(checkNotNull(links, "links"));
+         return this;
+      }
+
+      public Builder portBindings(Map<String, List<Map<String, String>>> portBindings) {
+         this.portBindings.putAll(portBindings);
+         return this;
+      }
+
+      public Builder publishAllPorts(boolean publishAllPorts) {
+         this.publishAllPorts = publishAllPorts;
+         return this;
+      }
+
+      public Builder volumesFrom(List<String> volumesFrom) {
+         this.volumesFrom.addAll(checkNotNull(volumesFrom, "volumesFrom"));
+         return this;
+      }
+
+      public HostConfig build() {
+         return new HostConfig(containerIDFile, binds, lxcConf, privileged, dns, dnsSearch, portBindings, links,
+                 publishAllPorts, volumesFrom);
+      }
+
+      public Builder fromHostConfig(HostConfig in) {
+         return this
+                 .containerIDFile(in.getContainerIDFile())
+                 .binds(in.getBinds())
+                 .lxcConf(in.getLxcConf())
+                 .privileged(in.isPrivileged())
+                 .dns(in.getDns())
+                 .dnsSearch(in.getDnsSearch())
+                 .links(in.getLinks())
+                 .portBindings(in.getPortBindings())
+                 .publishAllPorts(in.isPublishAllPorts())
+                 .volumesFrom(in.getVolumesFrom());
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9b124ee9/docker/src/main/java/org/jclouds/docker/domain/Image.java
----------------------------------------------------------------------
diff --git a/docker/src/main/java/org/jclouds/docker/domain/Image.java b/docker/src/main/java/org/jclouds/docker/domain/Image.java
new file mode 100644
index 0000000..186ece3
--- /dev/null
+++ b/docker/src/main/java/org/jclouds/docker/domain/Image.java
@@ -0,0 +1,239 @@
+/*
+ * 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.docker.domain;
+
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableList;
+import com.google.gson.annotations.SerializedName;
+import org.jclouds.javax.annotation.Nullable;
+
+import java.beans.ConstructorProperties;
+import java.util.List;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+public class Image {
+
+   @SerializedName("Id")
+   private final String id;
+   @SerializedName("Parent")
+   private final String parent;
+   @SerializedName("Created")
+   private final String created;
+   @SerializedName("Container")
+   private final String container;
+   @SerializedName("DockerVersion")
+   private final String dockerVersion;
+   @SerializedName("Architecture")
+   private final String architecture;
+   @SerializedName("Os")
+   private final String os;
+   @SerializedName("Size")
+   private final long size;
+   @SerializedName("VirtualSize")
+   private final long virtualSize;
+   @SerializedName("RepoTags")
+   private final List<String> repoTags;
+
+   @ConstructorProperties({ "Id", "Parent", "Created", "Container", "DockerVersion", "Architecture", "Os", "Size",
+           "VirtualSize", "RepoTags", "Architecture" })
+   protected Image(String id, @Nullable String parent, @Nullable String created, @Nullable String container,
+                @Nullable String dockerVersion, @Nullable String architecture, @Nullable String os, long size,
+                @Nullable long virtualSize, @Nullable List<String> repoTags) {
+      this.id = checkNotNull(id, "id");
+      this.parent = parent;
+      this.created = created;
+      this.container = container;
+      this.dockerVersion = dockerVersion;
+      this.architecture = architecture;
+      this.os = os;
+      this.size = size;
+      this.virtualSize = virtualSize;
+      this.repoTags = repoTags != null ? ImmutableList.copyOf(repoTags) : ImmutableList.<String> of();
+   }
+
+   public String getId() {
+      return id;
+   }
+
+   public String getParent() {
+      return parent;
+   }
+
+   public String getCreated() {
+      return created;
+   }
+
+   public String getContainer() {
+      return container;
+   }
+
+   public String getDockerVersion() {
+      return dockerVersion;
+   }
+
+   public String getArchitecture() {
+      return architecture;
+   }
+
+   public String getOs() {
+      return os;
+   }
+
+   public long getSize() {
+      return size;
+   }
+
+   public long getVirtualSize() {
+      return virtualSize;
+   }
+
+   public List<String> getRepoTags() {
+      return repoTags;
+   }
+
+   @Override
+   public boolean equals(Object o) {
+      if (this == o) return true;
+      if (o == null || getClass() != o.getClass()) return false;
+
+      Image that = (Image) o;
+
+      return Objects.equal(this.id, that.id) &&
+              Objects.equal(this.parent, that.parent) &&
+              Objects.equal(this.created, that.created) &&
+              Objects.equal(this.container, that.container) &&
+              Objects.equal(this.dockerVersion, that.dockerVersion) &&
+              Objects.equal(this.architecture, that.architecture) &&
+              Objects.equal(this.os, that.os) &&
+              Objects.equal(this.size, that.size) &&
+              Objects.equal(this.virtualSize, that.virtualSize);
+   }
+
+   @Override
+   public int hashCode() {
+      return Objects.hashCode(id, parent, created, container, dockerVersion, architecture, os, size,
+              virtualSize);
+   }
+
+   @Override
+   public String toString() {
+      return Objects.toStringHelper(this)
+              .add("id", id)
+              .add("parent", parent)
+              .add("created", created)
+              .add("container", container)
+              .add("dockerVersion", dockerVersion)
+              .add("architecture", architecture)
+              .add("os", os)
+              .add("size", size)
+              .add("virtualSize", virtualSize)
+              .add("repoTags", repoTags)
+              .toString();
+   }
+
+   public static Builder builder() {
+      return new Builder();
+   }
+
+   public Builder toBuilder() {
+      return builder().fromImage(this);
+   }
+
+   public static final class Builder {
+
+      private String id;
+      private String parent;
+      private String created;
+      private String container;
+      private String dockerVersion;
+      private String architecture;
+      private String os;
+      private long size;
+      private long virtualSize;
+      private List<String> repoTags = ImmutableList.of();
+
+      public Builder id(String id) {
+         this.id = id;
+         return this;
+      }
+
+      public Builder parent(String parent) {
+         this.parent = parent;
+         return this;
+      }
+
+      public Builder created(String created) {
+         this.created = created;
+         return this;
+      }
+
+      public Builder container(String container) {
+         this.container = container;
+         return this;
+      }
+
+      public Builder dockerVersion(String dockerVersion) {
+         this.dockerVersion = dockerVersion;
+         return this;
+      }
+
+      public Builder architecture(String architecture) {
+         this.architecture = architecture;
+         return this;
+      }
+
+      public Builder os(String os) {
+         this.os = os;
+         return this;
+      }
+
+      public Builder size(long size) {
+         this.size = size;
+         return this;
+      }
+
+      public Builder virtualSize(long virtualSize) {
+         this.virtualSize = virtualSize;
+         return this;
+      }
+
+      public Builder repoTags(List<String> repoTags) {
+         this.repoTags = ImmutableList.copyOf(checkNotNull(repoTags, "repoTags"));
+         return this;
+      }
+
+      public Image build() {
+         return new Image(id, parent, created, container, dockerVersion, architecture, os, size,
+                 virtualSize, repoTags);
+      }
+
+      public Builder fromImage(Image in) {
+         return this
+                 .id(in.getId())
+                 .parent(in.getParent())
+                 .created(in.getCreated())
+                 .container(in.getContainer())
+                 .dockerVersion(in.getDockerVersion())
+                 .architecture(in.getArchitecture())
+                 .os(in.getOs())
+                 .size(in.getSize())
+                 .virtualSize(in.getVirtualSize());
+                 //DO NOT add .repoTags(in.getRepoTags());
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9b124ee9/docker/src/main/java/org/jclouds/docker/domain/NetworkSettings.java
----------------------------------------------------------------------
diff --git a/docker/src/main/java/org/jclouds/docker/domain/NetworkSettings.java b/docker/src/main/java/org/jclouds/docker/domain/NetworkSettings.java
new file mode 100644
index 0000000..23f3fbe
--- /dev/null
+++ b/docker/src/main/java/org/jclouds/docker/domain/NetworkSettings.java
@@ -0,0 +1,175 @@
+/*
+ * 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.docker.domain;
+
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableMap;
+import com.google.gson.annotations.SerializedName;
+import org.jclouds.javax.annotation.Nullable;
+
+import java.beans.ConstructorProperties;
+import java.util.List;
+import java.util.Map;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+public class NetworkSettings {
+
+   @SerializedName("IPAddress")
+   private final String ipAddress;
+   @SerializedName("IPPrefixLen")
+   private final int ipPrefixLen;
+   @SerializedName("Gateway")
+   private final String gateway;
+   @SerializedName("Bridge")
+   private final String bridge;
+   @SerializedName("PortMapping")
+   private final String portMapping;
+   @SerializedName("Ports")
+   private final Map<String, List<Map<String, String>>> ports;
+
+   @ConstructorProperties({ "IPAddress", "IPPrefixLen", "Gateway", "Bridge", "PortMapping", "Ports" })
+   protected NetworkSettings(String ipAddress, int ipPrefixLen, String gateway, String bridge,
+                          @Nullable String portMapping, @Nullable Map<String, List<Map<String, String>>> ports) {
+      this.ipAddress = checkNotNull(ipAddress, "ipAddress");
+      this.ipPrefixLen = checkNotNull(ipPrefixLen, "ipPrefixLen");
+      this.gateway = checkNotNull(gateway, "gateway");
+      this.bridge = checkNotNull(bridge, "bridge");
+      this.portMapping = portMapping;
+      this.ports = ports != null ? ImmutableMap.copyOf(ports) : ImmutableMap.<String, List<Map<String, String>>> of();
+   }
+
+   public String getIpAddress() {
+      return ipAddress;
+   }
+
+   public int getIpPrefixLen() {
+      return ipPrefixLen;
+   }
+
+   public String getGateway() {
+      return gateway;
+   }
+
+   public String getBridge() {
+      return bridge;
+   }
+
+   public String getPortMapping() {
+      return portMapping;
+   }
+
+   public Map<String, List<Map<String, String>>> getPorts() {
+      return ports;
+   }
+
+   @Override
+   public boolean equals(Object o) {
+      if (this == o) return true;
+      if (o == null || getClass() != o.getClass()) return false;
+
+      NetworkSettings that = (NetworkSettings) o;
+
+      return Objects.equal(this.ipAddress, that.ipAddress) &&
+              Objects.equal(this.ipPrefixLen, that.ipPrefixLen) &&
+              Objects.equal(this.gateway, that.gateway) &&
+              Objects.equal(this.bridge, that.bridge) &&
+              Objects.equal(this.portMapping, that.portMapping) &&
+              Objects.equal(this.ports, that.ports);
+   }
+
+   @Override
+   public int hashCode() {
+      return Objects.hashCode(ipAddress, ipPrefixLen, gateway, bridge, portMapping, ports);
+   }
+
+   @Override
+   public String toString() {
+      return Objects.toStringHelper(this)
+              .add("ipAddress", ipAddress)
+              .add("ipPrefixLen", ipPrefixLen)
+              .add("gateway", gateway)
+              .add("bridge", bridge)
+              .add("portMapping", portMapping)
+              .add("ports", ports)
+              .toString();
+   }
+
+   public static Builder builder() {
+      return new Builder();
+   }
+
+   public Builder toBuilder() {
+      return builder().fromNetworkSettings(this);
+   }
+
+   public static final class Builder {
+
+      private String ipAddress;
+      private int ipPrefixLen;
+      private String gateway;
+      private String bridge;
+      private String portMapping;
+      private Map<String, List<Map<String, String>>> ports = ImmutableMap.of();
+
+      public Builder ipAddress(String ipAddress) {
+         this.ipAddress = ipAddress;
+         return this;
+      }
+
+      public Builder ipPrefixLen(int ipPrefixLen) {
+         this.ipPrefixLen = ipPrefixLen;
+         return this;
+      }
+
+      public Builder gateway(String gateway) {
+         this.gateway = gateway;
+         return this;
+      }
+
+      public Builder bridge(String bridge) {
+         this.bridge = bridge;
+         return this;
+      }
+
+      public Builder portMapping(String portMapping) {
+         this.portMapping = portMapping;
+         return this;
+      }
+
+      public Builder ports(Map<String, List<Map<String, String>>> ports) {
+         this.ports = ImmutableMap.copyOf(checkNotNull(ports, "ports"));
+         return this;
+      }
+
+      public NetworkSettings build() {
+         return new NetworkSettings(ipAddress, ipPrefixLen, gateway, bridge, portMapping, ports);
+      }
+
+      public Builder fromNetworkSettings(NetworkSettings in) {
+         return this
+                 .ipAddress(in.getIpAddress())
+                 .ipPrefixLen(in.getIpPrefixLen())
+                 .gateway(in.getGateway())
+                 .bridge(in.getBridge())
+                 .portMapping(in.getPortMapping())
+                 .ports(in.getPorts());
+      }
+
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9b124ee9/docker/src/main/java/org/jclouds/docker/domain/Port.java
----------------------------------------------------------------------
diff --git a/docker/src/main/java/org/jclouds/docker/domain/Port.java b/docker/src/main/java/org/jclouds/docker/domain/Port.java
new file mode 100644
index 0000000..b49dfa6
--- /dev/null
+++ b/docker/src/main/java/org/jclouds/docker/domain/Port.java
@@ -0,0 +1,88 @@
+/*
+ * 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.docker.domain;
+
+import com.google.common.base.Objects;
+import com.google.gson.annotations.SerializedName;
+
+import java.beans.ConstructorProperties;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+public class Port {
+
+   @SerializedName("PrivatePort")
+   private final int privatePort;
+   @SerializedName("PublicPort")
+   private final int publicPort;
+   @SerializedName("Type")
+   private final String type;
+   @SerializedName("IP")
+   private final String ip;
+
+   @ConstructorProperties({ "PrivatePort", "PublicPort", "Type", "IP" })
+   protected Port(int privatePort, int publicPort, String type, String ip) {
+      this.privatePort = checkNotNull(privatePort, "privatePort");
+      this.publicPort = checkNotNull(publicPort, "publicPort");
+      this.type = checkNotNull(type, "type");
+      this.ip = checkNotNull(ip, "ip");
+   }
+
+   public int getPrivatePort() {
+      return privatePort;
+   }
+
+   public int getPublicPort() {
+      return publicPort;
+   }
+
+   public String getType() {
+      return type;
+   }
+
+   public String getIp() {
+      return ip;
+   }
+
+   @Override
+   public boolean equals(Object o) {
+      if (this == o) return true;
+      if (o == null || getClass() != o.getClass()) return false;
+
+      Port that = (Port) o;
+
+      return Objects.equal(this.privatePort, that.privatePort) &&
+              Objects.equal(this.publicPort, that.publicPort) &&
+              Objects.equal(this.type, that.type) &&
+              Objects.equal(this.ip, that.ip);
+   }
+
+   @Override
+   public int hashCode() {
+      return Objects.hashCode(privatePort, publicPort, type, ip);
+   }
+
+   @Override
+   public String toString() {
+      return Objects.toStringHelper(this)
+              .add("privatePort", privatePort)
+              .add("publicPort", publicPort)
+              .add("type", type)
+              .add("ip", ip)
+              .toString();
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9b124ee9/docker/src/main/java/org/jclouds/docker/domain/State.java
----------------------------------------------------------------------
diff --git a/docker/src/main/java/org/jclouds/docker/domain/State.java b/docker/src/main/java/org/jclouds/docker/domain/State.java
new file mode 100644
index 0000000..ac1ba85
--- /dev/null
+++ b/docker/src/main/java/org/jclouds/docker/domain/State.java
@@ -0,0 +1,166 @@
+/*
+ * 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.docker.domain;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import java.beans.ConstructorProperties;
+
+import com.google.common.base.Objects;
+import com.google.gson.annotations.SerializedName;
+
+public class State {
+   @SerializedName("Pid")
+   private final int pid;
+   @SerializedName("Running")
+   private final boolean running;
+   @SerializedName("ExitCode")
+   private final int exitCode;
+   @SerializedName("StartedAt")
+   private final String startedAt;
+   @SerializedName("FinishedAt")
+   private final String finishedAt;
+   @SerializedName("Ghost")
+   private final boolean ghost;
+
+   @ConstructorProperties({ "Pid", "Running", "ExitCode", "StartedAt", "FinishedAt", "Ghost" })
+   protected State(int pid, boolean running, int exitCode, String startedAt, String finishedAt, boolean ghost) {
+      this.pid = checkNotNull(pid, "pid");
+      this.running = checkNotNull(running, "running");
+      this.exitCode = checkNotNull(exitCode, "exitCode");
+      this.startedAt = checkNotNull(startedAt, "startedAt");
+      this.finishedAt = checkNotNull(finishedAt, "finishedAt");
+      this.ghost = checkNotNull(ghost, "ghost");
+   }
+
+   public int getPid() {
+      return pid;
+   }
+
+   public boolean isRunning() {
+      return running;
+   }
+
+   public int getExitCode() {
+      return exitCode;
+   }
+
+   public String getStartedAt() {
+      return startedAt;
+   }
+
+   public String getFinishedAt() {
+      return finishedAt;
+   }
+
+   public boolean isGhost() {
+      return ghost;
+   }
+
+   @Override
+   public boolean equals(Object o) {
+      if (this == o) return true;
+      if (o == null || getClass() != o.getClass()) return false;
+
+      State that = (State) o;
+
+      return Objects.equal(this.pid, that.pid) &&
+              Objects.equal(this.running, that.running) &&
+              Objects.equal(this.exitCode, that.exitCode) &&
+              Objects.equal(this.startedAt, that.startedAt) &&
+              Objects.equal(this.finishedAt, that.finishedAt) &&
+              Objects.equal(this.ghost, that.ghost);
+   }
+
+   @Override
+   public int hashCode() {
+      return Objects.hashCode(pid, running, exitCode, startedAt, finishedAt, ghost);
+   }
+
+   @Override
+   public String toString() {
+      return Objects.toStringHelper(this)
+              .add("pid", pid)
+              .add("running", running)
+              .add("exitCode", exitCode)
+              .add("startedAt", startedAt)
+              .add("finishedAt", finishedAt)
+              .add("ghost", ghost)
+              .toString();
+   }
+
+   public static Builder builder() {
+      return new Builder();
+   }
+
+   public Builder toBuilder() {
+      return builder().fromState(this);
+   }
+
+   public static final class Builder {
+
+      private int pid;
+      private boolean running;
+      private int exitCode;
+      private String startedAt;
+      private String finishedAt;
+      private boolean ghost;
+
+      public Builder pid(int pid) {
+         this.pid = pid;
+         return this;
+      }
+
+      public Builder running(boolean running) {
+         this.running = running;
+         return this;
+      }
+
+      public Builder exitCode(int exitCode) {
+         this.exitCode = exitCode;
+         return this;
+      }
+
+      public Builder startedAt(String startedAt) {
+         this.startedAt = startedAt;
+         return this;
+      }
+
+      public Builder finishedAt(String finishedAt) {
+         this.finishedAt = finishedAt;
+         return this;
+      }
+
+      public Builder ghost(boolean ghost) {
+         this.ghost = ghost;
+         return this;
+      }
+
+      public State build() {
+         return new State(pid, running, exitCode, startedAt, finishedAt, ghost);
+      }
+
+      public Builder fromState(State in) {
+         return this
+                 .pid(in.getPid())
+                 .running(in.isRunning())
+                 .exitCode(in.getExitCode())
+                 .startedAt(in.getStartedAt())
+                 .finishedAt(in.getFinishedAt())
+                 .ghost(in.isGhost());
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9b124ee9/docker/src/main/java/org/jclouds/docker/domain/Version.java
----------------------------------------------------------------------
diff --git a/docker/src/main/java/org/jclouds/docker/domain/Version.java b/docker/src/main/java/org/jclouds/docker/domain/Version.java
new file mode 100644
index 0000000..445a225
--- /dev/null
+++ b/docker/src/main/java/org/jclouds/docker/domain/Version.java
@@ -0,0 +1,166 @@
+/*
+ * 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.docker.domain;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import java.beans.ConstructorProperties;
+
+import com.google.common.base.Objects;
+import com.google.gson.annotations.SerializedName;
+
+public class Version {
+   @SerializedName("Arch")
+   private final String arch;
+   @SerializedName("GitCommit")
+   private final String gitCommit;
+   @SerializedName("GoVersion")
+   private final String goVersion;
+   @SerializedName("KernelVersion")
+   private final String kernelVersion;
+   @SerializedName("Os")
+   private final String os;
+   @SerializedName("Version")
+   private final String version;
+
+   @ConstructorProperties({ "Arch", "GitCommit", "GoVersion", "KernelVersion", "Os", "Version" })
+   protected Version(String arch, String gitCommit, String goVersion, String kernelVersion, String os, String version) {
+      this.arch = checkNotNull(arch, "arch");
+      this.gitCommit = checkNotNull(gitCommit, "gitCommit");
+      this.goVersion = checkNotNull(goVersion, "goVersion");
+      this.kernelVersion = checkNotNull(kernelVersion, "kernelVersion");
+      this.os = checkNotNull(os, "os");
+      this.version = checkNotNull(version, "version");
+   }
+
+   public String getArch() {
+      return arch;
+   }
+
+   public String getGitCommit() {
+      return gitCommit;
+   }
+
+   public String getGoVersion() {
+      return goVersion;
+   }
+
+   public String getKernelVersion() {
+      return kernelVersion;
+   }
+
+   public String getOs() {
+      return os;
+   }
+
+   public String getVersion() {
+      return version;
+   }
+
+   @Override
+   public boolean equals(Object o) {
+      if (this == o) return true;
+      if (o == null || getClass() != o.getClass()) return false;
+
+      Version that = (Version) o;
+
+      return Objects.equal(this.arch, that.arch) &&
+              Objects.equal(this.gitCommit, that.gitCommit) &&
+              Objects.equal(this.goVersion, that.goVersion) &&
+              Objects.equal(this.kernelVersion, that.kernelVersion) &&
+              Objects.equal(this.os, that.os) &&
+              Objects.equal(this.version, that.version);
+   }
+
+   @Override
+   public int hashCode() {
+      return Objects.hashCode(arch, gitCommit, goVersion, kernelVersion, os, version);
+   }
+
+   @Override
+   public String toString() {
+      return Objects.toStringHelper(this)
+              .add("arch", arch)
+              .add("gitCommit", gitCommit)
+              .add("goVersion", goVersion)
+              .add("kernelVersion", kernelVersion)
+              .add("os", os)
+              .add("version", version)
+              .toString();
+   }
+
+   public static Builder builder() {
+      return new Builder();
+   }
+
+   public Builder toBuilder() {
+      return builder().fromVersion(this);
+   }
+
+   public static final class Builder {
+
+      private String arch;
+      private String gitCommit;
+      private String goVersion;
+      private String kernelVersion;
+      private String os;
+      private String version;
+
+      public Builder arch(String arch) {
+         this.arch = arch;
+         return this;
+      }
+
+      public Builder gitCommit(String gitCommit) {
+         this.gitCommit = gitCommit;
+         return this;
+      }
+
+      public Builder goVersion(String goVersion) {
+         this.goVersion = goVersion;
+         return this;
+      }
+
+      public Builder kernelVersion(String kernelVersion) {
+         this.kernelVersion = kernelVersion;
+         return this;
+      }
+
+      public Builder os(String os) {
+         this.os = os;
+         return this;
+      }
+
+      public Builder version(String version) {
+         this.version = version;
+         return this;
+      }
+
+      public Version build() {
+         return new Version(arch, gitCommit, goVersion, kernelVersion, os, version);
+      }
+
+      public Builder fromVersion(Version in) {
+         return this
+                 .arch(in.getArch())
+                 .gitCommit(in.getGitCommit())
+                 .goVersion(in.getGoVersion())
+                 .kernelVersion(in.getKernelVersion())
+                 .os(in.getOs())
+                 .version(in.getVersion());
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9b124ee9/docker/src/main/java/org/jclouds/docker/features/RemoteApi.java
----------------------------------------------------------------------
diff --git a/docker/src/main/java/org/jclouds/docker/features/RemoteApi.java b/docker/src/main/java/org/jclouds/docker/features/RemoteApi.java
new file mode 100644
index 0000000..96b0228
--- /dev/null
+++ b/docker/src/main/java/org/jclouds/docker/features/RemoteApi.java
@@ -0,0 +1,272 @@
+/*
+ * 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.docker.features;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.InputStream;
+import java.util.Set;
+
+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.QueryParam;
+import javax.ws.rs.core.MediaType;
+
+import org.jclouds.Fallbacks;
+import org.jclouds.docker.binders.BindInputStreamToRequest;
+import org.jclouds.docker.domain.Config;
+import org.jclouds.docker.domain.Container;
+import org.jclouds.docker.domain.HostConfig;
+import org.jclouds.docker.domain.Image;
+import org.jclouds.docker.domain.Version;
+import org.jclouds.docker.options.BuildOptions;
+import org.jclouds.docker.options.CommitOptions;
+import org.jclouds.docker.options.CreateImageOptions;
+import org.jclouds.docker.options.DeleteImageOptions;
+import org.jclouds.docker.options.ListContainerOptions;
+import org.jclouds.docker.options.ListImageOptions;
+import org.jclouds.docker.options.RemoveContainerOptions;
+import org.jclouds.io.Payload;
+import org.jclouds.rest.annotations.BinderParam;
+import org.jclouds.rest.annotations.Fallback;
+import org.jclouds.rest.annotations.Headers;
+import org.jclouds.rest.binders.BindToJsonPayload;
+
+@Consumes(MediaType.APPLICATION_JSON)
+public interface RemoteApi extends Closeable {
+
+   /**
+    * Get the information of the current docker version.
+    *
+    * @return The information of the current docker version.
+    */
+   @Named("version")
+   @GET
+   @Path("/version")
+   Version getVersion();
+
+   /**
+    * List all running containers
+    *
+    * @return a set of containers
+    */
+   @Named("containers:list")
+   @GET
+   @Path("/containers/json")
+   @Fallback(Fallbacks.EmptySetOnNotFoundOr404.class)
+   Set<Container> listContainers();
+
+   /**
+    * List all running containers
+    *
+    * @param options the options to list the containers (@see ListContainerOptions)
+    * @return a set of containers
+    */
+   @Named("containers:list")
+   @GET
+   @Path("/containers/json")
+   @Fallback(Fallbacks.EmptySetOnNotFoundOr404.class)
+   Set<Container> listContainers(ListContainerOptions options);
+
+   /**
+    * Create a container
+    *
+    * @param name the name for the new container. Must match /?[a-zA-Z0-9_-]+.
+    * @param config the container’s configuration (@see BindToJsonPayload)
+    * @return a new container
+    */
+   @Named("container:create")
+   @POST
+   @Path("/containers/create")
+   Container createContainer(@QueryParam("name") String name, @BinderParam(BindToJsonPayload.class) Config config);
+
+   /**
+    * Return low-level information on the container id
+    * @param containerId  The id of the container to get.
+    * @return The details of the container or <code>null</code> if the container with the given id doesn't exist.
+    */
+   @Named("container:inspect")
+   @GET
+   @Path("/containers/{id}/json")
+   @Fallback(Fallbacks.NullOnNotFoundOr404.class)
+   Container inspectContainer(@PathParam("id") String containerId);
+
+   /**
+    * Remove the container by id from the filesystem
+    *
+    * @param containerId The id of the container to be removed.
+    */
+   @Named("container:delete")
+   @DELETE
+   @Path("/containers/{id}")
+   void removeContainer(@PathParam("id") String containerId);
+
+   /**
+    * Remove the container by id from the filesystem
+    *
+    * @param containerId The id of the container to be removed.
+    * @param options the operation’s configuration (@see RemoveContainerOptions)
+    */
+   @Named("container:delete")
+   @DELETE
+   @Path("/containers/{id}")
+   void removeContainer(@PathParam("id") String containerId, RemoveContainerOptions options);
+
+   /**
+    * Start a container by id.
+    *
+    * @param containerId The id of the container to be started.
+    */
+   @Named("container:start")
+   @POST
+   @Path("/containers/{id}/start")
+   void startContainer(@PathParam("id") String containerId);
+
+   /**
+    * Start a container.
+    *
+    * @param containerId The id of the container to be started.
+    * @param hostConfig the container’s host configuration
+    */
+   @Named("container:start")
+   @POST
+   @Path("/containers/{id}/start")
+   void startContainer(@PathParam("id") String containerId, @BinderParam(BindToJsonPayload.class) HostConfig hostConfig);
+
+   /**
+    * Stop a container by id.
+    *
+    * @param containerId The id of the container to be stopped.
+    * @return the stream of the stop execution.
+    */
+   @Named("container:stop")
+   @POST
+   @Path("/containers/{id}/stop")
+   void stopContainer(@PathParam("id") String containerId);
+
+   /**
+    * Create a new image from a container’s changes
+    *
+    * @param options the commit’s configuration (@see CommitOptions)
+    * @return a new image created from the current container's status.
+    */
+   @Named("container:commit")
+   @POST
+   @Path("/commit")
+   Image commit(CommitOptions options);
+
+   /**
+    * List images
+    *
+    * @return the images available.
+    */
+   @Named("images:list")
+   @GET
+   @Path("/images/json")
+   @Fallback(Fallbacks.EmptySetOnNotFoundOr404.class)
+   Set<Image> listImages();
+
+   /**
+    * List images
+    *
+    * @param options the configuration to list images (@see ListImageOptions)
+    * @return the images available.
+    */
+   @Named("images:list")
+   @GET
+   @Path("/images/json")
+   @Fallback(Fallbacks.EmptySetOnNotFoundOr404.class)
+   Set<Image> listImages(ListImageOptions options);
+
+   /**
+    * Inspect an image
+    *
+    * @param imageName The id of the image to inspect.
+    * @return low-level information on the image name
+    */
+   @Named("image:inspect")
+   @GET
+   @Path("/images/{name}/json")
+   Image inspectImage(@PathParam("name") String imageName);
+
+   /**
+    * Create an image, either by pull it from the registry or by importing it
+    *
+    * @param options the configuration to create an image (@see CreateImageOptions)
+    * @return a stream of the image creation.
+    */
+   @Named("image:create")
+   @POST
+   @Path("/images/create")
+   InputStream createImage(CreateImageOptions options);
+
+   /**
+    * Delete an image.
+    *
+    * @param name the image name to be deleted
+    * @return the stream of the deletion execution.
+    */
+   @Named("image:delete")
+   @DELETE
+   @Path("/images/{name}")
+   InputStream deleteImage(@PathParam("name") String name);
+
+   /**
+    * Remove the image from the filesystem by name
+    *
+    * @param name the name of the image to be removed
+    * @param options the image deletion's options (@see DeleteImageOptions)
+    * @return the stream of the deletion execution.
+    */
+   @Named("image:delete")
+   @DELETE
+   @Path("/images/{name}")
+   InputStream deleteImage(@PathParam("name") String name, DeleteImageOptions options);
+
+   /**
+    * Build an image from Dockerfile via stdin
+    *
+    * @param inputStream The stream must be a tar archive compressed with one of the following algorithms: identity
+    *                    (no compression), gzip, bzip2, xz.
+    * @param options the image build's options (@see BuildOptions)
+    * @return a stream of the build execution
+    */
+   @Named("image:build")
+   @POST
+   @Path("/build")
+   @Headers(keys = "Content-Type", values = "application/tar")
+   InputStream build(Payload inputStream, BuildOptions options);
+
+   /**
+    * Build an image from Dockerfile via stdin
+    *
+    * @param dockerFile The file to be compressed with one of the following algorithms: identity, gzip, bzip2, xz.*
+    * @param options the image build's options (@see BuildOptions)
+    * @return a stream of the build execution
+    */
+   @Named("image:build")
+   @POST
+   @Path("/build")
+   @Headers(keys = "Content-Type", values = "application/tar")
+   InputStream build(@BinderParam(BindInputStreamToRequest.class) File dockerFile, BuildOptions options);
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9b124ee9/docker/src/main/java/org/jclouds/docker/features/internal/Archives.java
----------------------------------------------------------------------
diff --git a/docker/src/main/java/org/jclouds/docker/features/internal/Archives.java b/docker/src/main/java/org/jclouds/docker/features/internal/Archives.java
new file mode 100644
index 0000000..43b69c3
--- /dev/null
+++ b/docker/src/main/java/org/jclouds/docker/features/internal/Archives.java
@@ -0,0 +1,60 @@
+/*
+ * 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.docker.features.internal;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.collect.Iterables.getLast;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
+import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
+
+import com.google.common.base.Splitter;
+import com.google.common.io.Files;
+
+public class Archives {
+
+   public static File tar(File baseDir, String archivePath) throws IOException {
+      return tar(baseDir, new File(archivePath));
+   }
+
+   public static File tar(File baseDir, File tarFile) throws IOException {
+      // Check that the directory is a directory, and get its contents
+      checkArgument(baseDir.isDirectory(), "%s is not a directory", baseDir);
+      File[] files = baseDir.listFiles();
+      String token = getLast(Splitter.on("/").split(baseDir.getAbsolutePath()));
+      TarArchiveOutputStream tos = new TarArchiveOutputStream(new FileOutputStream(tarFile));
+      tos.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU);
+      try {
+         for (File file : files) {
+            TarArchiveEntry tarEntry = new TarArchiveEntry(file);
+            tarEntry.setName("/" + getLast(Splitter.on(token).split(file.toString())));
+            tos.putArchiveEntry(tarEntry);
+            if (!file.isDirectory()) {
+               Files.asByteSource(file).copyTo(tos);
+            }
+            tos.closeArchiveEntry();
+         }
+      } finally {
+         tos.close();
+      }
+      return tarFile;
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9b124ee9/docker/src/main/java/org/jclouds/docker/handlers/DockerErrorHandler.java
----------------------------------------------------------------------
diff --git a/docker/src/main/java/org/jclouds/docker/handlers/DockerErrorHandler.java b/docker/src/main/java/org/jclouds/docker/handlers/DockerErrorHandler.java
new file mode 100644
index 0000000..855f25f
--- /dev/null
+++ b/docker/src/main/java/org/jclouds/docker/handlers/DockerErrorHandler.java
@@ -0,0 +1,106 @@
+/*
+ * 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.docker.handlers;
+
+import com.google.common.base.Throwables;
+import com.google.common.io.Closeables;
+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.rest.ResourceNotFoundException;
+import org.jclouds.util.Strings2;
+
+import javax.annotation.Resource;
+import java.io.IOException;
+
+/**
+ * This will parse and set an appropriate exception on the command object.
+ * <p/>
+ * <p/>
+ * Errors are returned with an appropriate HTTP status code, an X-Elastic- Error header specifying
+ * the error type, and a text description in the HTTP body.
+ */
+public class DockerErrorHandler implements HttpErrorHandler {
+   @Resource
+   protected Logger logger = Logger.NULL;
+
+   public void handleError(HttpCommand command, HttpResponse response) {
+      // it is important to always read fully and close streams
+      String message = parseMessage(response);
+      Exception exception = message != null ? new HttpResponseException(command, response, message)
+              : new HttpResponseException(command, response);
+      try {
+         message = message != null ? message : String.format("%s -> %s", command.getCurrentRequest().getRequestLine(),
+                 response.getStatusLine());
+         switch (response.getStatusCode()) {
+            case 400:
+               if ((command.getCurrentRequest().getEndpoint().getPath().endsWith("/info"))
+                       || (message != null && message.indexOf("could not be found") != -1))
+                  exception = new ResourceNotFoundException(message, exception);
+               else if (message != null && message.indexOf("currently in use") != -1)
+                  exception = new IllegalStateException(message, exception);
+               else
+                  exception = new IllegalArgumentException(message, exception);
+               break;
+            case 401:
+               exception = new AuthorizationException(message, exception);
+               break;
+            case 402:
+               exception = new IllegalStateException(message, exception);
+               break;
+            case 404:
+               if (!command.getCurrentRequest().getMethod().equals("DELETE")) {
+                  exception = new ResourceNotFoundException(message, exception);
+               }
+               break;
+            case 405:
+               exception = new IllegalArgumentException(message, exception);
+               break;
+            case 409:
+               exception = new IllegalStateException(message, exception);
+               break;
+         }
+      } finally {
+         try {
+            Closeables.close(response.getPayload(), true);
+         } catch (IOException e) {
+            // This code will never be reached
+            throw Throwables.propagate(e);
+         }
+         command.setException(exception);
+      }
+   }
+
+   public String parseMessage(HttpResponse response) {
+      if (response.getPayload() == null)
+         return null;
+      try {
+         return Strings2.toString(response.getPayload());
+      } catch (IOException e) {
+         throw Throwables.propagate(e);
+      } finally {
+         try {
+            response.getPayload().close();
+         } catch (IOException e) {
+            throw Throwables.propagate(e);
+         }
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9b124ee9/docker/src/main/java/org/jclouds/docker/options/BuildOptions.java
----------------------------------------------------------------------
diff --git a/docker/src/main/java/org/jclouds/docker/options/BuildOptions.java b/docker/src/main/java/org/jclouds/docker/options/BuildOptions.java
new file mode 100644
index 0000000..4d7196c
--- /dev/null
+++ b/docker/src/main/java/org/jclouds/docker/options/BuildOptions.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.docker.options;
+
+import org.jclouds.http.options.BaseHttpRequestOptions;
+
+/**
+ * Options to customize image builder.
+ */
+public class BuildOptions extends BaseHttpRequestOptions {
+
+   public static final BuildOptions NONE = new BuildOptions();
+
+   public BuildOptions tag(String tag) {
+      this.queryParameters.put("tag", tag);
+      return this;
+   }
+
+   public BuildOptions verbose(Boolean verbose) {
+      this.queryParameters.put("verbose", verbose.toString());
+      return this;
+   }
+
+   public BuildOptions nocache(Boolean nocache) {
+      this.queryParameters.put("nocache", nocache.toString());
+      return this;
+   }
+
+   public static class Builder {
+
+      /**
+       * @see BuildOptions#tag
+       */
+      public static BuildOptions tag(String tag) {
+         BuildOptions options = new BuildOptions();
+         return options.tag(tag);
+      }
+
+      /**
+       * @see BuildOptions#verbose(Boolean)
+       */
+      public static BuildOptions verbose(Boolean verbose) {
+         BuildOptions options = new BuildOptions();
+         return options.verbose(verbose);
+      }
+
+      /**
+       * @see BuildOptions#nocache(Boolean)
+       */
+      public static BuildOptions nocache(Boolean nocache) {
+         BuildOptions options = new BuildOptions();
+         return options.nocache(nocache);
+      }
+
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9b124ee9/docker/src/main/java/org/jclouds/docker/options/CommitOptions.java
----------------------------------------------------------------------
diff --git a/docker/src/main/java/org/jclouds/docker/options/CommitOptions.java b/docker/src/main/java/org/jclouds/docker/options/CommitOptions.java
new file mode 100644
index 0000000..5653fba
--- /dev/null
+++ b/docker/src/main/java/org/jclouds/docker/options/CommitOptions.java
@@ -0,0 +1,109 @@
+/*
+ * 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.docker.options;
+
+import org.jclouds.http.options.BaseHttpRequestOptions;
+
+/**
+ * Options to customize image commit.
+ */
+public class CommitOptions extends BaseHttpRequestOptions {
+
+   public static final CommitOptions NONE = new CommitOptions();
+
+   public CommitOptions containerId(String containerId) {
+      this.queryParameters.put("containerId", containerId);
+      return this;
+   }
+
+   public CommitOptions repository(String repository) {
+      this.queryParameters.put("repository", repository);
+      return this;
+   }
+
+   public CommitOptions tag(String tag) {
+      this.queryParameters.put("tag", tag);
+      return this;
+   }
+
+   public CommitOptions message(String message) {
+      this.queryParameters.put("message", message);
+      return this;
+   }
+
+   public CommitOptions author(String author) {
+      this.queryParameters.put("author", author);
+      return this;
+   }
+
+   public CommitOptions run(String run) {
+      this.queryParameters.put("run", run);
+      return this;
+   }
+
+   public static class Builder {
+
+      /**
+       * @see CommitOptions#containerId
+       */
+      public static CommitOptions containerId(String containerId) {
+         CommitOptions options = new CommitOptions();
+         return options.containerId(containerId);
+      }
+
+      /**
+       * @see CommitOptions#repository
+       */
+      public static CommitOptions repository(String repository) {
+         CommitOptions options = new CommitOptions();
+         return options.repository(repository);
+      }
+
+      /**
+       * @see CommitOptions#tag
+       */
+      public static CommitOptions tag(String tag) {
+         CommitOptions options = new CommitOptions();
+         return options.tag(tag);
+      }
+
+      /**
+       * @see CommitOptions#message
+       */
+      public static CommitOptions message(String message) {
+         CommitOptions options = new CommitOptions();
+         return options.message(message);
+      }
+
+      /**
+       * @see CommitOptions#author
+       */
+      public static CommitOptions author(String author) {
+         CommitOptions options = new CommitOptions();
+         return options.author(author);
+      }
+
+      /**
+       * @see CommitOptions#run
+       */
+      public static CommitOptions run(String run) {
+         CommitOptions options = new CommitOptions();
+         return options.run(run);
+      }
+
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9b124ee9/docker/src/main/java/org/jclouds/docker/options/CreateImageOptions.java
----------------------------------------------------------------------
diff --git a/docker/src/main/java/org/jclouds/docker/options/CreateImageOptions.java b/docker/src/main/java/org/jclouds/docker/options/CreateImageOptions.java
new file mode 100644
index 0000000..51dc399
--- /dev/null
+++ b/docker/src/main/java/org/jclouds/docker/options/CreateImageOptions.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.docker.options;
+
+import org.jclouds.http.options.BaseHttpRequestOptions;
+
+/**
+ * Options to customize container creation.
+ */
+public class CreateImageOptions extends BaseHttpRequestOptions {
+
+   public static final CreateImageOptions NONE = new CreateImageOptions();
+
+   public CreateImageOptions fromImage(String fromImage) {
+      this.queryParameters.put("fromImage", fromImage);
+      return this;
+   }
+
+   public CreateImageOptions fromSrc(String fromSrc) {
+      this.queryParameters.put("fromSrc", fromSrc);
+      return this;
+   }
+
+   public CreateImageOptions repo(String repo) {
+      this.queryParameters.put("repo", repo);
+      return this;
+   }
+
+   public CreateImageOptions tag(String tag) {
+      this.queryParameters.put("tag", tag);
+      return this;
+   }
+
+   public CreateImageOptions registry(String registry) {
+      this.queryParameters.put("registry", registry);
+      return this;
+   }
+
+   public static class Builder {
+      /**
+       * @see CreateImageOptions#fromImage
+       */
+      public static CreateImageOptions fromImage(String fromImage) {
+         CreateImageOptions options = new CreateImageOptions();
+         return options.fromImage(fromImage);
+      }
+
+      /**
+       * @see CreateImageOptions#fromSrc
+       */
+      public static CreateImageOptions fromSrc(String fromSrc) {
+         CreateImageOptions options = new CreateImageOptions();
+         return options.fromSrc(fromSrc);
+      }
+
+      /**
+       * @see CreateImageOptions#repo
+       */
+      public static CreateImageOptions repo(String repo) {
+         CreateImageOptions options = new CreateImageOptions();
+         return options.repo(repo);
+      }
+
+      /**
+       * @see CreateImageOptions#tag
+       */
+      public static CreateImageOptions tag(String tag) {
+         CreateImageOptions options = new CreateImageOptions();
+         return options.tag(tag);
+      }
+
+      /**
+       * @see CreateImageOptions#registry
+       */
+      public static CreateImageOptions registry(String registry) {
+         CreateImageOptions options = new CreateImageOptions();
+         return options.registry(registry);
+      }
+
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9b124ee9/docker/src/main/java/org/jclouds/docker/options/DeleteImageOptions.java
----------------------------------------------------------------------
diff --git a/docker/src/main/java/org/jclouds/docker/options/DeleteImageOptions.java b/docker/src/main/java/org/jclouds/docker/options/DeleteImageOptions.java
new file mode 100644
index 0000000..9438616
--- /dev/null
+++ b/docker/src/main/java/org/jclouds/docker/options/DeleteImageOptions.java
@@ -0,0 +1,44 @@
+/*
+ * 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.docker.options;
+
+import org.jclouds.http.options.BaseHttpRequestOptions;
+
+/**
+ * Options to customize image deletion.
+ */
+public class DeleteImageOptions extends BaseHttpRequestOptions {
+
+   public static final DeleteImageOptions NONE = new DeleteImageOptions();
+
+   public DeleteImageOptions force(Boolean force) {
+      this.queryParameters.put("force", force.toString());
+      return this;
+   }
+
+   public static class Builder {
+
+      /**
+       * @see DeleteImageOptions#force
+       */
+      public static DeleteImageOptions force(Boolean force) {
+         DeleteImageOptions options = new DeleteImageOptions();
+         return options.force(force);
+      }
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9b124ee9/docker/src/main/java/org/jclouds/docker/options/ListContainerOptions.java
----------------------------------------------------------------------
diff --git a/docker/src/main/java/org/jclouds/docker/options/ListContainerOptions.java b/docker/src/main/java/org/jclouds/docker/options/ListContainerOptions.java
new file mode 100644
index 0000000..af16664
--- /dev/null
+++ b/docker/src/main/java/org/jclouds/docker/options/ListContainerOptions.java
@@ -0,0 +1,97 @@
+/*
+ * 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.docker.options;
+
+import org.jclouds.http.options.BaseHttpRequestOptions;
+
+/**
+ * Options to customize container's listing.
+ */
+public class ListContainerOptions extends BaseHttpRequestOptions {
+
+   public static final ListContainerOptions NONE = new ListContainerOptions();
+
+   public ListContainerOptions all(Boolean all) {
+      this.queryParameters.put("all", all.toString());
+      return this;
+   }
+
+   public ListContainerOptions limit(Integer limit) {
+      this.queryParameters.put("limit", limit.toString());
+      return this;
+   }
+
+   public ListContainerOptions since(Integer since) {
+      this.queryParameters.put("since", since.toString());
+      return this;
+   }
+
+   public ListContainerOptions before(Integer before) {
+      this.queryParameters.put("before", before.toString());
+      return this;
+   }
+
+   public ListContainerOptions size(Integer size) {
+      this.queryParameters.put("size", size.toString());
+      return this;
+   }
+
+   public static class Builder {
+
+      /**
+       * @see ListContainerOptions#all
+       */
+      public static ListContainerOptions all(Boolean all) {
+         ListContainerOptions options = new ListContainerOptions();
+         return options.all(all);
+      }
+
+      /**
+       * @see ListContainerOptions#limit(Integer)
+       */
+      public static ListContainerOptions limit(Integer limit) {
+         ListContainerOptions options = new ListContainerOptions();
+         return options.limit(limit);
+      }
+
+      /**
+       * @see ListContainerOptions#since(Integer)
+       */
+      public static ListContainerOptions since(Integer since) {
+         ListContainerOptions options = new ListContainerOptions();
+         return options.since(since);
+      }
+
+      /**
+       * @see ListContainerOptions#before(Integer)
+       */
+      public static ListContainerOptions before(Integer before) {
+         ListContainerOptions options = new ListContainerOptions();
+         return options.before(before);
+      }
+
+      /**
+       * @see ListContainerOptions#limit(Integer)
+       */
+      public static ListContainerOptions size(Integer size) {
+         ListContainerOptions options = new ListContainerOptions();
+         return options.size(size);
+      }
+
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/9b124ee9/docker/src/main/java/org/jclouds/docker/options/ListImageOptions.java
----------------------------------------------------------------------
diff --git a/docker/src/main/java/org/jclouds/docker/options/ListImageOptions.java b/docker/src/main/java/org/jclouds/docker/options/ListImageOptions.java
new file mode 100644
index 0000000..fab75d4
--- /dev/null
+++ b/docker/src/main/java/org/jclouds/docker/options/ListImageOptions.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.docker.options;
+
+import org.jclouds.http.options.BaseHttpRequestOptions;
+
+/**
+ * Options to customize image's listing.
+ */
+public class ListImageOptions extends BaseHttpRequestOptions {
+
+   public static final ListImageOptions NONE = new ListImageOptions();
+
+   public ListImageOptions all(Boolean all) {
+      this.queryParameters.put("all", all.toString());
+      return this;
+   }
+
+   public static class Builder {
+      /**
+       * @see ListImageOptions#all
+       */
+      public static ListImageOptions all(Boolean all) {
+         ListImageOptions options = new ListImageOptions();
+         return options.all(all);
+      }
+   }
+
+}