You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jclouds.apache.org by ad...@apache.org on 2014/11/04 04:28:22 UTC

[2/2] git commit: JCLOUDS-737 update docker to support v1.3

JCLOUDS-737 update docker to support v1.3


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

Branch: refs/heads/master
Commit: e3c6a6f5a7b26badaa385a2ba626fcb9e386761a
Parents: 933f31b
Author: Andrea Turli <an...@gmail.com>
Authored: Thu Oct 2 18:08:24 2014 +0200
Committer: Adrian Cole <ad...@apache.org>
Committed: Mon Nov 3 19:23:09 2014 -0800

----------------------------------------------------------------------
 docker/README.md                                |  10 +-
 docker/pom.xml                                  |   1 +
 .../main/java/org/jclouds/docker/DockerApi.java |  20 +-
 .../org/jclouds/docker/DockerApiMetadata.java   |  12 +-
 .../functions/ContainerToNodeMetadata.java      |   1 -
 .../docker/compute/functions/ImageToImage.java  |   2 +
 .../compute/options/DockerTemplateOptions.java  |   2 +-
 .../strategy/DockerComputeServiceAdapter.java   |  44 +--
 .../docker/config/DockerHttpApiModule.java      |  23 ++
 .../java/org/jclouds/docker/domain/Config.java  |   6 +
 .../jclouds/docker/domain/ContainerSummary.java |  49 +++
 .../org/jclouds/docker/domain/HostConfig.java   |  10 +-
 .../java/org/jclouds/docker/domain/Image.java   |   3 +-
 .../java/org/jclouds/docker/domain/Info.java    |  63 ++++
 .../java/org/jclouds/docker/domain/Version.java |  11 +-
 .../jclouds/docker/features/ContainerApi.java   | 157 +++++++++
 .../org/jclouds/docker/features/ImageApi.java   | 110 ++++++
 .../org/jclouds/docker/features/MiscApi.java    |  72 ++++
 .../org/jclouds/docker/features/RemoteApi.java  | 256 --------------
 .../docker/suppliers/KeyStoreSupplier.java      | 130 +++++++
 .../suppliers/SSLContextWithKeysSupplier.java   |  77 +++++
 .../docker/compute/BaseDockerApiLiveTest.java   |   2 +
 .../DockerComputeServiceAdapterLiveTest.java    |  38 ++-
 .../compute/DockerComputeServiceLiveTest.java   | 220 ++++++++----
 .../functions/ContainerToNodeMetadataTest.java  |   6 +
 .../compute/functions/ImageToImageTest.java     |   1 +
 .../docker/features/ContainerApiLiveTest.java   | 105 ++++++
 .../docker/features/ContainerApiMockTest.java   | 248 ++++++++++++++
 .../docker/features/ImageApiLiveTest.java       |  59 ++++
 .../docker/features/ImageApiMockTest.java       |  97 ++++++
 .../docker/features/MiscApiLiveTest.java        |  86 +++++
 .../docker/features/MiscApiMockTest.java        | 159 +++++++++
 .../docker/features/RemoteApiLiveTest.java      | 115 -------
 .../docker/features/RemoteApiMockTest.java      | 337 -------------------
 .../docker/internal/BaseDockerMockTest.java     |   4 +-
 docker/src/test/resources/Dockerfile            |  17 +-
 docker/src/test/resources/SimpleDockerfile      |  18 +
 docker/src/test/resources/container.json        | 204 ++++++-----
 docker/src/test/resources/info.json             |  28 ++
 docker/src/test/resources/version.json          |   9 +
 40 files changed, 1892 insertions(+), 920 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e3c6a6f5/docker/README.md
