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/c4ba0d04 Tree: http://git-wip-us.apache.org/repos/asf/jclouds-labs/tree/c4ba0d04 Diff: http://git-wip-us.apache.org/repos/asf/jclouds-labs/diff/c4ba0d04 Branch: refs/heads/1.8.x Commit: c4ba0d044fa7b19546ea174ff614d9dfcc1a95f2 Parents: 325ae5c Author: Andrea Turli <[email protected]> Authored: Thu Oct 2 18:08:24 2014 +0200 Committer: Adrian Cole <[email protected]> Committed: Mon Nov 3 19:28:26 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/c4ba0d04/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/c4ba0d04/docker/pom.xml ---------------------------------------------------------------------- diff --git a/docker/pom.xml b/docker/pom.xml index bf332db..fc5ce72 100644 --- a/docker/pom.xml +++ b/docker/pom.xml @@ -130,6 +130,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/c4ba0d04/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/c4ba0d04/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/c4ba0d04/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/c4ba0d04/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/c4ba0d04/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/c4ba0d04/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/c4ba0d04/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/c4ba0d04/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/c4ba0d04/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/c4ba0d04/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/c4ba0d04/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/c4ba0d04/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/c4ba0d04/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/c4ba0d04/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/c4ba0d04/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/c4ba0d04/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/c4ba0d04/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/c4ba0d04/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/c4ba0d04/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/c4ba0d04/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 f779b7f..38037d1 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; @@ -59,6 +60,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/c4ba0d04/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/c4ba0d04/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/c4ba0d04/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/c4ba0d04/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/c4ba0d04/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(); + } +}