----------------------------------------------------------------------
diff --git a/docker/README.md b/docker/README.md
index 1c4394a..9b55cbc 100644
--- a/docker/README.md
+++ b/docker/README.md
@@ -7,6 +7,10 @@ providers, it supports the same portable abstractions offered by jclouds.
 Please follow these steps to configure your workstation for jclouds-docker:
 
 - install the latest Docker release (please visit https://docs.docker.com/installation/)
+If you are using boot2docker, notice that from version v1.3.0 the Docker daemon is set to use an encrypted TCP socket (--tls, or --tlsverify), 
+then you need to create a p12 certificate using the following command:
+      
+      `openssl pkcs12 -export -out $HOME/.jclouds/docker.p12 -inkey $HOME/.boot2docker/certs/boot2docker-vm/key.pem -in $HOME/.boot2docker/certs/boot2docker-vm/cert.pem -certfile $HOME/.boot2docker/certs/boot2docker-vm/ca.pem`
 
 #How it works
 
@@ -41,8 +45,8 @@ Please follow these steps to configure your workstation for jclouds-docker:
 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`
+    
+    `$ docker ps -aq | xargs docker rm -f`
 2. remove all the images
 
-    `$ docker images -q | xargs docker rmi`
+    `$ docker images -q | xargs docker rmi -f`

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e3c6a6f5/docker/pom.xml
----------------------------------------------------------------------
diff --git a/docker/pom.xml b/docker/pom.xml
index 0469d60..b268a83 100644
--- a/docker/pom.xml
+++ b/docker/pom.xml
@@ -131,6 +131,7 @@
                   <goal>test</goal>
                 </goals>
                 <configuration>
+                  <threadCount>1</threadCount>
                   <systemPropertyVariables>
                     <test.docker.endpoint>${test.docker.endpoint}</test.docker.endpoint>
                     <test.docker.api-version>${test.docker.api-version}</test.docker.api-version>

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e3c6a6f5/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
index 54bf5c9..502f577 100644
--- a/docker/src/main/java/org/jclouds/docker/DockerApi.java
+++ b/docker/src/main/java/org/jclouds/docker/DockerApi.java
@@ -16,22 +16,22 @@
  */
 package org.jclouds.docker;
 
-import org.jclouds.docker.features.RemoteApi;
+import org.jclouds.docker.features.ContainerApi;
+import org.jclouds.docker.features.ImageApi;
+import org.jclouds.docker.features.MiscApi;
 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();
+   MiscApi getMiscApi();
+
+   @Delegate
+   ContainerApi getContainerApi();
+
+   @Delegate
+   ImageApi getImageApi();
 
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e3c6a6f5/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
index a533614..d5c470f 100644
--- a/docker/src/main/java/org/jclouds/docker/DockerApiMetadata.java
+++ b/docker/src/main/java/org/jclouds/docker/DockerApiMetadata.java
@@ -56,7 +56,7 @@ public class DockerApiMetadata extends BaseHttpApiMetadata<DockerApi> {
       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]");
+      properties.setProperty(TEMPLATE, "osFamily=UBUNTU,os64Bit=true");
       return properties;
    }
 
@@ -66,16 +66,16 @@ public class DockerApiMetadata extends BaseHttpApiMetadata<DockerApi> {
          super(DockerApi.class);
          id("docker")
                  .name("Docker API")
-                 .identityName("user")
-                 .credentialName("password")
+                 .identityName("Path to Certificate .p12 file")
+                 .credentialName("Password to Certificate")
                  .documentation(URI.create("https://docs.docker.com/reference/api/docker_remote_api/"))
-                 .version("1.12")
-                 .defaultEndpoint("http://127.0.0.1:2375")
+                 .version("1.15")
+                 .defaultEndpoint("https://127.0.0.1:2376")
                  .defaultProperties(DockerApiMetadata.defaultProperties())
                  .view(typeToken(ComputeServiceContext.class))
                  .defaultModules(ImmutableSet.<Class<? extends Module>>of(
-                         DockerHttpApiModule.class,
                          DockerParserModule.class,
+                         DockerHttpApiModule.class,
                          DockerComputeServiceContextModule.class));
       }
 

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e3c6a6f5/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
index 7b070fb..20df6a3 100644
--- a/docker/src/main/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadata.java
+++ b/docker/src/main/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadata.java
@@ -100,7 +100,6 @@ public class ContainerToNodeMetadata implements Function<Container, NodeMetadata
           Image image = images.get().get(imageId);
           builder.operatingSystem(image.getOperatingSystem());
       }
-
       return builder.build();
    }
 

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e3c6a6f5/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
index 551c441..7e9de03 100644
--- a/docker/src/main/java/org/jclouds/docker/compute/functions/ImageToImage.java
+++ b/docker/src/main/java/org/jclouds/docker/compute/functions/ImageToImage.java
@@ -19,6 +19,7 @@ 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;
@@ -44,6 +45,7 @@ public class ImageToImage implements Function<org.jclouds.docker.domain.Image, o
    @Override
    public Image apply(org.jclouds.docker.domain.Image from) {
       checkNotNull(from, "image");
+
       String description = checkNotNull(Iterables.getFirst(from.repoTags(), "image must have at least one repo tag"));
 
       OsFamily osFamily = osFamily().apply(description);

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e3c6a6f5/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
index 9568f33..355c617 100644
--- a/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java
+++ b/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java
@@ -215,7 +215,7 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable
       }
 
       /**
-       * @see DockerTemplateOptions#memory(int)
+       * @see DockerTemplateOptions#memory
        */
       public static DockerTemplateOptions memory(int memory) {
          DockerTemplateOptions options = new DockerTemplateOptions();

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e3c6a6f5/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
index fa83ffc..4cbb3b0 100644
--- a/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java
+++ b/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java
@@ -37,6 +37,7 @@ 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.ContainerSummary;
 import org.jclouds.docker.domain.HostConfig;
 import org.jclouds.docker.domain.Image;
 import org.jclouds.docker.options.ListContainerOptions;
@@ -78,7 +79,7 @@ public class DockerComputeServiceAdapter implements
 
       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();
+      String loginUserPassword = template.getImage().getDefaultCredentials().getOptionalPassword().or("password");
 
       DockerTemplateOptions templateOptions = DockerTemplateOptions.class.cast(template.getOptions());
       int[] inboundPorts = templateOptions.getInboundPorts();
@@ -119,10 +120,15 @@ public class DockerComputeServiceAdapter implements
          }
          containerConfigBuilder.volumes(volumes);
       }
+
+      if (templateOptions.getEnv().isPresent()) {
+         containerConfigBuilder.env(templateOptions.getEnv().get());
+      }
+
       Config containerConfig = containerConfigBuilder.build();
 
       logger.debug(">> creating new container with containerConfig(%s)", containerConfig);
-      Container container = api.getRemoteApi().createContainer(name, containerConfig);
+      Container container = api.getContainerApi().createContainer(name, containerConfig);
       logger.trace("<< container(%s)", container.id());
 
       HostConfig.Builder hostConfigBuilder = HostConfig.builder()
@@ -140,13 +146,13 @@ public class DockerComputeServiceAdapter implements
       }
       HostConfig hostConfig = hostConfigBuilder.build();
 
-      api.getRemoteApi().startContainer(container.id(), hostConfig);
-      container = api.getRemoteApi().inspectContainer(container.id());
+      api.getContainerApi().startContainer(container.id(), hostConfig);
+      container = api.getContainerApi().inspectContainer(container.id());
       if (container.state().exitCode() != 0) {
          destroyNode(container.id());
          throw new IllegalStateException(String.format("Container %s has not started correctly", container.id()));
       }
-      return new NodeAndInitialCredentials<Container>(container, container.id(),
+      return new NodeAndInitialCredentials(container, container.id(),
               LoginCredentials.builder().user(loginUser).password(loginUserPassword).build());
    }
 
@@ -156,21 +162,21 @@ public class DockerComputeServiceAdapter implements
       // 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());
+      hardware.add(new HardwareBuilder().ids("medium").hypervisor("lxc").name("medium").processor(new Processor(2, 1)).ram(2048).build());
+      hardware.add(new HardwareBuilder().ids("large").hypervisor("lxc").name("large").processor(new Processor(2, 1)).ram(3072).build());
       return hardware;
    }
 
    @Override
    public Set<Image> listImages() {
       Set<Image> images = Sets.newHashSet();
-      for (Image image : api.getRemoteApi().listImages()) {
+      for (Image image : api.getImageApi().listImages()) {
          // less efficient than just listImages but returns richer json that needs repoTags coming from listImages
-         Image inspected = api.getRemoteApi().inspectImage(image.id());
+         Image inspected = api.getImageApi().inspectImage(image.id());
          if (inspected.repoTags().isEmpty()) {
-            inspected = Image.create(inspected.id(), inspected.parent(), inspected.created(), inspected.container(),
-                  inspected.dockerVersion(), inspected.architecture(), inspected.os(), inspected.size(),
-                  inspected.virtualSize(), image.repoTags());
+            inspected = Image.create(inspected.id(), inspected.parent(), inspected.container(), inspected.created(),
+                    inspected.dockerVersion(), inspected.architecture(), inspected.os(), inspected.size(),
+                    inspected.virtualSize(), image.repoTags());
          }
          images.add(inspected);
       }
@@ -192,9 +198,9 @@ public class DockerComputeServiceAdapter implements
    @Override
    public Iterable<Container> listNodes() {
       Set<Container> containers = Sets.newHashSet();
-      for (Container container : api.getRemoteApi().listContainers(ListContainerOptions.Builder.all(true))) {
+      for (ContainerSummary containerSummary : api.getContainerApi().listContainers(ListContainerOptions.Builder.all(true))) {
          // less efficient than just listNodes but returns richer json
-         containers.add(api.getRemoteApi().inspectContainer(container.id()));
+         containers.add(api.getContainerApi().inspectContainer(containerSummary.id()));
       }
       return containers;
    }
@@ -203,7 +209,7 @@ public class DockerComputeServiceAdapter implements
    public Iterable<Container> listNodesByIds(final Iterable<String> ids) {
       Set<Container> containers = Sets.newHashSet();
       for (String id : ids) {
-         containers.add(api.getRemoteApi().inspectContainer(id));
+         containers.add(api.getContainerApi().inspectContainer(id));
       }
       return containers;
    }
@@ -215,18 +221,18 @@ public class DockerComputeServiceAdapter implements
 
    @Override
    public Container getNode(String id) {
-      return api.getRemoteApi().inspectContainer(id);
+      return api.getContainerApi().inspectContainer(id);
    }
 
    @Override
    public void destroyNode(String id) {
-      api.getRemoteApi().removeContainer(id, RemoveContainerOptions.Builder.force(true));
+      api.getContainerApi().removeContainer(id, RemoveContainerOptions.Builder.force(true));
    }
 
    @Override
    public void rebootNode(String id) {
-      api.getRemoteApi().stopContainer(id);
-      api.getRemoteApi().startContainer(id);
+      api.getContainerApi().stopContainer(id);
+      api.getContainerApi().startContainer(id);
    }
 
    @Override

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e3c6a6f5/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
index e6da554..ced396c 100644
--- a/docker/src/main/java/org/jclouds/docker/config/DockerHttpApiModule.java
+++ b/docker/src/main/java/org/jclouds/docker/config/DockerHttpApiModule.java
@@ -16,8 +16,14 @@
  */
 package org.jclouds.docker.config;
 
+import java.security.KeyStore;
+
+import javax.net.ssl.SSLContext;
+
 import org.jclouds.docker.DockerApi;
 import org.jclouds.docker.handlers.DockerErrorHandler;
+import org.jclouds.docker.suppliers.KeyStoreSupplier;
+import org.jclouds.docker.suppliers.SSLContextWithKeysSupplier;
 import org.jclouds.http.HttpErrorHandler;
 import org.jclouds.http.annotation.ClientError;
 import org.jclouds.http.annotation.Redirection;
@@ -25,6 +31,9 @@ import org.jclouds.http.annotation.ServerError;
 import org.jclouds.rest.ConfiguresHttpApi;
 import org.jclouds.rest.config.HttpApiModule;
 
+import com.google.common.base.Supplier;
+import com.google.inject.TypeLiteral;
+
 /**
  * Configures the Docker connection.
  */
@@ -37,4 +46,18 @@ public class DockerHttpApiModule extends HttpApiModule<DockerApi> {
       bind(HttpErrorHandler.class).annotatedWith(ClientError.class).to(DockerErrorHandler.class);
       bind(HttpErrorHandler.class).annotatedWith(ServerError.class).to(DockerErrorHandler.class);
    }
+
+   /**
+    * This configures SSL certificate authentication when the Docker daemon is set to use an encrypted TCP socket
+    */
+   @Override
+   protected void configure() {
+      super.configure();
+      bind(new TypeLiteral<Supplier<SSLContext>>() {
+      }).to(new TypeLiteral<SSLContextWithKeysSupplier>() {
+      });
+      bind(new TypeLiteral<Supplier<KeyStore>>() {
+      }).to(new TypeLiteral<KeyStoreSupplier>() {
+      });
+   }
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e3c6a6f5/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
index 396ab2f..bef8969 100644
--- a/docker/src/main/java/org/jclouds/docker/domain/Config.java
+++ b/docker/src/main/java/org/jclouds/docker/domain/Config.java
@@ -123,6 +123,7 @@ public abstract class Config {
       private List<String> entrypoint = ImmutableList.of();
       private boolean networkDisabled;
       private List<String> onBuild = ImmutableList.of();
+      private Map<String, String> restartPolicy = ImmutableMap.of();
 
       public Builder hostname(String hostname) {
          this.hostname = hostname;
@@ -239,6 +240,11 @@ public abstract class Config {
          return this;
       }
 
+      public Builder restartPolicy(Map<String, String> restartPolicy) {
+         this.restartPolicy = ImmutableMap.copyOf(restartPolicy);
+         return this;
+      }
+
       public Config build() {
          return Config.create(hostname, domainname, user, memory, memorySwap, cpuShares, attachStdin, attachStdout,
                attachStderr, exposedPorts, tty, openStdin, stdinOnce, env, cmd, dns, image, volumes, volumesFrom,

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e3c6a6f5/docker/src/main/java/org/jclouds/docker/domain/ContainerSummary.java
----------------------------------------------------------------------
diff --git a/docker/src/main/java/org/jclouds/docker/domain/ContainerSummary.java b/docker/src/main/java/org/jclouds/docker/domain/ContainerSummary.java
new file mode 100644
index 0000000..17d0064
--- /dev/null
+++ b/docker/src/main/java/org/jclouds/docker/domain/ContainerSummary.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.docker.domain;
+
+import static org.jclouds.docker.internal.NullSafeCopies.copyOf;
+import java.util.List;
+
+import org.jclouds.json.SerializedNames;
+
+import com.google.auto.value.AutoValue;
+
+// TODO it may be redundant (we already have Container value class)
+@AutoValue
+public abstract class ContainerSummary {
+
+   public abstract String id();
+
+   public abstract List<String> names();
+
+   public abstract String created();
+
+   public abstract String image();
+
+   public abstract String command();
+
+   public abstract List<Port> ports();
+
+   public abstract String status();
+
+   @SerializedNames({"Id", "Names", "Created", "Image", "Command", "Ports", "Status"})
+   public static ContainerSummary create(String id, List<String> names, String created, String image, String command, List<Port> ports, String status) {
+      return new AutoValue_ContainerSummary(id, copyOf(names), created, image, command, copyOf(ports), status);
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e3c6a6f5/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
index 757157c..630be7c 100644
--- a/docker/src/main/java/org/jclouds/docker/domain/HostConfig.java
+++ b/docker/src/main/java/org/jclouds/docker/domain/HostConfig.java
@@ -35,7 +35,7 @@ public abstract class HostConfig {
 
    public abstract List<String> binds();
 
-   public abstract Map<String, String> lxcConf();
+   public abstract List<Map<String, String>> lxcConf();
 
    public abstract boolean privileged();
 
@@ -53,7 +53,7 @@ public abstract class HostConfig {
 
    @SerializedNames({ "ContainerIDFile", "Binds", "LxcConf", "Privileged", "Dns", "DnsSearch", "PortBindings",
            "Links", "PublishAllPorts", "VolumesFrom" })
-   public static HostConfig create(String containerIDFile, List<String> binds, Map<String, String> lxcConf,
+   public static HostConfig create(String containerIDFile, List<String> binds, List<Map<String, String>> lxcConf,
          boolean privileged, String dns, String dnsSearch, Map<String, List<Map<String, String>>> portBindings,
          List<String> links, boolean publishAllPorts, List<String> volumesFrom) {
       return new AutoValue_HostConfig(containerIDFile, copyOf(binds), copyOf(lxcConf), privileged, dns, dnsSearch,
@@ -72,7 +72,7 @@ public abstract class HostConfig {
 
       private String containerIDFile;
       private List<String> binds = Lists.newArrayList();
-      private Map<String, String> lxcConf = Maps.newLinkedHashMap();
+      private List<Map<String, String>> lxcConf = Lists.newArrayList();
       private boolean privileged;
       private String dns;
       private String dnsSearch;
@@ -91,8 +91,8 @@ public abstract class HostConfig {
          return this;
       }
 
-      public Builder lxcConf(Map<String, String> lxcConf) {
-         this.lxcConf.putAll(checkNotNull(lxcConf, "lxcConf"));
+      public Builder lxcConf(List<Map<String, String>> lxcConf) {
+         this.lxcConf.addAll(checkNotNull(lxcConf, "lxcConf"));
          return this;
       }
 

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e3c6a6f5/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
index 99b2594..c80e448 100644
--- a/docker/src/main/java/org/jclouds/docker/domain/Image.java
+++ b/docker/src/main/java/org/jclouds/docker/domain/Image.java
@@ -48,10 +48,11 @@ public abstract class Image {
    public abstract List<String> repoTags();
 
    @SerializedNames({ "Id", "Parent", "Created", "Container", "DockerVersion", "Architecture", "Os", "Size",
-         "VirtualSize", "RepoTags", "Architecture" })
+         "VirtualSize", "RepoTags" })
    public static Image create(String id, String parent, String created, String container, String dockerVersion,
          String architecture, String os, long size, long virtualSize, List<String> repoTags) {
       return new AutoValue_Image(id, parent, created, container, dockerVersion, architecture, os, size, virtualSize,
             copyOf(repoTags));
    }
+
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e3c6a6f5/docker/src/main/java/org/jclouds/docker/domain/Info.java
----------------------------------------------------------------------
diff --git a/docker/src/main/java/org/jclouds/docker/domain/Info.java b/docker/src/main/java/org/jclouds/docker/domain/Info.java
new file mode 100644
index 0000000..596c0a5
--- /dev/null
+++ b/docker/src/main/java/org/jclouds/docker/domain/Info.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.docker.domain;
+
+import org.jclouds.json.SerializedNames;
+
+import com.google.auto.value.AutoValue;
+
+@AutoValue
+public abstract class Info {
+
+   public abstract int containers();
+
+   public abstract int images();
+
+   public abstract String driver();
+
+   public abstract String executionDriver();
+
+   public abstract String kernelVersion();
+
+   public abstract int debug();
+
+   public abstract int nFd();
+
+   public abstract int nGoroutines();
+
+   public abstract int nEventsListener();
+
+   public abstract String initPath();
+
+   public abstract String indexServerAddress();
+
+   public abstract int memoryLimit();
+
+   public abstract int swapLimit();
+
+   public abstract int iPv4Forwarding();
+
+   @SerializedNames(
+           {"Containers", "Images", "Driver", "ExecutionDriver", "KernelVersion", "Debug", "NFd", "NGoroutines",
+                   "NEventsListener", "InitPath", "IndexServerAddress", "MemoryLimit", "SwapLimit", "IPv4Forwarding"})
+   public static Info create(int containers, int images, String driver, String executionDriver, String kernelVersion, int debug,
+                             int nFd, int nGoroutines, int nEventsListener, String initPath, String indexServerAddress,
+                             int memoryLimit, int swapLimit, int iPv4Forwarding) {
+      return new AutoValue_Info(containers, images, driver, executionDriver, kernelVersion, debug, nFd, nGoroutines,
+              nEventsListener, initPath, indexServerAddress, memoryLimit, swapLimit, iPv4Forwarding);
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e3c6a6f5/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
index ee0ba19..7cf7960 100644
--- a/docker/src/main/java/org/jclouds/docker/domain/Version.java
+++ b/docker/src/main/java/org/jclouds/docker/domain/Version.java
@@ -22,6 +22,9 @@ import com.google.auto.value.AutoValue;
 
 @AutoValue
 public abstract class Version {
+
+   public abstract String apiVersion();
+
    public abstract String arch();
 
    public abstract String gitCommit();
@@ -34,9 +37,9 @@ public abstract class Version {
 
    public abstract String version();
 
-   @SerializedNames({ "Arch", "GitCommit", "GoVersion", "KernelVersion", "Os", "Version" })
-   public static Version create(String arch, String gitCommit, String goVersion, String kernelVersion, String os,
-         String version) {
-      return new AutoValue_Version(arch, gitCommit, goVersion, kernelVersion, os, version);
+   @SerializedNames({ "ApiVersion", "Arch", "GitCommit", "GoVersion", "KernelVersion", "Os", "Version" })
+   public static Version create(String apiVersion, String arch, String gitCommit, String goVersion,
+                                String kernelVersion, String os, String version) {
+      return new AutoValue_Version(apiVersion, arch, gitCommit, goVersion, kernelVersion, os, version);
    }
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e3c6a6f5/docker/src/main/java/org/jclouds/docker/features/ContainerApi.java
----------------------------------------------------------------------
diff --git a/docker/src/main/java/org/jclouds/docker/features/ContainerApi.java b/docker/src/main/java/org/jclouds/docker/features/ContainerApi.java
new file mode 100644
index 0000000..60f7749
--- /dev/null
+++ b/docker/src/main/java/org/jclouds/docker/features/ContainerApi.java
@@ -0,0 +1,157 @@
+/*
+ * 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.util.List;
+
+import javax.inject.Named;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+
+import org.jclouds.Fallbacks;
+import org.jclouds.docker.domain.Config;
+import org.jclouds.docker.domain.Container;
+import org.jclouds.docker.domain.ContainerSummary;
+import org.jclouds.docker.domain.HostConfig;
+import org.jclouds.docker.domain.Image;
+import org.jclouds.docker.options.CommitOptions;
+import org.jclouds.docker.options.ListContainerOptions;
+import org.jclouds.docker.options.RemoveContainerOptions;
+import org.jclouds.rest.annotations.BinderParam;
+import org.jclouds.rest.annotations.Fallback;
+import org.jclouds.rest.binders.BindToJsonPayload;
+
+@Consumes(MediaType.APPLICATION_JSON)
+public interface ContainerApi {
+
+   /**
+    * List all running containers
+    *
+    * @return a set of containers
+    */
+   @Named("containers:list")
+   @GET
+   @Path("/containers/json")
+   @Fallback(Fallbacks.EmptyListOnNotFoundOr404.class)
+   List<ContainerSummary> 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.EmptyListOnNotFoundOr404.class)
+   List<ContainerSummary> 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);
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e3c6a6f5/docker/src/main/java/org/jclouds/docker/features/ImageApi.java
----------------------------------------------------------------------
diff --git a/docker/src/main/java/org/jclouds/docker/features/ImageApi.java b/docker/src/main/java/org/jclouds/docker/features/ImageApi.java
new file mode 100644
index 0000000..95c963c
--- /dev/null
+++ b/docker/src/main/java/org/jclouds/docker/features/ImageApi.java
@@ -0,0 +1,110 @@
+/*
+ * 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.InputStream;
+import java.util.List;
+
+import javax.inject.Named;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.core.MediaType;
+
+import org.jclouds.Fallbacks;
+import org.jclouds.docker.domain.Image;
+import org.jclouds.docker.options.CreateImageOptions;
+import org.jclouds.docker.options.DeleteImageOptions;
+import org.jclouds.docker.options.ListImageOptions;
+import org.jclouds.rest.annotations.Fallback;
+
+@Consumes(MediaType.APPLICATION_JSON)
+public interface ImageApi {
+
+   /**
+    * List images
+    *
+    * @return the images available.
+    */
+   @Named("images:list")
+   @GET
+   @Path("/images/json")
+   @Fallback(Fallbacks.EmptyListOnNotFoundOr404.class)
+   List<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.EmptyListOnNotFoundOr404.class)
+   List<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")
+   @Fallback(Fallbacks.VoidOnNotFoundOr404.class)
+   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);
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e3c6a6f5/docker/src/main/java/org/jclouds/docker/features/MiscApi.java
----------------------------------------------------------------------
diff --git a/docker/src/main/java/org/jclouds/docker/features/MiscApi.java b/docker/src/main/java/org/jclouds/docker/features/MiscApi.java
new file mode 100644
index 0000000..30cd1f3
--- /dev/null
+++ b/docker/src/main/java/org/jclouds/docker/features/MiscApi.java
@@ -0,0 +1,72 @@
+/*
+ * 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.InputStream;
+
+import javax.inject.Named;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.MediaType;
+
+import org.jclouds.docker.domain.Info;
+import org.jclouds.docker.domain.Version;
+import org.jclouds.docker.options.BuildOptions;
+import org.jclouds.io.Payload;
+import org.jclouds.rest.annotations.Headers;
+
+@Consumes(MediaType.APPLICATION_JSON)
+public interface MiscApi {
+
+   /**
+    * Get the information of the current docker version.
+    *
+    * @return The information of the current docker version.
+    */
+   @Named("version")
+   @GET
+   @Path("/version")
+   Version getVersion();
+
+   /**
+    * Get the information of the current docker version.
+    *
+    * @return The information of the current docker version.
+    */
+   @Named("info")
+   @GET
+   @Path("/info")
+   Info getInfo();
+
+
+   /**
+    * 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);
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e3c6a6f5/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
deleted file mode 100644
index 785eb20..0000000
--- a/docker/src/main/java/org/jclouds/docker/features/RemoteApi.java
+++ /dev/null
@@ -1,256 +0,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.
- */
-package org.jclouds.docker.features;
-
-import java.io.Closeable;
-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.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);
-}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e3c6a6f5/docker/src/main/java/org/jclouds/docker/suppliers/KeyStoreSupplier.java
----------------------------------------------------------------------
diff --git a/docker/src/main/java/org/jclouds/docker/suppliers/KeyStoreSupplier.java b/docker/src/main/java/org/jclouds/docker/suppliers/KeyStoreSupplier.java
new file mode 100644
index 0000000..e8643cd
--- /dev/null
+++ b/docker/src/main/java/org/jclouds/docker/suppliers/KeyStoreSupplier.java
@@ -0,0 +1,130 @@
+/*
+ * 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.suppliers;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Throwables.propagate;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.KeySpec;
+import java.util.Collection;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.jclouds.crypto.Crypto;
+import org.jclouds.crypto.Pems;
+import org.jclouds.domain.Credentials;
+import org.jclouds.location.Provider;
+import org.jclouds.rest.AuthorizationException;
+
+import com.google.common.base.Charsets;
+import com.google.common.base.Supplier;
+import com.google.common.io.ByteSource;
+
+@Singleton
+public class KeyStoreSupplier implements Supplier<KeyStore> {
+   private final Crypto crypto;
+   private final Supplier<Credentials> creds;
+
+   @Inject
+   KeyStoreSupplier(Crypto crypto, @Provider Supplier<Credentials> creds) {
+      this.crypto = crypto;
+      this.creds = creds;
+   }
+
+   @Override
+   public KeyStore get() {
+      Credentials currentCreds = checkNotNull(creds.get(), "credential supplier returned null");
+      String cert = checkNotNull(currentCreds.identity, "credential supplier returned null identity (should be cert)");
+      String keyStorePassword = checkNotNull(currentCreds.credential,
+            "credential supplier returned null credential (should be keyStorePassword)");
+      try {
+         KeyStore keyStore = KeyStore.getInstance("PKCS12");
+
+         File certFile = new File(checkNotNull(cert));
+         if (certFile.isFile()) { // cert is path to pkcs12 file
+            FileInputStream stream = new FileInputStream(certFile);
+            try {
+               keyStore.load(stream, keyStorePassword.toCharArray());
+            } finally {
+               stream.close();
+            }
+         } else { // cert is PEM encoded, containing private key and certs
+
+            // split in private key and certs
+            int privateKeyBeginIdx = cert.indexOf("-----BEGIN PRIVATE KEY");
+            int privateKeyEndIdx = cert.indexOf("-----END PRIVATE KEY");
+            if (privateKeyBeginIdx != -1) {
+               String pemPrivateKey = cert.substring(privateKeyBeginIdx, privateKeyEndIdx + 26);
+
+               StringBuilder pemCerts = new StringBuilder();
+               int certsBeginIdx = 0;
+
+               do {
+                  certsBeginIdx = cert.indexOf("-----BEGIN CERTIFICATE", certsBeginIdx);
+
+                  if (certsBeginIdx >= 0) {
+                     int certsEndIdx = cert.indexOf("-----END CERTIFICATE", certsBeginIdx) + 26;
+                     pemCerts.append(cert.substring(certsBeginIdx, certsEndIdx));
+                     certsBeginIdx = certsEndIdx;
+                  }
+               } while (certsBeginIdx != -1);
+
+               // parse private key
+               KeySpec keySpec = Pems.privateKeySpec(ByteSource.wrap(pemPrivateKey.getBytes(Charsets.UTF_8)));
+               PrivateKey privateKey = crypto.rsaKeyFactory().generatePrivate(keySpec);
+
+               // populate keystore with private key and certs
+               CertificateFactory cf = CertificateFactory.getInstance("X.509");
+               @SuppressWarnings("unchecked")
+               Collection<Certificate> certs = (Collection<Certificate>) cf.generateCertificates(new ByteArrayInputStream(
+                       pemCerts.toString().getBytes(Charsets.UTF_8)));
+               keyStore.load(null);
+               keyStore.setKeyEntry("dummy", privateKey, keyStorePassword.toCharArray(),
+                       certs.toArray(new Certificate[0]));
+            } else {
+               throw new AuthorizationException();
+            }
+         }
+         return keyStore;
+      } catch (NoSuchAlgorithmException e) {
+         throw propagate(e);
+      } catch (KeyStoreException e) {
+         throw propagate(e);
+      } catch (CertificateException e) {
+         throw propagate(e);
+      } catch (FileNotFoundException e) {
+         throw propagate(e);
+      } catch (IOException e) {
+         throw propagate(e);
+      } catch (InvalidKeySpecException e) {
+         throw propagate(e);
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e3c6a6f5/docker/src/main/java/org/jclouds/docker/suppliers/SSLContextWithKeysSupplier.java
----------------------------------------------------------------------
diff --git a/docker/src/main/java/org/jclouds/docker/suppliers/SSLContextWithKeysSupplier.java b/docker/src/main/java/org/jclouds/docker/suppliers/SSLContextWithKeysSupplier.java
new file mode 100644
index 0000000..59695d3
--- /dev/null
+++ b/docker/src/main/java/org/jclouds/docker/suppliers/SSLContextWithKeysSupplier.java
@@ -0,0 +1,77 @@
+/*
+ * 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.suppliers;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Throwables.propagate;
+import java.security.KeyManagementException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.UnrecoverableKeyException;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
+
+import org.jclouds.domain.Credentials;
+import org.jclouds.http.HttpUtils;
+import org.jclouds.http.config.SSLModule.TrustAllCerts;
+import org.jclouds.location.Provider;
+
+import com.google.common.base.Supplier;
+
+@Singleton
+public class SSLContextWithKeysSupplier implements Supplier<SSLContext> {
+   private final Supplier<KeyStore> keyStore;
+   private final TrustManager[] trustManager;
+   private final Supplier<Credentials> creds;
+
+   @Inject
+   SSLContextWithKeysSupplier(Supplier<KeyStore> keyStore, @Provider Supplier<Credentials> creds, HttpUtils utils,
+         TrustAllCerts trustAllCerts) {
+      this.keyStore = keyStore;
+      this.trustManager = utils.trustAllCerts() ? new TrustManager[] { trustAllCerts } : null;
+      this.creds = creds;
+   }
+
+   @Override
+   public SSLContext get() {
+      Credentials currentCreds = checkNotNull(creds.get(), "credential supplier returned null");
+      String keyStorePassword = checkNotNull(currentCreds.credential,
+            "credential supplier returned null credential (should be keyStorePassword)");
+      KeyManagerFactory kmf;
+      try {
+         kmf = KeyManagerFactory.getInstance("SunX509");
+         kmf.init(keyStore.get(), keyStorePassword.toCharArray());
+         SSLContext sc = SSLContext.getInstance("TLS");
+         sc.init(kmf.getKeyManagers(), trustManager, new SecureRandom());
+         return sc;
+      } catch (NoSuchAlgorithmException e) {
+         throw propagate(e);
+      } catch (UnrecoverableKeyException e) {
+         throw propagate(e);
+      } catch (KeyStoreException e) {
+         throw propagate(e);
+      } catch (KeyManagementException e) {
+         throw propagate(e);
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e3c6a6f5/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
index 54f61a1..9aca266 100644
--- a/docker/src/test/java/org/jclouds/docker/compute/BaseDockerApiLiveTest.java
+++ b/docker/src/test/java/org/jclouds/docker/compute/BaseDockerApiLiveTest.java
@@ -31,6 +31,7 @@ import org.jboss.shrinkwrap.api.asset.ClassLoaderAsset;
 import org.jboss.shrinkwrap.api.exporter.TarExporter;
 import org.jclouds.Constants;
 import org.jclouds.apis.BaseApiLiveTest;
+import org.jclouds.compute.config.ComputeServiceProperties;
 import org.jclouds.docker.DockerApi;
 import org.jclouds.io.Payload;
 import org.jclouds.io.Payloads;
@@ -58,6 +59,7 @@ public class BaseDockerApiLiveTest extends BaseApiLiveTest<DockerApi> {
       Properties overrides = super.setupProperties();
       overrides.setProperty(Constants.PROPERTY_MAX_RETRIES, "15");
       overrides.setProperty("jclouds.ssh.retry-auth", "true");
+      overrides.setProperty(ComputeServiceProperties.IMAGE_LOGIN_USER, "root:password");
       return overrides;
    }
 

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e3c6a6f5/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
index b64dace..c3da551 100644
--- a/docker/src/test/java/org/jclouds/docker/compute/DockerComputeServiceAdapterLiveTest.java
+++ b/docker/src/test/java/org/jclouds/docker/compute/DockerComputeServiceAdapterLiveTest.java
@@ -22,17 +22,24 @@ import static org.testng.Assert.assertNotNull;
 import java.util.Properties;
 import java.util.Random;
 
+import org.jclouds.compute.ComputeService;
 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.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.options.CreateImageOptions;
+import org.jclouds.docker.options.DeleteImageOptions;
 import org.jclouds.sshj.config.SshjSshClientModule;
 import org.testng.annotations.AfterGroups;
+import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
 import com.google.inject.Injector;
@@ -41,27 +48,47 @@ import com.google.inject.Module;
 @Test(groups = "live", singleThreaded = true, testName = "DockerComputeServiceAdapterLiveTest")
 public class DockerComputeServiceAdapterLiveTest extends BaseDockerApiLiveTest {
 
+   private static final String SSHABLE_IMAGE = "tutum/ubuntu";
+   private static final String SSHABLE_IMAGE_TAG = "trusty";
+   private Image defaultImage;
+
    private DockerComputeServiceAdapter adapter;
    private TemplateBuilder templateBuilder;
+   private ComputeService computeService;
    private NodeAndInitialCredentials<Container> guest;
 
+   @BeforeClass
+   protected void init() {
+      super.initialize();
+      String imageName = SSHABLE_IMAGE + ":" + SSHABLE_IMAGE_TAG;
+      Image image = api.getImageApi().inspectImage(imageName);
+      if (image == null) {
+         CreateImageOptions options = CreateImageOptions.Builder.fromImage(SSHABLE_IMAGE).tag(SSHABLE_IMAGE_TAG);
+         api.getImageApi().createImage(options);
+      }
+      defaultImage = api.getImageApi().inspectImage(imageName);
+      assertNotNull(defaultImage);
+   }
+
    @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);
+      computeService = injector.getInstance(ComputeService.class);
       return injector.getInstance(DockerApi.class);
    }
 
    public void testCreateNodeWithGroupEncodedIntoNameThenStoreCredentials() {
       String group = "foo";
-      String name = "container-" + new Random().nextInt();
+      String name = "container" + new Random().nextInt();
 
-      Template template = templateBuilder.smallest()
-              .osDescriptionMatches("jclouds/default:latest").build();
+      Template template = templateBuilder.imageId(defaultImage.id()).build();
 
+      DockerTemplateOptions options = template.getOptions().as(DockerTemplateOptions.class);
+      options.env(ImmutableList.of("ROOT_PASS=password"));
       guest = adapter.createNodeWithGroupEncodedIntoName(group, name, template);
-      assertEquals(guest.getNodeId(), guest.getNode().id() + "");
+      assertEquals(guest.getNodeId(), guest.getNode().id());
    }
 
    public void testListHardwareProfiles() {
@@ -78,6 +105,9 @@ public class DockerComputeServiceAdapterLiveTest extends BaseDockerApiLiveTest {
       if (guest != null) {
          adapter.destroyNode(guest.getNode().id() + "");
       }
+      if (defaultImage != null) {
+         api.getImageApi().deleteImage(defaultImage.id(), DeleteImageOptions.Builder.force(true));
+      }
       super.tearDown();
    }
 

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e3c6a6f5/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
index cc460c3..1e024e4 100644
--- a/docker/src/test/java/org/jclouds/docker/compute/DockerComputeServiceLiveTest.java
+++ b/docker/src/test/java/org/jclouds/docker/compute/DockerComputeServiceLiveTest.java
@@ -16,30 +16,67 @@
  */
 package org.jclouds.docker.compute;
 
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static java.lang.String.format;
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static java.util.logging.Logger.getAnonymousLogger;
+import static org.jclouds.compute.options.RunScriptOptions.Builder.nameTask;
+import static org.jclouds.compute.options.TemplateOptions.Builder.runAsRoot;
+import static org.jclouds.util.Predicates2.retry;
+import static org.testng.Assert.assertNotNull;
+
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.concurrent.TimeUnit;
+
+import org.jclouds.compute.ComputeService;
+import org.jclouds.compute.JettyStatements;
+import org.jclouds.compute.RunNodesException;
+import org.jclouds.compute.domain.ExecResponse;
 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.compute.internal.BaseComputeServiceContextLiveTest;
+import org.jclouds.compute.util.OpenSocketFinder;
+import org.jclouds.docker.DockerApi;
+import org.jclouds.docker.compute.options.DockerTemplateOptions;
+import org.jclouds.docker.domain.Container;
+import org.jclouds.docker.features.ImageApi;
+import org.jclouds.docker.options.CreateImageOptions;
+import org.jclouds.docker.options.DeleteImageOptions;
+import org.jclouds.domain.LoginCredentials;
+import org.jclouds.predicates.SocketOpen;
+import org.jclouds.scriptbuilder.domain.Statement;
+import org.jclouds.scriptbuilder.domain.Statements;
 import org.jclouds.sshj.config.SshjSshClientModule;
-import org.testng.Assert;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeGroups;
 import org.testng.annotations.Test;
 
-import com.google.common.base.Optional;
 import com.google.common.base.Predicate;
+import com.google.common.base.Stopwatch;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
+import com.google.common.net.HostAndPort;
 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 {
+public class DockerComputeServiceLiveTest extends BaseComputeServiceContextLiveTest {
 
-   private static final String DEFAULT_JCLOUDS_IMAGE = "jclouds/default";
+   private static final String SSHABLE_IMAGE = "tutum/ubuntu";
+   private static final String SSHABLE_IMAGE_TAG = "trusty";
    private Image defaultImage;
+   protected Template template;
+   protected Predicate<HostAndPort> socketTester;
+   protected OpenSocketFinder openSocketFinder;
+
+   protected ComputeService client;
 
    public DockerComputeServiceLiveTest() {
       provider = "docker";
@@ -50,93 +87,134 @@ public class DockerComputeServiceLiveTest extends BaseComputeServiceLiveTest {
       return new SshjSshClientModule();
    }
 
+   @BeforeGroups(groups = { "integration", "live" })
    @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();
+   public void setupContext() {
+      super.setupContext();
+      buildSocketTester();
    }
 
    @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
-   }
+   protected void initializeContext() {
+      super.initializeContext();
+      client = view.getComputeService();
 
-   @Override
-   protected void checkUserMetadataContains(NodeMetadata node, ImmutableMap<String, String> userMetadata) {
-      // Docker does not support user metadata
+      String imageName = SSHABLE_IMAGE + ":" + SSHABLE_IMAGE_TAG;
+      org.jclouds.docker.domain.Image image = imageApi().inspectImage(imageName);
+      if (image == null) {
+         CreateImageOptions options = CreateImageOptions.Builder.fromImage(SSHABLE_IMAGE).tag(SSHABLE_IMAGE_TAG);
+         imageApi().createImage(options);
+      }
+      image = imageApi().inspectImage(imageName);
+      defaultImage = client.getImage(image.id());
+      assertNotNull(defaultImage);
    }
 
+   @AfterClass
    @Override
-   public void testCreateAndRunAService() throws Exception {
-      // Docker does not support blockOnPort
+   protected void tearDownContext() {
+      super.tearDownContext();
+      if (defaultImage != null) {
+         imageApi().deleteImage(SSHABLE_IMAGE + ":" + SSHABLE_IMAGE_TAG, DeleteImageOptions.Builder.force(true));
+      }
    }
 
-   @Override
-   @Test(enabled = true, dependsOnMethods = { "testCompareSizes" })
-   public void testAScriptExecutionAfterBootWithBasicTemplate() throws Exception {
-      super.testAScriptExecutionAfterBootWithBasicTemplate();
+   private ImageApi imageApi() {
+      return client.getContext().unwrapApi(DockerApi.class).getImageApi();
    }
 
-   @Override
-   @Test(enabled = true, dependsOnMethods = "testReboot", expectedExceptions = UnsupportedOperationException.class)
-   public void testSuspendResume() throws Exception {
-      super.testSuspendResume();
-   }
+   protected Template buildTemplate(TemplateBuilder templateBuilder) {
 
-   @Override
-   @Test(enabled = true, dependsOnMethods = "testSuspendResume")
-   public void testGetNodesWithDetails() throws Exception {
-      super.testGetNodesWithDetails();
+      String imageName = SSHABLE_IMAGE + ":" + SSHABLE_IMAGE_TAG;
+      org.jclouds.docker.domain.Image image = imageApi().inspectImage(imageName);
+      if (image == null) {
+         CreateImageOptions options = CreateImageOptions.Builder.fromImage(SSHABLE_IMAGE).tag(SSHABLE_IMAGE_TAG);
+         imageApi().createImage(options);
+      }
+      image = imageApi().inspectImage(imageName);
+      defaultImage = client.getImage(image.id());
+
+
+      DockerTemplateOptions options = new DockerTemplateOptions();
+      options.env(ImmutableList.of("ROOT_PASS=password"));
+      options.overrideLoginCredentials(LoginCredentials.builder().identity("root").credential("password").build());
+      template = templateBuilder.imageId(defaultImage.getId()).options(options).build();
+      return template;
+   }
+
+   protected void createAndRunAServiceInGroup(String group) throws RunNodesException {
+      // note that some cloud providers do not support mixed case tag names
+      ImmutableMap<String, String> userMetadata = ImmutableMap.of("test", group);
+      ImmutableSet<String> tags = ImmutableSet.of(group);
+      Stopwatch watch = Stopwatch.createStarted();
+      template = buildTemplate(client.templateBuilder());
+      template.getOptions().inboundPorts(22, 8080).blockOnPort(22, 300).userMetadata(userMetadata).tags(tags);
+      NodeMetadata node = getOnlyElement(client.createNodesInGroup(group, 1, template));
+      long createSeconds = watch.elapsed(TimeUnit.SECONDS);
+      final String nodeId = node.getId();
+      //checkUserMetadataContains(node, userMetadata);
+      //checkTagsInNodeEquals(node, tags);
+      getAnonymousLogger().info(
+              format("<< available node(%s) os(%s) in %ss", node.getId(), node.getOperatingSystem(), createSeconds));
+      watch.reset().start();
+      client.runScriptOnNode(nodeId, JettyStatements.install(), nameTask("configure-jetty"));
+      long configureSeconds = watch.elapsed(TimeUnit.SECONDS);
+      getAnonymousLogger().info(
+              format(
+                      "<< configured node(%s) with %s and jetty %s in %ss",
+                      nodeId,
+                      exec(nodeId, "java -fullversion"),
+                      exec(nodeId, JettyStatements.version()), configureSeconds));
+      trackProcessOnNode(JettyStatements.start(), "start jetty", node);
+      client.runScriptOnNode(nodeId, JettyStatements.stop(), runAsRoot(false).wrapInInitScript(false));
+      trackProcessOnNode(JettyStatements.start(), "start jetty", node);
+   }
+
+   protected void trackProcessOnNode(Statement process, String processName, NodeMetadata node) {
+      ServiceStats stats = new ServiceStats();
+      Stopwatch watch = Stopwatch.createStarted();
+      ExecResponse exec = client.runScriptOnNode(node.getId(), process, runAsRoot(false).wrapInInitScript(false));
+      stats.backgroundProcessMilliseconds = watch.elapsed(TimeUnit.MILLISECONDS);
+
+      Container container = client.getContext().unwrapApi(DockerApi.class).getContainerApi().inspectContainer(node.getId());
+      Map<String, List<Map<String, String>>> ports = container.networkSettings().ports();
+      int port = Integer.parseInt(getOnlyElement(ports.get("8080/tcp")).get("HostPort"));
+
+      watch.reset().start();
+      HostAndPort socket;
+      try {
+         socket = openSocketFinder.findOpenSocketOnNode(node, port, 600, TimeUnit.SECONDS);
+      } catch (NoSuchElementException e) {
+         throw new NoSuchElementException(format("%s%n%s%s", e.getMessage(), exec.getOutput(), exec.getError()));
+      }
+      stats.socketOpenMilliseconds = watch.elapsed(TimeUnit.MILLISECONDS);
+      getAnonymousLogger().info(format("<< %s on node(%s)[%s] %s", processName, node.getId(), socket, stats));
    }
 
-   @Override
-   @Test(enabled = true, dependsOnMethods = "testSuspendResume")
-   public void testListNodes() throws Exception {
-      super.testListNodes();
-   }
+   static class ServiceStats {
+      long backgroundProcessMilliseconds;
+      long socketOpenMilliseconds;
 
-   @Override
-   @Test(enabled = true, dependsOnMethods = "testSuspendResume")
-   public void testListNodesByIds() throws Exception {
-      super.testListNodesByIds();
+      @Override
+      public String toString() {
+         return format("[backgroundProcessMilliseconds=%s, socketOpenMilliseconds=%s]",
+                 backgroundProcessMilliseconds, socketOpenMilliseconds);
+      }
    }
 
-   @Override
-   @Test(enabled = true, dependsOnMethods = { "testListNodes", "testGetNodesWithDetails", "testListNodesByIds" })
-   public void testDestroyNodes() {
-      super.testDestroyNodes();
+   protected String exec(final String nodeId, String command) {
+      return exec(nodeId, Statements.exec(command));
    }
 
-   @Test(enabled = true, expectedExceptions = NullPointerException.class)
-   public void testCorrectExceptionRunningNodesNotFound() throws Exception {
-      super.testCorrectExceptionRunningNodesNotFound();
+   protected String exec(final String nodeId, Statement command) {
+      return client.runScriptOnNode(nodeId, command, runAsRoot(false).wrapInInitScript(false)).getOutput().trim();
    }
 
-   @Test(enabled = true, expectedExceptions = NullPointerException.class)
-   public void testCorrectAuthException() throws Exception {
-      // Docker does not support authentication yet
-      super.testCorrectAuthException();
+   protected void buildSocketTester() {
+      SocketOpen socketOpen = view.utils().injector().getInstance(SocketOpen.class);
+      socketTester = retry(socketOpen, 60, 1, SECONDS);
+      // wait a maximum of 60 seconds for port 8080 to open.
+      openSocketFinder = context.utils().injector().getInstance(OpenSocketFinder.class);
    }
 
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e3c6a6f5/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
index 6e79685..94ee205 100644
--- a/docker/src/test/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadataTest.java
+++ b/docker/src/test/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadataTest.java
@@ -43,6 +43,7 @@ import org.jclouds.docker.domain.State;
 import org.jclouds.domain.Location;
 import org.jclouds.domain.LocationBuilder;
 import org.jclouds.domain.LocationScope;
+import org.jclouds.domain.LoginCredentials;
 import org.jclouds.providers.ProviderMetadata;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
@@ -59,6 +60,9 @@ import com.google.inject.Guice;
  */
 @Test(groups = "unit", testName = "ContainerToNodeMetadataTest")
 public class ContainerToNodeMetadataTest {
+
+   private LoginCredentials credentials;
+
    private ContainerToNodeMetadata function;
 
    private Container container;
@@ -164,6 +168,8 @@ public class ContainerToNodeMetadataTest {
          }
       };
 
+      credentials = LoginCredentials.builder().user("foo").password("bar").build();
+
       function = new ContainerToNodeMetadata(providerMetadata, toPortableStatus(), namingConvention, images, locations);
    }
 

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e3c6a6f5/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
index 0d35792..76d82ee 100644
--- a/docker/src/test/java/org/jclouds/docker/compute/functions/ImageToImageTest.java
+++ b/docker/src/test/java/org/jclouds/docker/compute/functions/ImageToImageTest.java
@@ -33,6 +33,7 @@ import com.google.common.collect.ImmutableList;
  */
 @Test(groups = "unit", testName = "ImageToImageTest")
 public class ImageToImageTest {
+
    private ImageToImage function;
 
    private org.jclouds.docker.domain.Image image;

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e3c6a6f5/docker/src/test/java/org/jclouds/docker/features/ContainerApiLiveTest.java
----------------------------------------------------------------------
diff --git a/docker/src/test/java/org/jclouds/docker/features/ContainerApiLiveTest.java b/docker/src/test/java/org/jclouds/docker/features/ContainerApiLiveTest.java
new file mode 100644
index 0000000..44edc57
--- /dev/null
+++ b/docker/src/test/java/org/jclouds/docker/features/ContainerApiLiveTest.java
@@ -0,0 +1,105 @@
+/*
+ * 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 static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+
+import org.jclouds.docker.compute.BaseDockerApiLiveTest;
+import org.jclouds.docker.domain.Config;
+import org.jclouds.docker.domain.Container;
+import org.jclouds.docker.domain.ContainerSummary;
+import org.jclouds.docker.domain.Image;
+import org.jclouds.docker.options.CreateImageOptions;
+import org.jclouds.docker.options.ListContainerOptions;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+
+@Test(groups = "live", testName = "RemoteApiLiveTest", singleThreaded = true)
+public class ContainerApiLiveTest extends BaseDockerApiLiveTest {
+
+   private Container container = null;
+   protected static final String BUSYBOX_IMAGE_TAG = "busybox:ubuntu-12.04";
+   protected Image image = null;
+
+   @BeforeClass
+   protected void init() {
+      if (api.getImageApi().inspectImage(BUSYBOX_IMAGE_TAG) == null) {
+         CreateImageOptions options = CreateImageOptions.Builder.fromImage(BUSYBOX_IMAGE_TAG);
+         InputStream createImageStream = api.getImageApi().createImage(options);
+         consumeStream(createImageStream);
+      }
+      image = api.getImageApi().inspectImage(BUSYBOX_IMAGE_TAG);
+      assertNotNull(image);
+   }
+
+   @AfterClass
+   protected void tearDown() {
+      if (image != null) {
+         api.getImageApi().deleteImage(BUSYBOX_IMAGE_TAG);
+      }
+   }
+
+   public void testCreateContainer() throws IOException, InterruptedException {
+      Config containerConfig = Config.builder().image(image.id())
+              .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.id());
+   }
+
+   @Test(dependsOnMethods = "testCreateContainer")
+   public void testStartContainer() throws IOException, InterruptedException {
+      api().startContainer(container.id());
+      assertTrue(api().inspectContainer(container.id()).state().running());
+   }
+
+   @Test(dependsOnMethods = "testStartContainer")
+   public void testStopContainer() {
+      api().stopContainer(container.id());
+      assertFalse(api().inspectContainer(container.id()).state().running());
+   }
+
+   @Test
+   public void testListContainers() {
+      List<ContainerSummary> containerSummaries = api().listContainers(ListContainerOptions.Builder.all(true));
+      for (ContainerSummary containerSummary : containerSummaries) {
+         assertNotNull(containerSummary.id());
+         assertNotNull(containerSummary.image());
+         assertFalse(containerSummary.names().isEmpty());
+      }
+   }
+
+   @Test(dependsOnMethods = "testStopContainer", expectedExceptions = NullPointerException.class)
+   public void testRemoveContainer() {
+      api().removeContainer(container.id());
+      assertFalse(api().inspectContainer(container.id()).state().running());
+   }
+
+   private ContainerApi api() {
+      return api.getContainerApi();
+   }
+}