From 779551c40fc17c6ee39eba5c2a476d450dae1f8d Mon Sep 17 00:00:00 2001 From: Andrea Turli Date: Tue, 28 Jan 2014 10:42:43 +0100 Subject: [PATCH 01/81] [JCLOUDS-500] Initial commit for docker --- apis/docker/README.md | 48 ++ apis/docker/pom.xml | 143 ++++++ .../java/org/jclouds/docker/DockerApi.java | 37 ++ .../org/jclouds/docker/DockerApiMetadata.java | 98 ++++ .../binders/BindInputStreamToRequest.java | 68 +++ .../DockerComputeServiceContextModule.java | 58 +++ .../functions/ContainerToNodeMetadata.java | 126 +++++ .../compute/functions/ImageToImage.java | 98 ++++ .../compute/functions/StateToStatus.java | 38 ++ .../options/DockerTemplateOptions.java | 485 ++++++++++++++++++ .../strategy/DockerComputeServiceAdapter.java | 232 +++++++++ .../docker/config/DockerHttpApiModule.java | 40 ++ .../docker/config/DockerParserModule.java | 62 +++ .../docker/config/DockerProperties.java | 26 + .../org/jclouds/docker/domain/Config.java | 457 +++++++++++++++++ .../org/jclouds/docker/domain/Container.java | 389 ++++++++++++++ .../jclouds/docker/domain/ExposedPorts.java | 106 ++++ .../org/jclouds/docker/domain/HostConfig.java | 242 +++++++++ .../java/org/jclouds/docker/domain/Image.java | 239 +++++++++ .../docker/domain/NetworkSettings.java | 175 +++++++ .../java/org/jclouds/docker/domain/Port.java | 88 ++++ .../java/org/jclouds/docker/domain/State.java | 166 ++++++ .../org/jclouds/docker/domain/Version.java | 166 ++++++ .../jclouds/docker/features/RemoteApi.java | 272 ++++++++++ .../docker/features/internal/Archives.java | 60 +++ .../docker/handlers/DockerErrorHandler.java | 106 ++++ .../jclouds/docker/options/BuildOptions.java | 71 +++ .../jclouds/docker/options/CommitOptions.java | 109 ++++ .../docker/options/CreateImageOptions.java | 95 ++++ .../docker/options/DeleteImageOptions.java | 44 ++ .../docker/options/ListContainerOptions.java | 97 ++++ .../docker/options/ListImageOptions.java | 43 ++ .../options/RemoveContainerOptions.java | 55 ++ .../services/org.jclouds.apis.ApiMetadata | 1 + .../jclouds/docker/DockerApiMetadataTest.java | 47 ++ .../binders/BindInputStreamToRequestTest.java | 66 +++ .../docker/compute/BaseDockerApiLiveTest.java | 91 ++++ .../DockerComputeServiceAdapterLiveTest.java | 95 ++++ .../compute/DockerComputeServiceLiveTest.java | 142 +++++ .../ContainerToNodeMetadataTest.java | 204 ++++++++ .../compute/functions/ImageToImageTest.java | 74 +++ .../compute/functions/StateToStatusTest.java | 81 +++ .../options/DockerTemplateOptionsTest.java | 62 +++ .../docker/config/DockerParserModuleTest.java | 52 ++ .../docker/features/RemoteApiLiveTest.java | 121 +++++ .../docker/features/RemoteApiMockTest.java | 376 ++++++++++++++ .../features/internal/ArchivesTest.java | 112 ++++ .../docker/internal/BaseDockerMockTest.java | 118 +++++ apis/docker/src/test/resources/Dockerfile | 29 ++ .../test/resources/container-creation.json | 1 + apis/docker/src/test/resources/container.json | 81 +++ .../docker/src/test/resources/containers.json | 20 + apis/docker/src/test/resources/logback.xml | 34 ++ 53 files changed, 6346 insertions(+) create mode 100644 apis/docker/README.md create mode 100644 apis/docker/pom.xml create mode 100644 apis/docker/src/main/java/org/jclouds/docker/DockerApi.java create mode 100644 apis/docker/src/main/java/org/jclouds/docker/DockerApiMetadata.java create mode 100644 apis/docker/src/main/java/org/jclouds/docker/binders/BindInputStreamToRequest.java create mode 100644 apis/docker/src/main/java/org/jclouds/docker/compute/config/DockerComputeServiceContextModule.java create mode 100644 apis/docker/src/main/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadata.java create mode 100644 apis/docker/src/main/java/org/jclouds/docker/compute/functions/ImageToImage.java create mode 100644 apis/docker/src/main/java/org/jclouds/docker/compute/functions/StateToStatus.java create mode 100644 apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java create mode 100644 apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java create mode 100644 apis/docker/src/main/java/org/jclouds/docker/config/DockerHttpApiModule.java create mode 100644 apis/docker/src/main/java/org/jclouds/docker/config/DockerParserModule.java create mode 100644 apis/docker/src/main/java/org/jclouds/docker/config/DockerProperties.java create mode 100644 apis/docker/src/main/java/org/jclouds/docker/domain/Config.java create mode 100644 apis/docker/src/main/java/org/jclouds/docker/domain/Container.java create mode 100644 apis/docker/src/main/java/org/jclouds/docker/domain/ExposedPorts.java create mode 100644 apis/docker/src/main/java/org/jclouds/docker/domain/HostConfig.java create mode 100644 apis/docker/src/main/java/org/jclouds/docker/domain/Image.java create mode 100644 apis/docker/src/main/java/org/jclouds/docker/domain/NetworkSettings.java create mode 100644 apis/docker/src/main/java/org/jclouds/docker/domain/Port.java create mode 100644 apis/docker/src/main/java/org/jclouds/docker/domain/State.java create mode 100644 apis/docker/src/main/java/org/jclouds/docker/domain/Version.java create mode 100644 apis/docker/src/main/java/org/jclouds/docker/features/RemoteApi.java create mode 100644 apis/docker/src/main/java/org/jclouds/docker/features/internal/Archives.java create mode 100644 apis/docker/src/main/java/org/jclouds/docker/handlers/DockerErrorHandler.java create mode 100644 apis/docker/src/main/java/org/jclouds/docker/options/BuildOptions.java create mode 100644 apis/docker/src/main/java/org/jclouds/docker/options/CommitOptions.java create mode 100644 apis/docker/src/main/java/org/jclouds/docker/options/CreateImageOptions.java create mode 100644 apis/docker/src/main/java/org/jclouds/docker/options/DeleteImageOptions.java create mode 100644 apis/docker/src/main/java/org/jclouds/docker/options/ListContainerOptions.java create mode 100644 apis/docker/src/main/java/org/jclouds/docker/options/ListImageOptions.java create mode 100644 apis/docker/src/main/java/org/jclouds/docker/options/RemoveContainerOptions.java create mode 100644 apis/docker/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata create mode 100644 apis/docker/src/test/java/org/jclouds/docker/DockerApiMetadataTest.java create mode 100644 apis/docker/src/test/java/org/jclouds/docker/binders/BindInputStreamToRequestTest.java create mode 100644 apis/docker/src/test/java/org/jclouds/docker/compute/BaseDockerApiLiveTest.java create mode 100644 apis/docker/src/test/java/org/jclouds/docker/compute/DockerComputeServiceAdapterLiveTest.java create mode 100644 apis/docker/src/test/java/org/jclouds/docker/compute/DockerComputeServiceLiveTest.java create mode 100644 apis/docker/src/test/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadataTest.java create mode 100644 apis/docker/src/test/java/org/jclouds/docker/compute/functions/ImageToImageTest.java create mode 100644 apis/docker/src/test/java/org/jclouds/docker/compute/functions/StateToStatusTest.java create mode 100644 apis/docker/src/test/java/org/jclouds/docker/compute/options/DockerTemplateOptionsTest.java create mode 100644 apis/docker/src/test/java/org/jclouds/docker/config/DockerParserModuleTest.java create mode 100644 apis/docker/src/test/java/org/jclouds/docker/features/RemoteApiLiveTest.java create mode 100644 apis/docker/src/test/java/org/jclouds/docker/features/RemoteApiMockTest.java create mode 100644 apis/docker/src/test/java/org/jclouds/docker/features/internal/ArchivesTest.java create mode 100644 apis/docker/src/test/java/org/jclouds/docker/internal/BaseDockerMockTest.java create mode 100644 apis/docker/src/test/resources/Dockerfile create mode 100644 apis/docker/src/test/resources/container-creation.json create mode 100644 apis/docker/src/test/resources/container.json create mode 100644 apis/docker/src/test/resources/containers.json create mode 100644 apis/docker/src/test/resources/logback.xml diff --git a/apis/docker/README.md b/apis/docker/README.md new file mode 100644 index 0000000000..1c4394ac10 --- /dev/null +++ b/apis/docker/README.md @@ -0,0 +1,48 @@ +# Docker as a local cloud provider +jclouds-docker is a local cloud provider modelled on [docker](http://www.docker.io). Similar to other jclouds supported +providers, it supports the same portable abstractions offered by jclouds. + +##Setup + +Please follow these steps to configure your workstation for jclouds-docker: + +- install the latest Docker release (please visit https://docs.docker.com/installation/) + +#How it works + + + --------------- ------------- + | Image(s) | | Node(s) | + --------------- ------------- + --------- docker remote api ---------------------------------------- + | jclouds | ---------------------------> | DOCKER_HOST | + --------- ---------------------------------------- + +##Components + +- jclouds \- acts as a java client to access to docker features +- DOCKER_HOST \- hosts Docker API, NB: jclouds-docker assumes that the latest Docker is installed +- Image \- it is a docker image that can be started. +- Node \- is a docker container + +## Assumptions + +- jclouds-docker assumes that the images specified using the template are ssh'able. + +-------------- + +#Notes: +- jclouds-docker is still at alpha stage please report any issues you find at [jclouds issues](https://issues.apache.org/jira/browse/JCLOUDS) +- jclouds-docker has been tested on Mac OSX, it might work on Linux iff vbox is running and set up correctly. However, it has never been tried on Windows. + +-------------- + +#Troubleshooting +As jclouds docker support is quite new, issues may occasionally arise. Please follow these steps to get things going again: + +1. Remove all containers + + `$ docker ps -a -q | xargs docker stop | xargs docker rm` +2. remove all the images + + `$ docker images -q | xargs docker rmi` diff --git a/apis/docker/pom.xml b/apis/docker/pom.xml new file mode 100644 index 0000000000..42b9d4d204 --- /dev/null +++ b/apis/docker/pom.xml @@ -0,0 +1,143 @@ + + + + 4.0.0 + + org.apache.jclouds.labs + jclouds-labs + 1.8.0-SNAPSHOT + + + + org.apache.jclouds.labs + docker + jclouds docker API + ComputeService binding to the Docker API + bundle + + + https://localhost:4243 + 1.10 + FIXME + FIXME + org.jclouds.docker*;version="${project.version}" + + org.jclouds.compute.internal;version="${project.version}", + org.jclouds.rest.internal;version="${project.version}", + org.jclouds*;version="${project.version}", + * + + + + + + org.apache.jclouds + jclouds-core + ${project.version} + + + org.apache.jclouds + jclouds-compute + ${project.version} + + + org.apache.commons + commons-compress + 1.5 + + + + org.apache.jclouds.driver + jclouds-sshj + ${project.version} + test + + + org.apache.jclouds + jclouds-core + ${project.version} + test-jar + test + + + org.apache.jclouds + jclouds-compute + ${project.version} + test-jar + test + + + org.apache.jclouds.driver + jclouds-slf4j + ${project.version} + test + + + ch.qos.logback + logback-classic + test + + + com.squareup.okhttp + mockwebserver + test + + + + org.bouncycastle + bcprov-jdk15on + + + + + + + + live + + + + org.apache.maven.plugins + maven-surefire-plugin + + + integration + integration-test + + test + + + + ${test.docker.endpoint} + ${test.docker.api-version} + ${test.docker.identity} + ${test.docker.credential} + + + + + + + + + + + diff --git a/apis/docker/src/main/java/org/jclouds/docker/DockerApi.java b/apis/docker/src/main/java/org/jclouds/docker/DockerApi.java new file mode 100644 index 0000000000..54bf5c9a06 --- /dev/null +++ b/apis/docker/src/main/java/org/jclouds/docker/DockerApi.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker; + +import org.jclouds.docker.features.RemoteApi; +import org.jclouds.rest.annotations.Delegate; + +import java.io.Closeable; + +/** + * Provides synchronous access to Docker Remote API. + * + * @see + */ +public interface DockerApi extends Closeable { + + /** + * Provides synchronous access to Docker Remote API features. + */ + @Delegate + RemoteApi getRemoteApi(); + +} diff --git a/apis/docker/src/main/java/org/jclouds/docker/DockerApiMetadata.java b/apis/docker/src/main/java/org/jclouds/docker/DockerApiMetadata.java new file mode 100644 index 0000000000..b281eb7566 --- /dev/null +++ b/apis/docker/src/main/java/org/jclouds/docker/DockerApiMetadata.java @@ -0,0 +1,98 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker; + +import com.google.common.collect.ImmutableSet; +import com.google.inject.Module; +import org.jclouds.Constants; +import org.jclouds.apis.ApiMetadata; +import org.jclouds.compute.ComputeServiceContext; +import org.jclouds.compute.config.ComputeServiceProperties; +import org.jclouds.docker.compute.config.DockerComputeServiceContextModule; +import org.jclouds.docker.config.DockerHttpApiModule; +import org.jclouds.docker.config.DockerParserModule; +import org.jclouds.rest.internal.BaseHttpApiMetadata; + +import java.net.URI; +import java.util.Properties; + +import static org.jclouds.compute.config.ComputeServiceProperties.TEMPLATE; +import static org.jclouds.reflect.Reflection2.typeToken; + +/** + * Implementation of {@link BaseHttpApiMetadata} for the Docker API + */ +public class DockerApiMetadata extends BaseHttpApiMetadata { + + @Override + public Builder toBuilder() { + return new Builder().fromApiMetadata(this); + } + + public DockerApiMetadata() { + this(new Builder()); + } + + protected DockerApiMetadata(Builder builder) { + super(builder); + } + + public static Properties defaultProperties() { + Properties properties = BaseHttpApiMetadata.defaultProperties(); + properties.setProperty(Constants.PROPERTY_MAX_RETRIES, "15"); + properties.setProperty("jclouds.ssh.retry-auth", "true"); + properties.setProperty(Constants.PROPERTY_CONNECTION_TIMEOUT, "1200000"); // 15 minutes + properties.setProperty(ComputeServiceProperties.IMAGE_LOGIN_USER, "root:password"); + properties.setProperty(TEMPLATE, "osFamily=UBUNTU,os64Bit=true,osVersionMatches=1[012].[01][04]"); + return properties; + } + + public static class Builder extends BaseHttpApiMetadata.Builder { + + protected Builder() { + super(DockerApi.class); + id("docker") + .name("Docker API") + .identityName("user") + .credentialName("password") + .documentation(URI.create("https://docs.docker.com/reference/api/docker_remote_api/")) + .version("1.12") + .defaultEndpoint("http://127.0.0.1:2375") + .defaultProperties(DockerApiMetadata.defaultProperties()) + .view(typeToken(ComputeServiceContext.class)) + .defaultModules(ImmutableSet.>of( + DockerHttpApiModule.class, + DockerParserModule.class, + DockerComputeServiceContextModule.class)); + } + + @Override + public DockerApiMetadata build() { + return new DockerApiMetadata(this); + } + + @Override + protected Builder self() { + return this; + } + + @Override + public Builder fromApiMetadata(ApiMetadata in) { + return this; + } + } +} diff --git a/apis/docker/src/main/java/org/jclouds/docker/binders/BindInputStreamToRequest.java b/apis/docker/src/main/java/org/jclouds/docker/binders/BindInputStreamToRequest.java new file mode 100644 index 0000000000..855a2e5d41 --- /dev/null +++ b/apis/docker/src/main/java/org/jclouds/docker/binders/BindInputStreamToRequest.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.binders; + +import com.google.common.base.Throwables; +import com.google.common.io.Files; +import org.jclouds.compute.reference.ComputeServiceConstants; +import org.jclouds.docker.features.internal.Archives; +import org.jclouds.http.HttpRequest; +import org.jclouds.io.Payload; +import org.jclouds.io.Payloads; +import org.jclouds.logging.Logger; +import org.jclouds.rest.Binder; + +import javax.annotation.Resource; +import javax.inject.Named; +import javax.inject.Singleton; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +@Singleton +public class BindInputStreamToRequest implements Binder { + + @Resource + @Named(ComputeServiceConstants.COMPUTE_LOGGER) + protected Logger logger = Logger.NULL; + + @Override + public R bindToRequest(R request, Object input) { + checkArgument(checkNotNull(input, "input") instanceof File, "this binder is only valid for File!"); + checkNotNull(request, "request"); + + File dockerFile = (File) input; + File tmpDir = Files.createTempDir(); + final File targetFile = new File(tmpDir + File.separator + "Dockerfile"); + try { + Files.copy(dockerFile, targetFile); + File archive = Archives.tar(tmpDir, File.createTempFile("archive", ".tar")); + FileInputStream data = new FileInputStream(archive); + Payload payload = Payloads.newInputStreamPayload(data); + payload.getContentMetadata().setContentLength(data.getChannel().size()); + payload.getContentMetadata().setContentType("application/tar"); + request.setPayload(payload); + } catch (IOException e) { + logger.error(e, "Couldn't create a tarball for %s", targetFile); + throw Throwables.propagate(e); + } + return request; + } +} diff --git a/apis/docker/src/main/java/org/jclouds/docker/compute/config/DockerComputeServiceContextModule.java b/apis/docker/src/main/java/org/jclouds/docker/compute/config/DockerComputeServiceContextModule.java new file mode 100644 index 0000000000..b1cfee2da7 --- /dev/null +++ b/apis/docker/src/main/java/org/jclouds/docker/compute/config/DockerComputeServiceContextModule.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.compute.config; + +import com.google.common.base.Function; +import com.google.inject.TypeLiteral; +import org.jclouds.compute.ComputeServiceAdapter; +import org.jclouds.compute.config.ComputeServiceAdapterContextModule; +import org.jclouds.compute.domain.Hardware; +import org.jclouds.compute.domain.NodeMetadata; +import org.jclouds.compute.options.TemplateOptions; +import org.jclouds.docker.compute.functions.ContainerToNodeMetadata; +import org.jclouds.docker.compute.functions.ImageToImage; +import org.jclouds.docker.compute.functions.StateToStatus; +import org.jclouds.docker.compute.options.DockerTemplateOptions; +import org.jclouds.docker.compute.strategy.DockerComputeServiceAdapter; +import org.jclouds.docker.domain.Container; +import org.jclouds.docker.domain.Image; +import org.jclouds.docker.domain.State; +import org.jclouds.domain.Location; +import org.jclouds.functions.IdentityFunction; + +public class DockerComputeServiceContextModule extends + ComputeServiceAdapterContextModule { + + @Override + protected void configure() { + super.configure(); + bind(new TypeLiteral>() { + }).to(DockerComputeServiceAdapter.class); + bind(new TypeLiteral>() { + }).to(ContainerToNodeMetadata.class); + bind(new TypeLiteral>() { + }).to(ImageToImage.class); + bind(new TypeLiteral>() { + }).to(Class.class.cast(IdentityFunction.class)); + bind(new TypeLiteral>() { + }).to(Class.class.cast(IdentityFunction.class)); + bind(new TypeLiteral>() { + }).to(StateToStatus.class); + bind(TemplateOptions.class).to(DockerTemplateOptions.class); + } + +} diff --git a/apis/docker/src/main/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadata.java b/apis/docker/src/main/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadata.java new file mode 100644 index 0000000000..72c16af55c --- /dev/null +++ b/apis/docker/src/main/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadata.java @@ -0,0 +1,126 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.compute.functions; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.Iterables.getOnlyElement; +import java.net.URI; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.inject.Inject; + +import org.jclouds.collect.Memoized; +import org.jclouds.compute.domain.HardwareBuilder; +import org.jclouds.compute.domain.Image; +import org.jclouds.compute.domain.NodeMetadata; +import org.jclouds.compute.domain.NodeMetadataBuilder; +import org.jclouds.compute.domain.Processor; +import org.jclouds.compute.functions.GroupNamingConvention; +import org.jclouds.docker.domain.Container; +import org.jclouds.docker.domain.Port; +import org.jclouds.docker.domain.State; +import org.jclouds.domain.Location; +import org.jclouds.providers.ProviderMetadata; + +import com.google.common.base.Function; +import com.google.common.base.Supplier; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +import com.google.inject.Singleton; + +@Singleton +public class ContainerToNodeMetadata implements Function { + + private final ProviderMetadata providerMetadata; + private final Function toPortableStatus; + private final GroupNamingConvention nodeNamingConvention; + private final Supplier> images; + private final Supplier> locations; + + @Inject + public ContainerToNodeMetadata(ProviderMetadata providerMetadata, Function toPortableStatus, GroupNamingConvention.Factory namingConvention, + Supplier> images, + @Memoized Supplier> locations) { + this.providerMetadata = checkNotNull(providerMetadata, "providerMetadata"); + this.toPortableStatus = checkNotNull(toPortableStatus, "toPortableStatus cannot be null"); + this.nodeNamingConvention = checkNotNull(namingConvention, "namingConvention").createWithoutPrefix(); + this.images = checkNotNull(images, "images cannot be null"); + this.locations = checkNotNull(locations, "locations"); + } + + @Override + public NodeMetadata apply(Container container) { + String name = cleanUpName(container.getName()); + String group = nodeNamingConvention.extractGroup(name); + NodeMetadataBuilder builder = new NodeMetadataBuilder(); + builder.ids(container.getId()) + .name(name) + .group(group) + .hostname(container.getContainerConfig().getHostname()) + // TODO Set up hardware + .hardware(new HardwareBuilder() + .id("") + .ram(container.getContainerConfig().getMemory()) + .processor(new Processor(container.getContainerConfig().getCpuShares(), container.getContainerConfig().getCpuShares())) + .build()); + builder.status(toPortableStatus.apply(container.getState())); + builder.imageId(container.getImage()); + builder.loginPort(getLoginPort(container)); + builder.publicAddresses(getPublicIpAddresses()); + builder.privateAddresses(getPrivateIpAddresses(container)); + builder.location(Iterables.getOnlyElement(locations.get())); + Image image = images.get().get(container.getImage()); + builder.imageId(image.getId()); + builder.operatingSystem(image.getOperatingSystem()); + + return builder.build(); + } + + private String cleanUpName(String name) { + return name.startsWith("/") ? name.substring(1) : name; + } + + private Iterable getPrivateIpAddresses(Container container) { + if (container.getNetworkSettings() == null) return ImmutableList.of(); + return ImmutableList.of(container.getNetworkSettings().getIpAddress()); + } + + private List getPublicIpAddresses() { + String dockerIpAddress = URI.create(providerMetadata.getEndpoint()).getHost(); + return ImmutableList.of(dockerIpAddress); + } + + protected static int getLoginPort(Container container) { + if (container.getNetworkSettings() != null) { + Map>> ports = container.getNetworkSettings().getPorts(); + if (ports != null) { + return Integer.parseInt(getOnlyElement(ports.get("22/tcp")).get("HostPort")); + } + // this is needed in case the container list is coming from listContainers + } else if (container.getPorts() != null) { + for (Port port : container.getPorts()) { + if (port.getPrivatePort() == 22) { + return port.getPublicPort(); + } + } + } + throw new IllegalStateException("Cannot determine the login port for " + container.getId()); + } +} diff --git a/apis/docker/src/main/java/org/jclouds/docker/compute/functions/ImageToImage.java b/apis/docker/src/main/java/org/jclouds/docker/compute/functions/ImageToImage.java new file mode 100644 index 0000000000..4e3358df44 --- /dev/null +++ b/apis/docker/src/main/java/org/jclouds/docker/compute/functions/ImageToImage.java @@ -0,0 +1,98 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.compute.functions; + +import com.google.common.base.Function; +import com.google.common.base.Splitter; +import com.google.common.collect.Iterables; +import org.jclouds.compute.domain.Image; +import org.jclouds.compute.domain.ImageBuilder; +import org.jclouds.compute.domain.OperatingSystem; +import org.jclouds.compute.domain.OsFamily; +import org.jclouds.compute.reference.ComputeServiceConstants; +import org.jclouds.logging.Logger; + +import javax.annotation.Resource; +import javax.inject.Named; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.Iterables.get; + +public class ImageToImage implements Function { + + private static final String CENTOS = "centos"; + private static final String UBUNTU = "ubuntu"; + + @Resource + @Named(ComputeServiceConstants.COMPUTE_LOGGER) + protected Logger logger = Logger.NULL; + + @Override + public Image apply(org.jclouds.docker.domain.Image from) { + checkNotNull(from, "image"); + String description = checkNotNull(Iterables.getFirst(from.getRepoTags(), "image must have at least one repo tag")); + + OsFamily osFamily = osFamily().apply(description); + String osVersion = parseVersion(description); + + OperatingSystem os = OperatingSystem.builder() + .description(description) + .family(osFamily) + .version(osVersion) + .is64Bit(is64bit(from)) + .build(); + + return new ImageBuilder() + .ids(from.getId()) + .name(get(Splitter.on(":").split(description), 0)) + .description(description) + .operatingSystem(os) + .status(Image.Status.AVAILABLE) + .build(); + } + + private boolean is64bit(org.jclouds.docker.domain.Image inspectedImage) { + if (inspectedImage.getArchitecture() == null) return true; + return inspectedImage.getArchitecture().matches("x86_64|amd64"); + } + + /** + * Parses the item description to determine the OSFamily + * + * @return the @see OsFamily or OsFamily.UNRECOGNIZED + */ + private Function osFamily() { + return new Function() { + + @Override + public OsFamily apply(final String description) { + if (description != null) { + if (description.contains(CENTOS)) return OsFamily.CENTOS; + else if (description.contains(UBUNTU)) return OsFamily.UBUNTU; + } + return OsFamily.UNRECOGNIZED; + } + }; + } + + private String parseVersion(String description) { + String version = get(Splitter.on(":").split(description), 1); + logger.debug("os version for item: %s is %s", description, version); + return version; + } + +} diff --git a/apis/docker/src/main/java/org/jclouds/docker/compute/functions/StateToStatus.java b/apis/docker/src/main/java/org/jclouds/docker/compute/functions/StateToStatus.java new file mode 100644 index 0000000000..2d87384005 --- /dev/null +++ b/apis/docker/src/main/java/org/jclouds/docker/compute/functions/StateToStatus.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.compute.functions; + +import javax.inject.Singleton; + +import org.jclouds.compute.domain.NodeMetadata.Status; +import org.jclouds.docker.domain.State; + +import com.google.common.base.Function; + +/** + * Transforms an {@link org.jclouds.docker.domain.Container} to the jclouds portable model. + */ +@Singleton +public class StateToStatus implements Function { + + @Override + public Status apply(final State state) { + if (state == null) return Status.UNRECOGNIZED; + return state.isRunning() ? Status.RUNNING : Status.TERMINATED; + } + +} diff --git a/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java b/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java new file mode 100644 index 0000000000..5e4669b9ea --- /dev/null +++ b/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java @@ -0,0 +1,485 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.compute.options; + +import com.google.common.base.Objects; +import com.google.common.base.Optional; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import org.jclouds.compute.options.TemplateOptions; +import org.jclouds.domain.LoginCredentials; +import org.jclouds.javax.annotation.Nullable; +import org.jclouds.scriptbuilder.domain.Statement; + +import java.util.List; +import java.util.Map; + +import static com.google.common.base.Objects.equal; +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Contains options supported in the {@code ComputeService#runNode} operation on the + * "docker" provider.

Usage

The recommended way to instantiate a + * DockerTemplateOptions object is to statically import DockerTemplateOptions.* and invoke a static + * creation method followed by an instance mutator (if needed): + *

+ * + * import static org.jclouds.docker.compute.options.DockerTemplateOptions.Builder.*; + *

+ * ComputeService api = // get connection + * templateBuilder.options(inboundPorts(22, 80, 8080, 443)); + * Set set = api.createNodesInGroup(tag, 2, templateBuilder.build()); + * + */ +public class DockerTemplateOptions extends TemplateOptions implements Cloneable { + + protected Optional dns = Optional.absent(); + protected Optional hostname = Optional.absent(); + protected Optional memory = Optional.absent(); + protected Optional cpuShares = Optional.absent(); + protected Optional> commands = Optional.absent(); + protected Optional> volumes = Optional.absent(); + + @Override + public DockerTemplateOptions clone() { + DockerTemplateOptions options = new DockerTemplateOptions(); + copyTo(options); + return options; + } + + @Override + public void copyTo(TemplateOptions to) { + super.copyTo(to); + if (to instanceof DockerTemplateOptions) { + DockerTemplateOptions eTo = DockerTemplateOptions.class.cast(to); + if (volumes.isPresent()) { + eTo.volumes(getVolumes().get()); + } + if (hostname.isPresent()) { + eTo.hostname(hostname.get()); + } + if (dns.isPresent()) { + eTo.dns(dns.get()); + } + if (memory.isPresent()) { + eTo.memory(memory.get()); + } + if (commands.isPresent()) { + eTo.commands(commands.get()); + } + if (cpuShares.isPresent()) { + eTo.cpuShares(cpuShares.get()); + } + } + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + DockerTemplateOptions that = DockerTemplateOptions.class.cast(o); + return super.equals(that) && equal(this.volumes, that.volumes) && + equal(this.hostname, that.hostname) && + equal(this.dns, that.dns) && + equal(this.memory, that.memory) && + equal(this.commands, that.commands) && + equal(this.cpuShares, that.cpuShares); + } + + @Override + public int hashCode() { + return Objects.hashCode(super.hashCode(), volumes, hostname, dns, memory, commands, cpuShares); + } + + @Override + public String toString() { + return Objects.toStringHelper(this) + .add("dns", dns) + .add("hostname", hostname) + .add("memory", memory) + .add("cpuShares", cpuShares) + .add("commands", commands) + .add("volumes", volumes) + .toString(); + } + + public static final DockerTemplateOptions NONE = new DockerTemplateOptions(); + + public DockerTemplateOptions volumes(Map volumes) { + this.volumes = Optional.> of(ImmutableMap.copyOf(volumes)); + return this; + } + + public TemplateOptions dns(@Nullable String dns) { + this.dns = Optional.fromNullable(dns); + return this; + } + + public TemplateOptions hostname(@Nullable String hostname) { + this.hostname = Optional.fromNullable(hostname); + return this; + } + + public TemplateOptions memory(@Nullable Integer memory) { + this.memory = Optional.fromNullable(memory); + return this; + } + + public TemplateOptions commands(Iterable commands) { + for (String command : checkNotNull(commands, "commands")) + checkNotNull(command, "all commands must be non-empty"); + this.commands = Optional.> of(ImmutableList.copyOf(commands)); + return this; + } + + public TemplateOptions commands(String... commands) { + return commands(ImmutableList.copyOf(checkNotNull(commands, "commands"))); + } + + public TemplateOptions cpuShares(@Nullable Integer cpuShares) { + this.cpuShares = Optional.fromNullable(cpuShares); + return this; + } + + public Optional> getVolumes() { + return volumes; + } + + public Optional getDns() { return dns; } + + public Optional getHostname() { return hostname; } + + public Optional getMemory() { return memory; } + + public Optional> getCommands() { + return commands; + } + + public Optional getCpuShares() { return cpuShares; } + + public static class Builder { + + /** + * @see DockerTemplateOptions#volumes(java.util.Map) + */ + public static DockerTemplateOptions volumes(Map volumes) { + DockerTemplateOptions options = new DockerTemplateOptions(); + return DockerTemplateOptions.class.cast(options.volumes(volumes)); + } + + /** + * @see DockerTemplateOptions#dns(String) + */ + public static DockerTemplateOptions dns(String dns) { + DockerTemplateOptions options = new DockerTemplateOptions(); + return DockerTemplateOptions.class.cast(options.dns(dns)); + } + + /** + * @see DockerTemplateOptions#hostname(String) + */ + public static DockerTemplateOptions hostname(String hostname) { + DockerTemplateOptions options = new DockerTemplateOptions(); + return DockerTemplateOptions.class.cast(options.hostname(hostname)); + } + + /** + * @see DockerTemplateOptions#memory(int) + */ + public static DockerTemplateOptions memory(int memory) { + DockerTemplateOptions options = new DockerTemplateOptions(); + return DockerTemplateOptions.class.cast(options.memory(memory)); + } + + /** + * @see DockerTemplateOptions#commands(Iterable) + */ + public static DockerTemplateOptions commands(String... commands) { + DockerTemplateOptions options = new DockerTemplateOptions(); + return DockerTemplateOptions.class.cast(options.commands(commands)); + } + + public static DockerTemplateOptions commands(Iterable commands) { + DockerTemplateOptions options = new DockerTemplateOptions(); + return DockerTemplateOptions.class.cast(options.commands(commands)); + } + + /** + * @see DockerTemplateOptions#cpuShares(int) + */ + public static DockerTemplateOptions cpuShares(int cpuShares) { + DockerTemplateOptions options = new DockerTemplateOptions(); + return DockerTemplateOptions.class.cast(options.cpuShares(cpuShares)); + } + + // methods that only facilitate returning the correct object type + + /** + * @see TemplateOptions#inboundPorts + */ + public static DockerTemplateOptions inboundPorts(int... ports) { + DockerTemplateOptions options = new DockerTemplateOptions(); + return DockerTemplateOptions.class.cast(options.inboundPorts(ports)); + } + + /** + * @see TemplateOptions#port + */ + public static DockerTemplateOptions blockOnPort(int port, int seconds) { + DockerTemplateOptions options = new DockerTemplateOptions(); + return DockerTemplateOptions.class.cast(options.blockOnPort(port, seconds)); + } + + /** + * @see TemplateOptions#installPrivateKey + */ + public static DockerTemplateOptions installPrivateKey(String rsaKey) { + DockerTemplateOptions options = new DockerTemplateOptions(); + return DockerTemplateOptions.class.cast(options.installPrivateKey(rsaKey)); + } + + /** + * @see TemplateOptions#authorizePublicKey + */ + public static DockerTemplateOptions authorizePublicKey(String rsaKey) { + DockerTemplateOptions options = new DockerTemplateOptions(); + return DockerTemplateOptions.class.cast(options.authorizePublicKey(rsaKey)); + } + + /** + * @see TemplateOptions#userMetadata + */ + public static DockerTemplateOptions userMetadata(Map userMetadata) { + DockerTemplateOptions options = new DockerTemplateOptions(); + return DockerTemplateOptions.class.cast(options.userMetadata(userMetadata)); + } + + /** + * @see TemplateOptions#nodeNames(Iterable) + */ + public static DockerTemplateOptions nodeNames(Iterable nodeNames) { + DockerTemplateOptions options = new DockerTemplateOptions(); + return DockerTemplateOptions.class.cast(options.nodeNames(nodeNames)); + } + + /** + * @see TemplateOptions#networks(Iterable) + */ + public static DockerTemplateOptions networks(Iterable networks) { + DockerTemplateOptions options = new DockerTemplateOptions(); + return DockerTemplateOptions.class.cast(options.networks(networks)); + } + + /** + * @see TemplateOptions#overrideLoginUser + */ + public static DockerTemplateOptions overrideLoginUser(String user) { + DockerTemplateOptions options = new DockerTemplateOptions(); + return options.overrideLoginUser(user); + } + + /** + * @see TemplateOptions#overrideLoginPassword + */ + public static DockerTemplateOptions overrideLoginPassword(String password) { + DockerTemplateOptions options = new DockerTemplateOptions(); + return options.overrideLoginPassword(password); + } + + /** + * @see TemplateOptions#overrideLoginPrivateKey + */ + public static DockerTemplateOptions overrideLoginPrivateKey(String privateKey) { + DockerTemplateOptions options = new DockerTemplateOptions(); + return options.overrideLoginPrivateKey(privateKey); + } + + /** + * @see TemplateOptions#overrideAuthenticateSudo + */ + public static DockerTemplateOptions overrideAuthenticateSudo(boolean authenticateSudo) { + DockerTemplateOptions options = new DockerTemplateOptions(); + return options.overrideAuthenticateSudo(authenticateSudo); + } + + /** + * @see TemplateOptions#overrideLoginCredentials + */ + public static DockerTemplateOptions overrideLoginCredentials(LoginCredentials credentials) { + DockerTemplateOptions options = new DockerTemplateOptions(); + return options.overrideLoginCredentials(credentials); + } + + /** + * @see TemplateOptions#blockUntilRunning + */ + public static DockerTemplateOptions blockUntilRunning(boolean blockUntilRunning) { + DockerTemplateOptions options = new DockerTemplateOptions(); + return options.blockUntilRunning(blockUntilRunning); + } + + } + + // methods that only facilitate returning the correct object type + + /** + * {@inheritDoc} + */ + @Override + public DockerTemplateOptions blockOnPort(int port, int seconds) { + return DockerTemplateOptions.class.cast(super.blockOnPort(port, seconds)); + } + + /** + * {@inheritDoc} + */ + @Override + public DockerTemplateOptions inboundPorts(int... ports) { + return DockerTemplateOptions.class.cast(super.inboundPorts(ports)); + } + + /** + * {@inheritDoc} + */ + @Override + public DockerTemplateOptions authorizePublicKey(String publicKey) { + return DockerTemplateOptions.class.cast(super.authorizePublicKey(publicKey)); + } + + /** + * {@inheritDoc} + */ + @Override + public DockerTemplateOptions installPrivateKey(String privateKey) { + return DockerTemplateOptions.class.cast(super.installPrivateKey(privateKey)); + } + + /** + * {@inheritDoc} + */ + @Override + public DockerTemplateOptions blockUntilRunning(boolean blockUntilRunning) { + return DockerTemplateOptions.class.cast(super.blockUntilRunning(blockUntilRunning)); + } + + /** + * {@inheritDoc} + */ + @Override + public DockerTemplateOptions dontAuthorizePublicKey() { + return DockerTemplateOptions.class.cast(super.dontAuthorizePublicKey()); + } + + /** + * {@inheritDoc} + */ + @Override + public DockerTemplateOptions nameTask(String name) { + return DockerTemplateOptions.class.cast(super.nameTask(name)); + } + + /** + * {@inheritDoc} + */ + @Override + public DockerTemplateOptions runAsRoot(boolean runAsRoot) { + return DockerTemplateOptions.class.cast(super.runAsRoot(runAsRoot)); + } + + /** + * {@inheritDoc} + */ + @Override + public DockerTemplateOptions runScript(Statement script) { + return DockerTemplateOptions.class.cast(super.runScript(script)); + } + + /** + * {@inheritDoc} + */ + @Override + public DockerTemplateOptions overrideLoginCredentials(LoginCredentials overridingCredentials) { + return DockerTemplateOptions.class.cast(super.overrideLoginCredentials(overridingCredentials)); + } + + /** + * {@inheritDoc} + */ + @Override + public DockerTemplateOptions overrideLoginPassword(String password) { + return DockerTemplateOptions.class.cast(super.overrideLoginPassword(password)); + } + + /** + * {@inheritDoc} + */ + @Override + public DockerTemplateOptions overrideLoginPrivateKey(String privateKey) { + return DockerTemplateOptions.class.cast(super.overrideLoginPrivateKey(privateKey)); + } + + /** + * {@inheritDoc} + */ + @Override + public DockerTemplateOptions overrideLoginUser(String loginUser) { + return DockerTemplateOptions.class.cast(super.overrideLoginUser(loginUser)); + } + + /** + * {@inheritDoc} + */ + @Override + public DockerTemplateOptions overrideAuthenticateSudo(boolean authenticateSudo) { + return DockerTemplateOptions.class.cast(super.overrideAuthenticateSudo(authenticateSudo)); + } + + /** + * {@inheritDoc} + */ + @Override + public DockerTemplateOptions userMetadata(Map userMetadata) { + return DockerTemplateOptions.class.cast(super.userMetadata(userMetadata)); + } + + /** + * {@inheritDoc} + */ + @Override + public DockerTemplateOptions userMetadata(String key, String value) { + return DockerTemplateOptions.class.cast(super.userMetadata(key, value)); + } + + /** + * {@inheritDoc} + */ + @Override + public DockerTemplateOptions nodeNames(Iterable nodeNames) { + return DockerTemplateOptions.class.cast(super.nodeNames(nodeNames)); + } + + /** + * {@inheritDoc} + */ + @Override + public DockerTemplateOptions networks(Iterable networks) { + return DockerTemplateOptions.class.cast(super.networks(networks)); + } + +} diff --git a/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java b/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java new file mode 100644 index 0000000000..7bbead38fa --- /dev/null +++ b/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java @@ -0,0 +1,232 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.compute.strategy; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.Iterables.find; + +import java.util.Map; +import java.util.Set; + +import javax.annotation.Resource; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.jclouds.compute.ComputeServiceAdapter; +import org.jclouds.compute.domain.Hardware; +import org.jclouds.compute.domain.HardwareBuilder; +import org.jclouds.compute.domain.Processor; +import org.jclouds.compute.domain.Template; +import org.jclouds.compute.reference.ComputeServiceConstants; +import org.jclouds.docker.DockerApi; +import org.jclouds.docker.compute.options.DockerTemplateOptions; +import org.jclouds.docker.domain.Config; +import org.jclouds.docker.domain.Container; +import org.jclouds.docker.domain.HostConfig; +import org.jclouds.docker.domain.Image; +import org.jclouds.docker.options.ListContainerOptions; +import org.jclouds.docker.options.RemoveContainerOptions; +import org.jclouds.domain.Location; +import org.jclouds.domain.LoginCredentials; +import org.jclouds.logging.Logger; + +import com.google.common.base.Predicate; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; + +/** + * defines the connection between the {@link org.jclouds.docker.DockerApi} implementation and + * the jclouds {@link org.jclouds.compute.ComputeService} + */ +@Singleton +public class DockerComputeServiceAdapter implements + ComputeServiceAdapter { + + @Resource + @Named(ComputeServiceConstants.COMPUTE_LOGGER) + protected Logger logger = Logger.NULL; + + private final DockerApi api; + + @Inject + public DockerComputeServiceAdapter(DockerApi api) { + this.api = checkNotNull(api, "api"); + } + + @Override + public NodeAndInitialCredentials createNodeWithGroupEncodedIntoName(String group, String name, + Template template) { + checkNotNull(template, "template was null"); + checkNotNull(template.getOptions(), "template options was null"); + + String imageId = checkNotNull(template.getImage().getId(), "template image id must not be null"); + String loginUser = template.getImage().getDefaultCredentials().getUser(); + String loginUserPassword = template.getImage().getDefaultCredentials().getPassword(); + + DockerTemplateOptions templateOptions = DockerTemplateOptions.class.cast(template.getOptions()); + int[] inboundPorts = templateOptions.getInboundPorts(); + + Map exposedPorts = Maps.newHashMap(); + for (int inboundPort : inboundPorts) { + exposedPorts.put(inboundPort + "/tcp", Maps.newHashMap()); + } + + Config.Builder containerConfigBuilder = Config.builder() + .imageId(imageId) + .exposedPorts(exposedPorts); + + if (templateOptions.getCommands().isPresent()) { + containerConfigBuilder.cmd(templateOptions.getCommands().get()); + } + + if (templateOptions.getMemory().isPresent()) { + containerConfigBuilder.memory(templateOptions.getMemory().get()); + } + + if (templateOptions.getCpuShares().isPresent()) { + containerConfigBuilder.cpuShares(templateOptions.getCpuShares().get()); + } + + if (templateOptions.getVolumes().isPresent()) { + Map volumes = Maps.newLinkedHashMap(); + for (String containerDir : templateOptions.getVolumes().get().values()) { + volumes.put(containerDir, Maps.newHashMap()); + } + containerConfigBuilder.volumes(volumes); + } + Config containerConfig = containerConfigBuilder.build(); + + logger.debug(">> creating new container with containerConfig(%s)", containerConfig); + Container container = api.getRemoteApi().createContainer(name, containerConfig); + logger.trace("<< container(%s)", container.getId()); + + HostConfig.Builder hostConfigBuilder = HostConfig.builder() + .publishAllPorts(true) + .privileged(true); + + if (templateOptions.getDns().isPresent()) { + hostConfigBuilder.dns(templateOptions.getDns().get()); + } + // set up for volume bindings + if (templateOptions.getVolumes().isPresent()) { + for (Map.Entry entry : templateOptions.getVolumes().get().entrySet()) { + hostConfigBuilder.binds(ImmutableList.of(entry.getKey() + ":" + entry.getValue())); + } + } + HostConfig hostConfig = hostConfigBuilder.build(); + + api.getRemoteApi().startContainer(container.getId(), hostConfig); + container = api.getRemoteApi().inspectContainer(container.getId()); + if (container.getState().getExitCode() != 0) { + destroyNode(container.getId()); + throw new IllegalStateException(String.format("Container %s has not started correctly", container.getId())); + } + return new NodeAndInitialCredentials(container, container.getId(), + LoginCredentials.builder().user(loginUser).password(loginUserPassword).build()); + } + + @Override + public Iterable listHardwareProfiles() { + Set hardware = Sets.newLinkedHashSet(); + // todo they are only placeholders at the moment + hardware.add(new HardwareBuilder().ids("micro").hypervisor("lxc").name("micro").processor(new Processor(1, 1)).ram(512).build()); + hardware.add(new HardwareBuilder().ids("small").hypervisor("lxc").name("small").processor(new Processor(1, 1)).ram(1024).build()); + hardware.add(new HardwareBuilder().ids("medium").hypervisor("lxc").name("medium").processor(new Processor(1, 1)).ram(2048).build()); + hardware.add(new HardwareBuilder().ids("large").hypervisor("lxc").name("large").processor(new Processor(1, 1)).ram(3072).build()); + return hardware; + } + + @Override + public Set listImages() { + Set images = Sets.newHashSet(); + for (Image image : api.getRemoteApi().listImages()) { + // less efficient than just listImages but returns richer json that needs repoTags coming from listImages + Image inspected = api.getRemoteApi().inspectImage(image.getId()); + if (inspected.getRepoTags().isEmpty()) { + inspected = Image.builder().fromImage(inspected).repoTags(image.getRepoTags()).build(); + } + images.add(inspected); + } + return images; + } + + @Override + public Image getImage(final String imageId) { + // less efficient than just inspectImage but listImages return repoTags + return find(listImages(), new Predicate() { + + @Override + public boolean apply(Image input) { + return input.getId().equals(imageId); + } + }, null); + } + + @Override + public Iterable listNodes() { + Set containers = Sets.newHashSet(); + for (Container container : api.getRemoteApi().listContainers(ListContainerOptions.Builder.all(true))) { + // less efficient than just listNodes but returns richer json + containers.add(api.getRemoteApi().inspectContainer(container.getId())); + } + return containers; + } + + @Override + public Iterable listNodesByIds(final Iterable ids) { + Set containers = Sets.newHashSet(); + for (String id : ids) { + containers.add(api.getRemoteApi().inspectContainer(id)); + } + return containers; + } + + @Override + public Iterable listLocations() { + return ImmutableSet.of(); + } + + @Override + public Container getNode(String id) { + return api.getRemoteApi().inspectContainer(id); + } + + @Override + public void destroyNode(String id) { + api.getRemoteApi().removeContainer(id, RemoveContainerOptions.Builder.force(true)); + } + + @Override + public void rebootNode(String id) { + api.getRemoteApi().stopContainer(id); + api.getRemoteApi().startContainer(id); + } + + @Override + public void resumeNode(String id) { + throw new UnsupportedOperationException("resume not supported"); + } + + @Override + public void suspendNode(String id) { + throw new UnsupportedOperationException("suspend not supported"); + } + +} diff --git a/apis/docker/src/main/java/org/jclouds/docker/config/DockerHttpApiModule.java b/apis/docker/src/main/java/org/jclouds/docker/config/DockerHttpApiModule.java new file mode 100644 index 0000000000..e6da554704 --- /dev/null +++ b/apis/docker/src/main/java/org/jclouds/docker/config/DockerHttpApiModule.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.config; + +import org.jclouds.docker.DockerApi; +import org.jclouds.docker.handlers.DockerErrorHandler; +import org.jclouds.http.HttpErrorHandler; +import org.jclouds.http.annotation.ClientError; +import org.jclouds.http.annotation.Redirection; +import org.jclouds.http.annotation.ServerError; +import org.jclouds.rest.ConfiguresHttpApi; +import org.jclouds.rest.config.HttpApiModule; + +/** + * Configures the Docker connection. + */ +@ConfiguresHttpApi +public class DockerHttpApiModule extends HttpApiModule { + + @Override + protected void bindErrorHandlers() { + bind(HttpErrorHandler.class).annotatedWith(Redirection.class).to(DockerErrorHandler.class); + bind(HttpErrorHandler.class).annotatedWith(ClientError.class).to(DockerErrorHandler.class); + bind(HttpErrorHandler.class).annotatedWith(ServerError.class).to(DockerErrorHandler.class); + } +} diff --git a/apis/docker/src/main/java/org/jclouds/docker/config/DockerParserModule.java b/apis/docker/src/main/java/org/jclouds/docker/config/DockerParserModule.java new file mode 100644 index 0000000000..533184f82e --- /dev/null +++ b/apis/docker/src/main/java/org/jclouds/docker/config/DockerParserModule.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.config; + +import com.google.common.collect.ImmutableMap; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.inject.AbstractModule; +import com.google.inject.Provides; +import org.jclouds.docker.domain.Container; +import org.jclouds.json.config.GsonModule; + +import javax.inject.Singleton; +import java.lang.reflect.Type; +import java.util.Map; + +public class DockerParserModule extends AbstractModule { + + @Override + protected void configure() { + bind(GsonModule.DateAdapter.class).to(GsonModule.Iso8601DateAdapter.class); + } + + @Provides + @Singleton + public Map provideCustomAdapterBindings() { + return new ImmutableMap.Builder() + .put(Container.class, new ContainerTypeAdapter()) + .build(); + } + + protected static class ContainerTypeAdapter implements JsonDeserializer { + + @Override + public Container deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws + JsonParseException { + Gson gson = new GsonBuilder().serializeNulls().create(); + final JsonObject jsonObject = json.getAsJsonObject(); + return gson.fromJson(jsonObject, Container.class); + } + } + +} diff --git a/apis/docker/src/main/java/org/jclouds/docker/config/DockerProperties.java b/apis/docker/src/main/java/org/jclouds/docker/config/DockerProperties.java new file mode 100644 index 0000000000..b870feac0f --- /dev/null +++ b/apis/docker/src/main/java/org/jclouds/docker/config/DockerProperties.java @@ -0,0 +1,26 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.config; + +public class DockerProperties { + + /** + * default Docker host password + */ + public static final String HOST_PASSWORD = "jclouds.docker.host.password"; + +} diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/Config.java b/apis/docker/src/main/java/org/jclouds/docker/domain/Config.java new file mode 100644 index 0000000000..9c80472d96 --- /dev/null +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/Config.java @@ -0,0 +1,457 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.domain; + +import com.google.common.base.Objects; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.gson.annotations.SerializedName; +import org.jclouds.javax.annotation.Nullable; + +import java.beans.ConstructorProperties; +import java.util.List; +import java.util.Map; + +import static com.google.common.base.Preconditions.checkNotNull; + +public class Config { + + @SerializedName("Hostname") + private final String hostname; + @SerializedName("Domainname") + private final String domainName; + @SerializedName("User") + private final String user; + @SerializedName("Memory") + private final int memory; + @SerializedName("MemorySwap") + private final int memorySwap; + @SerializedName("CpuShares") + private final int cpuShares; + @SerializedName("AttachStdin") + private final boolean attachStdin; + @SerializedName("AttachStdout") + private final boolean attachStdout; + @SerializedName("AttachStderr") + private final boolean attachStderr; + @SerializedName("ExposedPorts") + private final Map exposedPorts; + @SerializedName("Tty") + private final boolean tty; + @SerializedName("OpenStdin") + private final boolean openStdin; + @SerializedName("StdinOnce") + private final boolean stdinOnce; + @SerializedName("Env") + private final List env; + @SerializedName("Cmd") + private final List cmd; + @SerializedName("Dns") + private final List dns; + @SerializedName("Image") + private final String imageId; + @SerializedName("Volumes") + private final Map volumes; + @SerializedName("VolumesFrom") + private final String volumesFrom; + @SerializedName("WorkingDir") + private final String workingDir; + @SerializedName("Entrypoint") + private final String entrypoint; + @SerializedName("NetworkDisabled") + private final boolean networkDisabled; + @SerializedName("OnBuild") + private final List onBuild; + + + @ConstructorProperties({ "Hostname", "Domainname", "User", "Memory", "MemorySwap", "CpuShares", "AttachStdin", + "AttachStdout", "AttachStderr", "ExposedPorts", "Tty", "OpenStdin", "StdinOnce", "Env", "Cmd", + "Dns", "Image", "Volumes", "VolumesFrom", "WorkingDir", "Entrypoint", "NetworkDisabled", "OnBuild" }) + protected Config(@Nullable String hostname, @Nullable String domainName, @Nullable String user, + int memory, int memorySwap, int cpuShares, boolean attachStdin, boolean attachStdout, + boolean attachStderr, Map exposedPorts, boolean tty, boolean openStdin, + boolean stdinOnce, @Nullable List env, @Nullable List cmd, + @Nullable List dns, String imageId, @Nullable Map volumes, + @Nullable String volumesFrom, @Nullable String workingDir, @Nullable String entrypoint, + @Nullable boolean networkDisabled, @Nullable List onBuild) { + this.hostname = hostname; + this.domainName = domainName; + this.user = user; + this.memory = checkNotNull(memory, "memory"); + this.memorySwap = checkNotNull(memorySwap, "memorySwap"); + this.cpuShares = checkNotNull(cpuShares, "cpuShares"); + this.attachStdin = checkNotNull(attachStdin, "attachStdin"); + this.attachStdout = checkNotNull(attachStdout, "attachStdout"); + this.attachStderr = checkNotNull(attachStderr, "attachStderr"); + this.exposedPorts = exposedPorts != null ? ImmutableMap.copyOf(exposedPorts) : ImmutableMap. of(); + this.tty = checkNotNull(tty, "tty"); + this.openStdin = checkNotNull(openStdin, "openStdin"); + this.stdinOnce = checkNotNull(stdinOnce, "stdinOnce"); + this.env = env != null ? ImmutableList.copyOf(env) : ImmutableList. of(); + this.cmd = cmd != null ? ImmutableList.copyOf(cmd) : ImmutableList. of(); + this.dns = dns != null ? ImmutableList.copyOf(dns) : ImmutableList. of(); + this.imageId = checkNotNull(imageId, "imageId"); + this.volumes = volumes != null ? ImmutableMap.copyOf(volumes) : ImmutableMap. of(); + this.volumesFrom = volumesFrom; + this.workingDir = workingDir; + this.entrypoint = entrypoint; + this.networkDisabled = networkDisabled; + this.onBuild = onBuild != null ? ImmutableList.copyOf(onBuild) : ImmutableList. of(); + } + + public String getHostname() { + return hostname; + } + + public String getDomainName() { + return domainName; + } + + public String getUser() { + return user; + } + + public int getMemory() { + return memory; + } + + public int getMemorySwap() { + return memorySwap; + } + + public int getCpuShares() { + return cpuShares; + } + + public boolean isAttachStdin() { + return attachStdin; + } + + public boolean isAttachStdout() { + return attachStdout; + } + + public boolean isAttachStderr() { + return attachStderr; + } + + public Map getExposedPorts() { + return exposedPorts; + } + + public boolean isTty() { + return tty; + } + + public boolean isOpenStdin() { + return openStdin; + } + + public boolean isStdinOnce() { + return stdinOnce; + } + + public List getEnv() { + return env; + } + + public List getCmd() { + return cmd; + } + + public List getDns() { + return dns; + } + + public String getImageId() { + return imageId; + } + + public Map getVolumes() { + return volumes; + } + + public String getVolumesFrom() { + return volumesFrom; + } + + public String getWorkingDir() { + return workingDir; + } + + public String getEntrypoint() { + return entrypoint; + } + + public boolean isNetworkDisabled() { + return networkDisabled; + } + + public List getOnBuild() { + return onBuild; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Config that = (Config) o; + + return Objects.equal(this.hostname, that.hostname) && + Objects.equal(this.domainName, that.domainName) && + Objects.equal(this.user, that.user) && + Objects.equal(this.memory, that.memory) && + Objects.equal(this.memorySwap, that.memorySwap) && + Objects.equal(this.cpuShares, that.cpuShares) && + Objects.equal(this.attachStdin, that.attachStdin) && + Objects.equal(this.attachStdout, that.attachStdout) && + Objects.equal(this.attachStderr, that.attachStderr) && + Objects.equal(this.exposedPorts, that.exposedPorts) && + Objects.equal(this.tty, that.tty) && + Objects.equal(this.openStdin, that.openStdin) && + Objects.equal(this.stdinOnce, that.stdinOnce) && + Objects.equal(this.env, that.env) && + Objects.equal(this.cmd, that.cmd) && + Objects.equal(this.dns, that.dns) && + Objects.equal(this.imageId, that.imageId) && + Objects.equal(this.volumes, that.volumes) && + Objects.equal(this.volumesFrom, that.volumesFrom) && + Objects.equal(this.workingDir, that.workingDir) && + Objects.equal(this.entrypoint, that.entrypoint) && + Objects.equal(this.onBuild, that.onBuild); + } + + @Override + public int hashCode() { + return Objects.hashCode(hostname, domainName, user, memory, memorySwap, cpuShares, attachStdin, attachStdout, + attachStderr, exposedPorts, tty, openStdin, stdinOnce, env, cmd, dns, imageId, volumes, + volumesFrom, workingDir, entrypoint, networkDisabled, onBuild); + } + + @Override + public String toString() { + return Objects.toStringHelper(this) + .add("hostname", hostname) + .add("domainName", domainName) + .add("user", user) + .add("memory", memory) + .add("memorySwap", memorySwap) + .add("cpuShares", cpuShares) + .add("attachStdin", attachStdin) + .add("attachStdout", attachStdout) + .add("attachStderr", attachStderr) + .add("exposedPorts", exposedPorts) + .add("tty", tty) + .add("openStdin", openStdin) + .add("stdinOnce", stdinOnce) + .add("env", env) + .add("cmd", cmd) + .add("dns", dns) + .add("imageId", imageId) + .add("volumes", volumes) + .add("volumesFrom", volumesFrom) + .add("workingDir", workingDir) + .add("entrypoint", entrypoint) + .add("networkDisabled", networkDisabled) + .add("onBuild", onBuild) + .toString(); + } + + public static Builder builder() { + return new Builder(); + } + + public Builder toBuilder() { + return builder().fromConfig(this); + } + + public static final class Builder { + private String hostname; + private String domainName; + private String user; + private int memory; + private int memorySwap; + private int cpuShares; + private boolean attachStdin; + private boolean attachStdout; + private boolean attachStderr; + private Map exposedPorts = ImmutableMap.of(); + private boolean tty; + private boolean openStdin; + private boolean stdinOnce; + private List env = ImmutableList.of(); + private List cmd = ImmutableList.of(); + private List dns = ImmutableList.of(); + private String imageId; + private Map volumes = ImmutableMap.of(); + private String volumesFrom; + private String workingDir; + private String entrypoint; + private boolean networkDisabled; + private List onBuild = ImmutableList.of(); + + public Builder hostname(String hostname) { + this.hostname = hostname; + return this; + } + + public Builder domainName(String domainName) { + this.domainName = domainName; + return this; + } + + public Builder user(String user) { + this.user = user; + return this; + } + + public Builder memory(int memory) { + this.memory = memory; + return this; + } + + public Builder memorySwap(int memorySwap) { + this.memorySwap = memorySwap; + return this; + } + + public Builder cpuShares(int cpuShares) { + this.cpuShares = cpuShares; + return this; + } + + public Builder attachStdin(boolean attachStdin) { + this.attachStdin = attachStdin; + return this; + } + + public Builder attachStdout(boolean attachStdout) { + this.attachStdout = attachStdout; + return this; + } + + public Builder attachStderr(boolean attachStderr) { + this.attachStderr = attachStderr; + return this; + } + + public Builder exposedPorts(Map exposedPorts) { + this.exposedPorts = ImmutableMap.copyOf(checkNotNull(exposedPorts, "exposedPorts")); + return this; + } + + public Builder tty(boolean tty) { + this.tty = tty; + return this; + } + + public Builder openStdin(boolean openStdin) { + this.openStdin = openStdin; + return this; + } + + public Builder stdinOnce(boolean stdinOnce) { + this.stdinOnce = stdinOnce; + return this; + } + + public Builder env(List env) { + this.env = env; + return this; + } + + public Builder cmd(List cmd) { + this.cmd = ImmutableList.copyOf(checkNotNull(cmd, "cmd")); + return this; + } + + public Builder dns(List dns) { + this.dns = ImmutableList.copyOf(checkNotNull(dns, "dns")); + return this; + } + + public Builder imageId(String imageId) { + this.imageId = imageId; + return this; + } + + public Builder volumes(Map volumes) { + this.volumes = ImmutableMap.copyOf(checkNotNull(volumes, "volumes")); + return this; + } + + public Builder volumesFrom(String volumesFrom) { + this.volumesFrom = volumesFrom; + return this; + } + + public Builder workingDir(String workingDir) { + this.workingDir = workingDir; + return this; + } + + public Builder entrypoint(String entrypoint) { + this.entrypoint = entrypoint; + return this; + } + + public Builder networkDisabled(boolean networkDisabled) { + this.networkDisabled = networkDisabled; + return this; + } + + public Builder onBuild(List onBuild) { + this.onBuild = ImmutableList.copyOf(checkNotNull(onBuild, "onBuild")); + return this; + } + + public Config build() { + return new Config(hostname, domainName, user, memory, memorySwap, cpuShares, attachStdin, attachStdout, + attachStderr, exposedPorts, tty, openStdin, stdinOnce, env, cmd, dns, imageId, volumes, + volumesFrom, workingDir, entrypoint, networkDisabled, onBuild); + } + + public Builder fromConfig(Config in) { + return this + .hostname(in.getHostname()) + .domainName(in.getDomainName()) + .user(in.getUser()) + .memory(in.getMemory()) + .memorySwap(in.getMemorySwap()) + .cpuShares(in.getCpuShares()) + .attachStdin(in.isAttachStdin()) + .attachStdout(in.isAttachStdout()) + .attachStderr(in.isAttachStderr()) + .exposedPorts(in.getExposedPorts()) + .tty(in.isTty()) + .openStdin(in.isOpenStdin()) + .stdinOnce(in.isStdinOnce()) + .env(in.getEnv()) + .cmd(in.getCmd()) + .dns(in.getDns()) + .imageId(in.getImageId()) + .volumes(in.getVolumes()) + .volumesFrom(in.getVolumesFrom()) + .workingDir(in.getWorkingDir()) + .entrypoint(in.getEntrypoint()) + .networkDisabled(in.isNetworkDisabled()) + .onBuild(in.getOnBuild()); + } + + } +} diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/Container.java b/apis/docker/src/main/java/org/jclouds/docker/domain/Container.java new file mode 100644 index 0000000000..27525292e0 --- /dev/null +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/Container.java @@ -0,0 +1,389 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.domain; + +import com.google.common.base.Objects; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.gson.annotations.SerializedName; +import org.jclouds.javax.annotation.Nullable; + +import java.beans.ConstructorProperties; +import java.util.List; +import java.util.Map; + +import static com.google.common.base.Preconditions.checkNotNull; + +public class Container { + + @SerializedName("Id") + private final String id; + @SerializedName("Name") + private final String name; + @SerializedName("Created") + private final String created; + @SerializedName("Path") + private final String path; + @SerializedName("Args") + private final String[] args; + @SerializedName("Config") + private final Config containerConfig; + @SerializedName("State") + private final State state; + @SerializedName("Image") + private final String image; + @SerializedName("NetworkSettings") + private final NetworkSettings networkSettings; + @SerializedName("ResolvConfPath") + private final String resolvConfPath; + @SerializedName("Driver") + private final String driver; + @SerializedName("ExecDriver") + private final String execDriver; + @SerializedName("Volumes") + private final Map volumes; + @SerializedName("VolumesRW") + private final Map volumesRW; + @SerializedName("Command") + private final String command; + @SerializedName("Status") + private final String status; + @SerializedName("HostConfig") + private final HostConfig hostConfig; + @SerializedName("Ports") + private final List ports; + @SerializedName("HostnamePath") + private final String hostnamePath; + + @ConstructorProperties({ "Id", "Name", "Created", "Path", "Args", "Config", "State", "Image", "NetworkSettings", + "ResolvConfPath", "Driver", "ExecDriver", "Volumes", "VolumesRW", "Command", "Status", "HostConfig", + "Ports", "HostnamePath" }) + protected Container(String id, @Nullable String name, @Nullable String created, @Nullable String path, @Nullable String[] args, + @Nullable Config containerConfig, @Nullable State state, @Nullable String image, @Nullable NetworkSettings networkSettings, + @Nullable String resolvConfPath, @Nullable String driver, @Nullable String execDriver, @Nullable Map volumes, + @Nullable Map volumesRW, @Nullable String command, @Nullable String status, + @Nullable HostConfig hostConfig, @Nullable List ports, @Nullable String hostnamePath) { + this.id = checkNotNull(id, "id"); + this.name = name; + this.created = created; + this.path = path; + this.args = args; + this.containerConfig = containerConfig; + this.state = state; + this.image = image; + this.networkSettings = networkSettings; + this.resolvConfPath = resolvConfPath; + this.driver = driver; + this.execDriver = execDriver; + this.volumes = volumes != null ? ImmutableMap.copyOf(volumes) : ImmutableMap.of(); + this.volumesRW = volumesRW != null ? ImmutableMap.copyOf(volumesRW) : ImmutableMap.of(); + this.command = command; + this.status = status; + this.hostConfig = hostConfig; + this.ports = ports != null ? ImmutableList.copyOf(ports) : ImmutableList.of(); + this.hostnamePath = hostnamePath; + } + + public String getId() { + return id; + } + + public String getName() { + return name; + } + + public String getCreated() { + return created; + } + + public String getPath() { + return path; + } + + public String[] getArgs() { + return args; + } + + public Config getContainerConfig() { + return containerConfig; + } + + public State getState() { + return state; + } + + public String getImage() { + return image; + } + + public NetworkSettings getNetworkSettings() { + return networkSettings; + } + + public String getResolvConfPath() { + return resolvConfPath; + } + + public String getDriver() { + return driver; + } + + public String getExecDriver() { + return execDriver; + } + + public Map getVolumes() { + return volumes; + } + + public Map getvolumesRW() { + return volumesRW; + } + + public String getCommand() { + return command; + } + + public String getStatus() { + return status; + } + + public HostConfig getHostConfig() { + return hostConfig; + } + + public List getPorts() { + return ports; + } + + public String getHostnamePath() { + return hostnamePath; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Container that = (Container) o; + + return Objects.equal(this.id, that.id) && + Objects.equal(this.name, that.name) && + Objects.equal(this.created, that.created) && + Objects.equal(this.path, that.path) && + Objects.equal(this.args, that.args) && + Objects.equal(this.containerConfig, that.containerConfig) && + Objects.equal(this.state, that.state) && + Objects.equal(this.image, that.image) && + Objects.equal(this.networkSettings, that.networkSettings) && + Objects.equal(this.resolvConfPath, that.resolvConfPath) && + Objects.equal(this.driver, that.driver) && + Objects.equal(this.execDriver, that.execDriver) && + Objects.equal(this.volumes, that.volumes) && + Objects.equal(this.volumesRW, that.volumesRW) && + Objects.equal(this.command, that.command) && + Objects.equal(this.status, that.status) && + Objects.equal(this.hostConfig, that.hostConfig) && + Objects.equal(this.ports, that.ports) && + Objects.equal(this.hostnamePath, that.hostnamePath); + } + + @Override + public int hashCode() { + return Objects.hashCode(id, name, created, path, args, containerConfig, state, image, networkSettings, resolvConfPath, + driver, execDriver, volumes, volumesRW, command, status, hostConfig, ports, hostnamePath); + } + + @Override + public String toString() { + return Objects.toStringHelper(this) + .add("id", id) + .add("name", name) + .add("created", created) + .add("path", path) + .add("args", args) + .add("containerConfig", containerConfig) + .add("state", state) + .add("image", image) + .add("networkSettings", networkSettings) + .add("resolvConfPath", resolvConfPath) + .add("driver", driver) + .add("execDriver", execDriver) + .add("volumes", volumes) + .add("volumesRW", volumesRW) + .add("command", command) + .add("status", status) + .add("hostConfig", hostConfig) + .add("ports", ports) + .add("hostnamePath", hostnamePath) + .toString(); + } + + public static Builder builder() { + return new Builder(); + } + + public Builder toBuilder() { + return builder().fromContainer(this); + } + + public static final class Builder { + + private String id; + private String name; + private String created; + private String path; + private String[] args; + private Config containerConfig; + private State state; + private String image; + private NetworkSettings networkSettings; + private String resolvConfPath; + private String driver; + private String execDriver; + private Map volumes = ImmutableMap.of(); + private Map volumesRW = ImmutableMap.of(); + private String command; + private String status; + private HostConfig hostConfig; + private List ports = ImmutableList.of(); + private String hostnamePath; + + public Builder id(String id) { + this.id = id; + return this; + } + + public Builder name(String name) { + this.name = name; + return this; + } + + public Builder created(String created) { + this.created = created; + return this; + } + + public Builder path(String path) { + this.path = path; + return this; + } + + public Builder args(String[] args) { + this.args = args; + return this; + } + + public Builder containerConfig(Config containerConfig) { + this.containerConfig = containerConfig; + return this; + } + + public Builder state(State state) { + this.state = state; + return this; + } + + public Builder image(String imageName) { + this.image = imageName; + return this; + } + + public Builder networkSettings(NetworkSettings networkSettings) { + this.networkSettings = networkSettings; + return this; + } + + public Builder resolvConfPath(String resolvConfPath) { + this.resolvConfPath = resolvConfPath; + return this; + } + + public Builder driver(String driver) { + this.driver = driver; + return this; + } + + public Builder execDriver(String execDriver) { + this.execDriver = execDriver; + return this; + } + + public Builder volumes(Map volumes) { + this.volumes = volumes; + return this; + } + + public Builder volumesRW(Map volumesRW) { + this.volumesRW = volumesRW; + return this; + } + + public Builder command(String command) { + this.command = command; + return this; + } + + public Builder status(String status) { + this.status = status; + return this; + } + + public Builder hostConfig(HostConfig hostConfig) { + this.hostConfig = hostConfig; + return this; + } + + public Builder ports(List ports) { + this.ports = ports; + return this; + } + + public Builder hostnamePath(String hostnamePath) { + this.hostnamePath = hostnamePath; + return this; + } + + public Container build() { + return new Container(id, name, created, path, args, containerConfig, state, image, networkSettings, resolvConfPath, + driver, execDriver, volumes, volumesRW, command, status, hostConfig, ports, hostnamePath); + } + + public Builder fromContainer(Container in) { + return this + .id(in.getId()) + .name(in.getName()) + .created(in.getCreated()) + .path(in.getPath()) + .args(in.getArgs()) + .containerConfig(in.getContainerConfig()) + .state(in.getState()) + .image(in.getImage()) + .networkSettings(in.getNetworkSettings()) + .resolvConfPath(in.getResolvConfPath()) + .driver(in.getDriver()) + .execDriver(in.getExecDriver()) + .volumes(in.getVolumes()) + .volumesRW(in.getvolumesRW()) + .command(in.getCommand()) + .status(in.getStatus()) + .hostConfig(in.getHostConfig()) + .ports(in.getPorts()) + .hostnamePath(in.getHostnamePath()); + } + } +} diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/ExposedPorts.java b/apis/docker/src/main/java/org/jclouds/docker/domain/ExposedPorts.java new file mode 100644 index 0000000000..b7a831a64c --- /dev/null +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/ExposedPorts.java @@ -0,0 +1,106 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.domain; + +import static com.google.common.base.Preconditions.checkNotNull; +import java.beans.ConstructorProperties; +import java.util.Set; + +import org.jclouds.javax.annotation.Nullable; + +import com.google.common.base.Objects; +import com.google.common.collect.ImmutableSet; +import com.google.gson.annotations.SerializedName; + +public class ExposedPorts { + + @SerializedName("PortAndProtocol") + private final String portAndProtocol; + @SerializedName("HostPorts") + private final Set hostPorts; + + @ConstructorProperties({ "PortAndProtocol", "HostPorts" }) + protected ExposedPorts(String portAndProtocol, @Nullable Set hostPorts) { + this.portAndProtocol = checkNotNull(portAndProtocol, "portAndProtocol"); + this.hostPorts = hostPorts != null ? ImmutableSet.copyOf(hostPorts) : ImmutableSet. of(); + } + + public String getPortAndProtocol() { + return portAndProtocol; + } + + public Set getHostPorts() { + return hostPorts; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ExposedPorts that = (ExposedPorts) o; + + return Objects.equal(this.portAndProtocol, that.portAndProtocol) && + Objects.equal(this.hostPorts, that.hostPorts); + } + + @Override + public int hashCode() { + return Objects.hashCode(portAndProtocol, hostPorts); + } + + @Override + public String toString() { + return Objects.toStringHelper(this) + .add("portAndProtocol", portAndProtocol) + .add("hostPorts", hostPorts) + .toString(); + } + + public static Builder builder() { + return new Builder(); + } + + public Builder toBuilder() { + return builder().fromExposedPorts(this); + } + + public static final class Builder { + + private String portAndProtocol; + private Set hostPorts = ImmutableSet.of(); + + public Builder portAndProtocol(String portAndProtocol) { + this.portAndProtocol = portAndProtocol; + return this; + } + + public Builder hostPorts(Set hostPorts) { + this.hostPorts = ImmutableSet.copyOf(checkNotNull(hostPorts, "hostPorts")); + return this; + } + + public ExposedPorts build() { + return new ExposedPorts(portAndProtocol, hostPorts); + } + + public Builder fromExposedPorts(ExposedPorts in) { + return this.portAndProtocol(in.getPortAndProtocol()) + .hostPorts(in.getHostPorts()); + } + } +} diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/HostConfig.java b/apis/docker/src/main/java/org/jclouds/docker/domain/HostConfig.java new file mode 100644 index 0000000000..275961fe9e --- /dev/null +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/HostConfig.java @@ -0,0 +1,242 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.domain; + +import static com.google.common.base.Preconditions.checkNotNull; +import java.beans.ConstructorProperties; +import java.util.List; +import java.util.Map; + +import org.jclouds.javax.annotation.Nullable; + +import com.google.common.base.Objects; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.gson.annotations.SerializedName; + +public class HostConfig { + + @SerializedName("ContainerIDFile") + private final String containerIDFile; + @SerializedName("Binds") + private final List binds; + @SerializedName("LxcConf") + private final Map lxcConf; + @SerializedName("Privileged") + private final boolean privileged; + @SerializedName("Dns") + private final String dns; + @SerializedName("DnsSearch") + private final String dnsSearch; + @SerializedName("PortBindings") + private final Map>> portBindings; + @SerializedName("Links") + private final List links; + @SerializedName("PublishAllPorts") + private final boolean publishAllPorts; + @SerializedName("VolumesFrom") + private final List volumesFrom; + + @ConstructorProperties({ "ContainerIDFile", "Binds", "LxcConf", "Privileged", "Dns", "DnsSearch", "PortBindings", + "Links", "PublishAllPorts", "VolumesFrom" }) + protected HostConfig(@Nullable String containerIDFile, @Nullable List binds, + Map lxcConf, boolean privileged, @Nullable String dns, + @Nullable String dnsSearch, @Nullable Map>> portBindings, + @Nullable List links, boolean publishAllPorts, @Nullable List volumesFrom) { + this.containerIDFile = containerIDFile; + this.binds = binds != null ? ImmutableList.copyOf(binds) : ImmutableList. of(); + this.lxcConf = lxcConf != null ? ImmutableMap.copyOf(lxcConf) : ImmutableMap. of(); + this.privileged = checkNotNull(privileged, "privileged"); + this.dns = dns; + this.dnsSearch = dnsSearch; + this.portBindings = portBindings != null ? ImmutableMap.copyOf(portBindings) : ImmutableMap.>> of(); + this.links = links != null ? ImmutableList.copyOf(links) : ImmutableList. of(); + this.publishAllPorts = checkNotNull(publishAllPorts, "publishAllPorts"); + this.volumesFrom = volumesFrom != null ? ImmutableList.copyOf(volumesFrom) : ImmutableList. of(); + } + + public String getContainerIDFile() { + return containerIDFile; + } + + public List getBinds() { + return binds; + } + + public Map getLxcConf() { + return lxcConf; + } + + public boolean isPrivileged() { + return privileged; + } + + public String getDns() { return dns; } + + public String getDnsSearch() { return dnsSearch; } + + public Map>> getPortBindings() { + return portBindings; + } + + @Nullable + public List getLinks() { + return links; + } + + public boolean isPublishAllPorts() { + return publishAllPorts; + } + + public List getVolumesFrom() { + return volumesFrom; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + HostConfig that = (HostConfig) o; + + return Objects.equal(this.containerIDFile, that.containerIDFile) && + Objects.equal(this.binds, that.binds) && + Objects.equal(this.lxcConf, that.lxcConf) && + Objects.equal(this.privileged, that.privileged) && + Objects.equal(this.dns, that.dns) && + Objects.equal(this.dnsSearch, that.dnsSearch) && + Objects.equal(this.portBindings, that.portBindings) && + Objects.equal(this.links, that.links) && + Objects.equal(this.publishAllPorts, that.publishAllPorts) && + Objects.equal(this.volumesFrom, that.volumesFrom); + } + + @Override + public int hashCode() { + return Objects.hashCode(containerIDFile, binds, lxcConf, privileged, dns, dnsSearch, portBindings, links, + publishAllPorts, volumesFrom); + } + + @Override + public String toString() { + return Objects.toStringHelper(this) + .add("containerIDFile", containerIDFile) + .add("binds", binds) + .add("lxcConf", lxcConf) + .add("privileged", privileged) + .add("dns", dns) + .add("dnsSearch", dnsSearch) + .add("portBindings", portBindings) + .add("links", links) + .add("publishAllPorts", publishAllPorts) + .add("volumesFrom", volumesFrom) + .toString(); + } + + public static Builder builder() { + return new Builder(); + } + + public Builder toBuilder() { + return builder().fromHostConfig(this); + } + + public static final class Builder { + + private String containerIDFile; + private List binds = Lists.newArrayList(); + private Map lxcConf = Maps.newLinkedHashMap(); + private boolean privileged; + private String dns; + private String dnsSearch; + private Map>> portBindings = Maps.newLinkedHashMap(); + private List links = Lists.newArrayList(); + private boolean publishAllPorts; + private List volumesFrom = Lists.newArrayList(); + + public Builder containerIDFile(String containerIDFile) { + this.containerIDFile = containerIDFile; + return this; + } + + public Builder binds(List binds) { + this.binds.addAll(checkNotNull(binds, "binds")); + return this; + } + + public Builder lxcConf(Map lxcConf) { + this.lxcConf.putAll(checkNotNull(lxcConf, "lxcConf")); + return this; + } + + public Builder privileged(boolean privileged) { + this.privileged = privileged; + return this; + } + + public Builder dns(String dns) { + this.dns = dns; + return this; + } + + public Builder dnsSearch(String dnsSearch) { + this.dnsSearch = dnsSearch; + return this; + } + + public Builder links(List links) { + this.links.addAll(checkNotNull(links, "links")); + return this; + } + + public Builder portBindings(Map>> portBindings) { + this.portBindings.putAll(portBindings); + return this; + } + + public Builder publishAllPorts(boolean publishAllPorts) { + this.publishAllPorts = publishAllPorts; + return this; + } + + public Builder volumesFrom(List volumesFrom) { + this.volumesFrom.addAll(checkNotNull(volumesFrom, "volumesFrom")); + return this; + } + + public HostConfig build() { + return new HostConfig(containerIDFile, binds, lxcConf, privileged, dns, dnsSearch, portBindings, links, + publishAllPorts, volumesFrom); + } + + public Builder fromHostConfig(HostConfig in) { + return this + .containerIDFile(in.getContainerIDFile()) + .binds(in.getBinds()) + .lxcConf(in.getLxcConf()) + .privileged(in.isPrivileged()) + .dns(in.getDns()) + .dnsSearch(in.getDnsSearch()) + .links(in.getLinks()) + .portBindings(in.getPortBindings()) + .publishAllPorts(in.isPublishAllPorts()) + .volumesFrom(in.getVolumesFrom()); + } + } +} diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/Image.java b/apis/docker/src/main/java/org/jclouds/docker/domain/Image.java new file mode 100644 index 0000000000..186ece32f4 --- /dev/null +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/Image.java @@ -0,0 +1,239 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.domain; + +import com.google.common.base.Objects; +import com.google.common.collect.ImmutableList; +import com.google.gson.annotations.SerializedName; +import org.jclouds.javax.annotation.Nullable; + +import java.beans.ConstructorProperties; +import java.util.List; + +import static com.google.common.base.Preconditions.checkNotNull; + +public class Image { + + @SerializedName("Id") + private final String id; + @SerializedName("Parent") + private final String parent; + @SerializedName("Created") + private final String created; + @SerializedName("Container") + private final String container; + @SerializedName("DockerVersion") + private final String dockerVersion; + @SerializedName("Architecture") + private final String architecture; + @SerializedName("Os") + private final String os; + @SerializedName("Size") + private final long size; + @SerializedName("VirtualSize") + private final long virtualSize; + @SerializedName("RepoTags") + private final List repoTags; + + @ConstructorProperties({ "Id", "Parent", "Created", "Container", "DockerVersion", "Architecture", "Os", "Size", + "VirtualSize", "RepoTags", "Architecture" }) + protected Image(String id, @Nullable String parent, @Nullable String created, @Nullable String container, + @Nullable String dockerVersion, @Nullable String architecture, @Nullable String os, long size, + @Nullable long virtualSize, @Nullable List repoTags) { + this.id = checkNotNull(id, "id"); + this.parent = parent; + this.created = created; + this.container = container; + this.dockerVersion = dockerVersion; + this.architecture = architecture; + this.os = os; + this.size = size; + this.virtualSize = virtualSize; + this.repoTags = repoTags != null ? ImmutableList.copyOf(repoTags) : ImmutableList. of(); + } + + public String getId() { + return id; + } + + public String getParent() { + return parent; + } + + public String getCreated() { + return created; + } + + public String getContainer() { + return container; + } + + public String getDockerVersion() { + return dockerVersion; + } + + public String getArchitecture() { + return architecture; + } + + public String getOs() { + return os; + } + + public long getSize() { + return size; + } + + public long getVirtualSize() { + return virtualSize; + } + + public List getRepoTags() { + return repoTags; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Image that = (Image) o; + + return Objects.equal(this.id, that.id) && + Objects.equal(this.parent, that.parent) && + Objects.equal(this.created, that.created) && + Objects.equal(this.container, that.container) && + Objects.equal(this.dockerVersion, that.dockerVersion) && + Objects.equal(this.architecture, that.architecture) && + Objects.equal(this.os, that.os) && + Objects.equal(this.size, that.size) && + Objects.equal(this.virtualSize, that.virtualSize); + } + + @Override + public int hashCode() { + return Objects.hashCode(id, parent, created, container, dockerVersion, architecture, os, size, + virtualSize); + } + + @Override + public String toString() { + return Objects.toStringHelper(this) + .add("id", id) + .add("parent", parent) + .add("created", created) + .add("container", container) + .add("dockerVersion", dockerVersion) + .add("architecture", architecture) + .add("os", os) + .add("size", size) + .add("virtualSize", virtualSize) + .add("repoTags", repoTags) + .toString(); + } + + public static Builder builder() { + return new Builder(); + } + + public Builder toBuilder() { + return builder().fromImage(this); + } + + public static final class Builder { + + private String id; + private String parent; + private String created; + private String container; + private String dockerVersion; + private String architecture; + private String os; + private long size; + private long virtualSize; + private List repoTags = ImmutableList.of(); + + public Builder id(String id) { + this.id = id; + return this; + } + + public Builder parent(String parent) { + this.parent = parent; + return this; + } + + public Builder created(String created) { + this.created = created; + return this; + } + + public Builder container(String container) { + this.container = container; + return this; + } + + public Builder dockerVersion(String dockerVersion) { + this.dockerVersion = dockerVersion; + return this; + } + + public Builder architecture(String architecture) { + this.architecture = architecture; + return this; + } + + public Builder os(String os) { + this.os = os; + return this; + } + + public Builder size(long size) { + this.size = size; + return this; + } + + public Builder virtualSize(long virtualSize) { + this.virtualSize = virtualSize; + return this; + } + + public Builder repoTags(List repoTags) { + this.repoTags = ImmutableList.copyOf(checkNotNull(repoTags, "repoTags")); + return this; + } + + public Image build() { + return new Image(id, parent, created, container, dockerVersion, architecture, os, size, + virtualSize, repoTags); + } + + public Builder fromImage(Image in) { + return this + .id(in.getId()) + .parent(in.getParent()) + .created(in.getCreated()) + .container(in.getContainer()) + .dockerVersion(in.getDockerVersion()) + .architecture(in.getArchitecture()) + .os(in.getOs()) + .size(in.getSize()) + .virtualSize(in.getVirtualSize()); + //DO NOT add .repoTags(in.getRepoTags()); + } + } +} diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/NetworkSettings.java b/apis/docker/src/main/java/org/jclouds/docker/domain/NetworkSettings.java new file mode 100644 index 0000000000..23f3fbede1 --- /dev/null +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/NetworkSettings.java @@ -0,0 +1,175 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.domain; + +import com.google.common.base.Objects; +import com.google.common.collect.ImmutableMap; +import com.google.gson.annotations.SerializedName; +import org.jclouds.javax.annotation.Nullable; + +import java.beans.ConstructorProperties; +import java.util.List; +import java.util.Map; + +import static com.google.common.base.Preconditions.checkNotNull; + +public class NetworkSettings { + + @SerializedName("IPAddress") + private final String ipAddress; + @SerializedName("IPPrefixLen") + private final int ipPrefixLen; + @SerializedName("Gateway") + private final String gateway; + @SerializedName("Bridge") + private final String bridge; + @SerializedName("PortMapping") + private final String portMapping; + @SerializedName("Ports") + private final Map>> ports; + + @ConstructorProperties({ "IPAddress", "IPPrefixLen", "Gateway", "Bridge", "PortMapping", "Ports" }) + protected NetworkSettings(String ipAddress, int ipPrefixLen, String gateway, String bridge, + @Nullable String portMapping, @Nullable Map>> ports) { + this.ipAddress = checkNotNull(ipAddress, "ipAddress"); + this.ipPrefixLen = checkNotNull(ipPrefixLen, "ipPrefixLen"); + this.gateway = checkNotNull(gateway, "gateway"); + this.bridge = checkNotNull(bridge, "bridge"); + this.portMapping = portMapping; + this.ports = ports != null ? ImmutableMap.copyOf(ports) : ImmutableMap.>> of(); + } + + public String getIpAddress() { + return ipAddress; + } + + public int getIpPrefixLen() { + return ipPrefixLen; + } + + public String getGateway() { + return gateway; + } + + public String getBridge() { + return bridge; + } + + public String getPortMapping() { + return portMapping; + } + + public Map>> getPorts() { + return ports; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + NetworkSettings that = (NetworkSettings) o; + + return Objects.equal(this.ipAddress, that.ipAddress) && + Objects.equal(this.ipPrefixLen, that.ipPrefixLen) && + Objects.equal(this.gateway, that.gateway) && + Objects.equal(this.bridge, that.bridge) && + Objects.equal(this.portMapping, that.portMapping) && + Objects.equal(this.ports, that.ports); + } + + @Override + public int hashCode() { + return Objects.hashCode(ipAddress, ipPrefixLen, gateway, bridge, portMapping, ports); + } + + @Override + public String toString() { + return Objects.toStringHelper(this) + .add("ipAddress", ipAddress) + .add("ipPrefixLen", ipPrefixLen) + .add("gateway", gateway) + .add("bridge", bridge) + .add("portMapping", portMapping) + .add("ports", ports) + .toString(); + } + + public static Builder builder() { + return new Builder(); + } + + public Builder toBuilder() { + return builder().fromNetworkSettings(this); + } + + public static final class Builder { + + private String ipAddress; + private int ipPrefixLen; + private String gateway; + private String bridge; + private String portMapping; + private Map>> ports = ImmutableMap.of(); + + public Builder ipAddress(String ipAddress) { + this.ipAddress = ipAddress; + return this; + } + + public Builder ipPrefixLen(int ipPrefixLen) { + this.ipPrefixLen = ipPrefixLen; + return this; + } + + public Builder gateway(String gateway) { + this.gateway = gateway; + return this; + } + + public Builder bridge(String bridge) { + this.bridge = bridge; + return this; + } + + public Builder portMapping(String portMapping) { + this.portMapping = portMapping; + return this; + } + + public Builder ports(Map>> ports) { + this.ports = ImmutableMap.copyOf(checkNotNull(ports, "ports")); + return this; + } + + public NetworkSettings build() { + return new NetworkSettings(ipAddress, ipPrefixLen, gateway, bridge, portMapping, ports); + } + + public Builder fromNetworkSettings(NetworkSettings in) { + return this + .ipAddress(in.getIpAddress()) + .ipPrefixLen(in.getIpPrefixLen()) + .gateway(in.getGateway()) + .bridge(in.getBridge()) + .portMapping(in.getPortMapping()) + .ports(in.getPorts()); + } + + } + +} diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/Port.java b/apis/docker/src/main/java/org/jclouds/docker/domain/Port.java new file mode 100644 index 0000000000..b49dfa6945 --- /dev/null +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/Port.java @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.domain; + +import com.google.common.base.Objects; +import com.google.gson.annotations.SerializedName; + +import java.beans.ConstructorProperties; + +import static com.google.common.base.Preconditions.checkNotNull; + +public class Port { + + @SerializedName("PrivatePort") + private final int privatePort; + @SerializedName("PublicPort") + private final int publicPort; + @SerializedName("Type") + private final String type; + @SerializedName("IP") + private final String ip; + + @ConstructorProperties({ "PrivatePort", "PublicPort", "Type", "IP" }) + protected Port(int privatePort, int publicPort, String type, String ip) { + this.privatePort = checkNotNull(privatePort, "privatePort"); + this.publicPort = checkNotNull(publicPort, "publicPort"); + this.type = checkNotNull(type, "type"); + this.ip = checkNotNull(ip, "ip"); + } + + public int getPrivatePort() { + return privatePort; + } + + public int getPublicPort() { + return publicPort; + } + + public String getType() { + return type; + } + + public String getIp() { + return ip; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Port that = (Port) o; + + return Objects.equal(this.privatePort, that.privatePort) && + Objects.equal(this.publicPort, that.publicPort) && + Objects.equal(this.type, that.type) && + Objects.equal(this.ip, that.ip); + } + + @Override + public int hashCode() { + return Objects.hashCode(privatePort, publicPort, type, ip); + } + + @Override + public String toString() { + return Objects.toStringHelper(this) + .add("privatePort", privatePort) + .add("publicPort", publicPort) + .add("type", type) + .add("ip", ip) + .toString(); + } +} diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/State.java b/apis/docker/src/main/java/org/jclouds/docker/domain/State.java new file mode 100644 index 0000000000..ac1ba85e01 --- /dev/null +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/State.java @@ -0,0 +1,166 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.domain; + +import static com.google.common.base.Preconditions.checkNotNull; +import java.beans.ConstructorProperties; + +import com.google.common.base.Objects; +import com.google.gson.annotations.SerializedName; + +public class State { + @SerializedName("Pid") + private final int pid; + @SerializedName("Running") + private final boolean running; + @SerializedName("ExitCode") + private final int exitCode; + @SerializedName("StartedAt") + private final String startedAt; + @SerializedName("FinishedAt") + private final String finishedAt; + @SerializedName("Ghost") + private final boolean ghost; + + @ConstructorProperties({ "Pid", "Running", "ExitCode", "StartedAt", "FinishedAt", "Ghost" }) + protected State(int pid, boolean running, int exitCode, String startedAt, String finishedAt, boolean ghost) { + this.pid = checkNotNull(pid, "pid"); + this.running = checkNotNull(running, "running"); + this.exitCode = checkNotNull(exitCode, "exitCode"); + this.startedAt = checkNotNull(startedAt, "startedAt"); + this.finishedAt = checkNotNull(finishedAt, "finishedAt"); + this.ghost = checkNotNull(ghost, "ghost"); + } + + public int getPid() { + return pid; + } + + public boolean isRunning() { + return running; + } + + public int getExitCode() { + return exitCode; + } + + public String getStartedAt() { + return startedAt; + } + + public String getFinishedAt() { + return finishedAt; + } + + public boolean isGhost() { + return ghost; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + State that = (State) o; + + return Objects.equal(this.pid, that.pid) && + Objects.equal(this.running, that.running) && + Objects.equal(this.exitCode, that.exitCode) && + Objects.equal(this.startedAt, that.startedAt) && + Objects.equal(this.finishedAt, that.finishedAt) && + Objects.equal(this.ghost, that.ghost); + } + + @Override + public int hashCode() { + return Objects.hashCode(pid, running, exitCode, startedAt, finishedAt, ghost); + } + + @Override + public String toString() { + return Objects.toStringHelper(this) + .add("pid", pid) + .add("running", running) + .add("exitCode", exitCode) + .add("startedAt", startedAt) + .add("finishedAt", finishedAt) + .add("ghost", ghost) + .toString(); + } + + public static Builder builder() { + return new Builder(); + } + + public Builder toBuilder() { + return builder().fromState(this); + } + + public static final class Builder { + + private int pid; + private boolean running; + private int exitCode; + private String startedAt; + private String finishedAt; + private boolean ghost; + + public Builder pid(int pid) { + this.pid = pid; + return this; + } + + public Builder running(boolean running) { + this.running = running; + return this; + } + + public Builder exitCode(int exitCode) { + this.exitCode = exitCode; + return this; + } + + public Builder startedAt(String startedAt) { + this.startedAt = startedAt; + return this; + } + + public Builder finishedAt(String finishedAt) { + this.finishedAt = finishedAt; + return this; + } + + public Builder ghost(boolean ghost) { + this.ghost = ghost; + return this; + } + + public State build() { + return new State(pid, running, exitCode, startedAt, finishedAt, ghost); + } + + public Builder fromState(State in) { + return this + .pid(in.getPid()) + .running(in.isRunning()) + .exitCode(in.getExitCode()) + .startedAt(in.getStartedAt()) + .finishedAt(in.getFinishedAt()) + .ghost(in.isGhost()); + } + } +} diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/Version.java b/apis/docker/src/main/java/org/jclouds/docker/domain/Version.java new file mode 100644 index 0000000000..445a225c3d --- /dev/null +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/Version.java @@ -0,0 +1,166 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.domain; + +import static com.google.common.base.Preconditions.checkNotNull; +import java.beans.ConstructorProperties; + +import com.google.common.base.Objects; +import com.google.gson.annotations.SerializedName; + +public class Version { + @SerializedName("Arch") + private final String arch; + @SerializedName("GitCommit") + private final String gitCommit; + @SerializedName("GoVersion") + private final String goVersion; + @SerializedName("KernelVersion") + private final String kernelVersion; + @SerializedName("Os") + private final String os; + @SerializedName("Version") + private final String version; + + @ConstructorProperties({ "Arch", "GitCommit", "GoVersion", "KernelVersion", "Os", "Version" }) + protected Version(String arch, String gitCommit, String goVersion, String kernelVersion, String os, String version) { + this.arch = checkNotNull(arch, "arch"); + this.gitCommit = checkNotNull(gitCommit, "gitCommit"); + this.goVersion = checkNotNull(goVersion, "goVersion"); + this.kernelVersion = checkNotNull(kernelVersion, "kernelVersion"); + this.os = checkNotNull(os, "os"); + this.version = checkNotNull(version, "version"); + } + + public String getArch() { + return arch; + } + + public String getGitCommit() { + return gitCommit; + } + + public String getGoVersion() { + return goVersion; + } + + public String getKernelVersion() { + return kernelVersion; + } + + public String getOs() { + return os; + } + + public String getVersion() { + return version; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Version that = (Version) o; + + return Objects.equal(this.arch, that.arch) && + Objects.equal(this.gitCommit, that.gitCommit) && + Objects.equal(this.goVersion, that.goVersion) && + Objects.equal(this.kernelVersion, that.kernelVersion) && + Objects.equal(this.os, that.os) && + Objects.equal(this.version, that.version); + } + + @Override + public int hashCode() { + return Objects.hashCode(arch, gitCommit, goVersion, kernelVersion, os, version); + } + + @Override + public String toString() { + return Objects.toStringHelper(this) + .add("arch", arch) + .add("gitCommit", gitCommit) + .add("goVersion", goVersion) + .add("kernelVersion", kernelVersion) + .add("os", os) + .add("version", version) + .toString(); + } + + public static Builder builder() { + return new Builder(); + } + + public Builder toBuilder() { + return builder().fromVersion(this); + } + + public static final class Builder { + + private String arch; + private String gitCommit; + private String goVersion; + private String kernelVersion; + private String os; + private String version; + + public Builder arch(String arch) { + this.arch = arch; + return this; + } + + public Builder gitCommit(String gitCommit) { + this.gitCommit = gitCommit; + return this; + } + + public Builder goVersion(String goVersion) { + this.goVersion = goVersion; + return this; + } + + public Builder kernelVersion(String kernelVersion) { + this.kernelVersion = kernelVersion; + return this; + } + + public Builder os(String os) { + this.os = os; + return this; + } + + public Builder version(String version) { + this.version = version; + return this; + } + + public Version build() { + return new Version(arch, gitCommit, goVersion, kernelVersion, os, version); + } + + public Builder fromVersion(Version in) { + return this + .arch(in.getArch()) + .gitCommit(in.getGitCommit()) + .goVersion(in.getGoVersion()) + .kernelVersion(in.getKernelVersion()) + .os(in.getOs()) + .version(in.getVersion()); + } + } +} diff --git a/apis/docker/src/main/java/org/jclouds/docker/features/RemoteApi.java b/apis/docker/src/main/java/org/jclouds/docker/features/RemoteApi.java new file mode 100644 index 0000000000..96b0228f94 --- /dev/null +++ b/apis/docker/src/main/java/org/jclouds/docker/features/RemoteApi.java @@ -0,0 +1,272 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.features; + +import java.io.Closeable; +import java.io.File; +import java.io.InputStream; +import java.util.Set; + +import javax.inject.Named; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.MediaType; + +import org.jclouds.Fallbacks; +import org.jclouds.docker.binders.BindInputStreamToRequest; +import org.jclouds.docker.domain.Config; +import org.jclouds.docker.domain.Container; +import org.jclouds.docker.domain.HostConfig; +import org.jclouds.docker.domain.Image; +import org.jclouds.docker.domain.Version; +import org.jclouds.docker.options.BuildOptions; +import org.jclouds.docker.options.CommitOptions; +import org.jclouds.docker.options.CreateImageOptions; +import org.jclouds.docker.options.DeleteImageOptions; +import org.jclouds.docker.options.ListContainerOptions; +import org.jclouds.docker.options.ListImageOptions; +import org.jclouds.docker.options.RemoveContainerOptions; +import org.jclouds.io.Payload; +import org.jclouds.rest.annotations.BinderParam; +import org.jclouds.rest.annotations.Fallback; +import org.jclouds.rest.annotations.Headers; +import org.jclouds.rest.binders.BindToJsonPayload; + +@Consumes(MediaType.APPLICATION_JSON) +public interface RemoteApi extends Closeable { + + /** + * Get the information of the current docker version. + * + * @return The information of the current docker version. + */ + @Named("version") + @GET + @Path("/version") + Version getVersion(); + + /** + * List all running containers + * + * @return a set of containers + */ + @Named("containers:list") + @GET + @Path("/containers/json") + @Fallback(Fallbacks.EmptySetOnNotFoundOr404.class) + Set 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 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 null 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 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 listImages(ListImageOptions options); + + /** + * Inspect an image + * + * @param imageName The id of the image to inspect. + * @return low-level information on the image name + */ + @Named("image:inspect") + @GET + @Path("/images/{name}/json") + Image inspectImage(@PathParam("name") String imageName); + + /** + * Create an image, either by pull it from the registry or by importing it + * + * @param options the configuration to create an image (@see CreateImageOptions) + * @return a stream of the image creation. + */ + @Named("image:create") + @POST + @Path("/images/create") + InputStream createImage(CreateImageOptions options); + + /** + * Delete an image. + * + * @param name the image name to be deleted + * @return the stream of the deletion execution. + */ + @Named("image:delete") + @DELETE + @Path("/images/{name}") + InputStream deleteImage(@PathParam("name") String name); + + /** + * Remove the image from the filesystem by name + * + * @param name the name of the image to be removed + * @param options the image deletion's options (@see DeleteImageOptions) + * @return the stream of the deletion execution. + */ + @Named("image:delete") + @DELETE + @Path("/images/{name}") + InputStream deleteImage(@PathParam("name") String name, DeleteImageOptions options); + + /** + * Build an image from Dockerfile via stdin + * + * @param inputStream The stream must be a tar archive compressed with one of the following algorithms: identity + * (no compression), gzip, bzip2, xz. + * @param options the image build's options (@see BuildOptions) + * @return a stream of the build execution + */ + @Named("image:build") + @POST + @Path("/build") + @Headers(keys = "Content-Type", values = "application/tar") + InputStream build(Payload inputStream, BuildOptions options); + + /** + * Build an image from Dockerfile via stdin + * + * @param dockerFile The file to be compressed with one of the following algorithms: identity, gzip, bzip2, xz.* + * @param options the image build's options (@see BuildOptions) + * @return a stream of the build execution + */ + @Named("image:build") + @POST + @Path("/build") + @Headers(keys = "Content-Type", values = "application/tar") + InputStream build(@BinderParam(BindInputStreamToRequest.class) File dockerFile, BuildOptions options); + +} diff --git a/apis/docker/src/main/java/org/jclouds/docker/features/internal/Archives.java b/apis/docker/src/main/java/org/jclouds/docker/features/internal/Archives.java new file mode 100644 index 0000000000..43b69c3e30 --- /dev/null +++ b/apis/docker/src/main/java/org/jclouds/docker/features/internal/Archives.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.features.internal; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.collect.Iterables.getLast; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; + +import org.apache.commons.compress.archivers.tar.TarArchiveEntry; +import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; + +import com.google.common.base.Splitter; +import com.google.common.io.Files; + +public class Archives { + + public static File tar(File baseDir, String archivePath) throws IOException { + return tar(baseDir, new File(archivePath)); + } + + public static File tar(File baseDir, File tarFile) throws IOException { + // Check that the directory is a directory, and get its contents + checkArgument(baseDir.isDirectory(), "%s is not a directory", baseDir); + File[] files = baseDir.listFiles(); + String token = getLast(Splitter.on("/").split(baseDir.getAbsolutePath())); + TarArchiveOutputStream tos = new TarArchiveOutputStream(new FileOutputStream(tarFile)); + tos.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU); + try { + for (File file : files) { + TarArchiveEntry tarEntry = new TarArchiveEntry(file); + tarEntry.setName("/" + getLast(Splitter.on(token).split(file.toString()))); + tos.putArchiveEntry(tarEntry); + if (!file.isDirectory()) { + Files.asByteSource(file).copyTo(tos); + } + tos.closeArchiveEntry(); + } + } finally { + tos.close(); + } + return tarFile; + } + +} diff --git a/apis/docker/src/main/java/org/jclouds/docker/handlers/DockerErrorHandler.java b/apis/docker/src/main/java/org/jclouds/docker/handlers/DockerErrorHandler.java new file mode 100644 index 0000000000..855f25fda8 --- /dev/null +++ b/apis/docker/src/main/java/org/jclouds/docker/handlers/DockerErrorHandler.java @@ -0,0 +1,106 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.handlers; + +import com.google.common.base.Throwables; +import com.google.common.io.Closeables; +import org.jclouds.http.HttpCommand; +import org.jclouds.http.HttpErrorHandler; +import org.jclouds.http.HttpResponse; +import org.jclouds.http.HttpResponseException; +import org.jclouds.logging.Logger; +import org.jclouds.rest.AuthorizationException; +import org.jclouds.rest.ResourceNotFoundException; +import org.jclouds.util.Strings2; + +import javax.annotation.Resource; +import java.io.IOException; + +/** + * This will parse and set an appropriate exception on the command object. + *

+ *

+ * Errors are returned with an appropriate HTTP status code, an X-Elastic- Error header specifying + * the error type, and a text description in the HTTP body. + */ +public class DockerErrorHandler implements HttpErrorHandler { + @Resource + protected Logger logger = Logger.NULL; + + public void handleError(HttpCommand command, HttpResponse response) { + // it is important to always read fully and close streams + String message = parseMessage(response); + Exception exception = message != null ? new HttpResponseException(command, response, message) + : new HttpResponseException(command, response); + try { + message = message != null ? message : String.format("%s -> %s", command.getCurrentRequest().getRequestLine(), + response.getStatusLine()); + switch (response.getStatusCode()) { + case 400: + if ((command.getCurrentRequest().getEndpoint().getPath().endsWith("/info")) + || (message != null && message.indexOf("could not be found") != -1)) + exception = new ResourceNotFoundException(message, exception); + else if (message != null && message.indexOf("currently in use") != -1) + exception = new IllegalStateException(message, exception); + else + exception = new IllegalArgumentException(message, exception); + break; + case 401: + exception = new AuthorizationException(message, exception); + break; + case 402: + exception = new IllegalStateException(message, exception); + break; + case 404: + if (!command.getCurrentRequest().getMethod().equals("DELETE")) { + exception = new ResourceNotFoundException(message, exception); + } + break; + case 405: + exception = new IllegalArgumentException(message, exception); + break; + case 409: + exception = new IllegalStateException(message, exception); + break; + } + } finally { + try { + Closeables.close(response.getPayload(), true); + } catch (IOException e) { + // This code will never be reached + throw Throwables.propagate(e); + } + command.setException(exception); + } + } + + public String parseMessage(HttpResponse response) { + if (response.getPayload() == null) + return null; + try { + return Strings2.toString(response.getPayload()); + } catch (IOException e) { + throw Throwables.propagate(e); + } finally { + try { + response.getPayload().close(); + } catch (IOException e) { + throw Throwables.propagate(e); + } + } + } +} diff --git a/apis/docker/src/main/java/org/jclouds/docker/options/BuildOptions.java b/apis/docker/src/main/java/org/jclouds/docker/options/BuildOptions.java new file mode 100644 index 0000000000..4d7196ceee --- /dev/null +++ b/apis/docker/src/main/java/org/jclouds/docker/options/BuildOptions.java @@ -0,0 +1,71 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.options; + +import org.jclouds.http.options.BaseHttpRequestOptions; + +/** + * Options to customize image builder. + */ +public class BuildOptions extends BaseHttpRequestOptions { + + public static final BuildOptions NONE = new BuildOptions(); + + public BuildOptions tag(String tag) { + this.queryParameters.put("tag", tag); + return this; + } + + public BuildOptions verbose(Boolean verbose) { + this.queryParameters.put("verbose", verbose.toString()); + return this; + } + + public BuildOptions nocache(Boolean nocache) { + this.queryParameters.put("nocache", nocache.toString()); + return this; + } + + public static class Builder { + + /** + * @see BuildOptions#tag + */ + public static BuildOptions tag(String tag) { + BuildOptions options = new BuildOptions(); + return options.tag(tag); + } + + /** + * @see BuildOptions#verbose(Boolean) + */ + public static BuildOptions verbose(Boolean verbose) { + BuildOptions options = new BuildOptions(); + return options.verbose(verbose); + } + + /** + * @see BuildOptions#nocache(Boolean) + */ + public static BuildOptions nocache(Boolean nocache) { + BuildOptions options = new BuildOptions(); + return options.nocache(nocache); + } + + } + +} diff --git a/apis/docker/src/main/java/org/jclouds/docker/options/CommitOptions.java b/apis/docker/src/main/java/org/jclouds/docker/options/CommitOptions.java new file mode 100644 index 0000000000..5653fba57e --- /dev/null +++ b/apis/docker/src/main/java/org/jclouds/docker/options/CommitOptions.java @@ -0,0 +1,109 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.options; + +import org.jclouds.http.options.BaseHttpRequestOptions; + +/** + * Options to customize image commit. + */ +public class CommitOptions extends BaseHttpRequestOptions { + + public static final CommitOptions NONE = new CommitOptions(); + + public CommitOptions containerId(String containerId) { + this.queryParameters.put("containerId", containerId); + return this; + } + + public CommitOptions repository(String repository) { + this.queryParameters.put("repository", repository); + return this; + } + + public CommitOptions tag(String tag) { + this.queryParameters.put("tag", tag); + return this; + } + + public CommitOptions message(String message) { + this.queryParameters.put("message", message); + return this; + } + + public CommitOptions author(String author) { + this.queryParameters.put("author", author); + return this; + } + + public CommitOptions run(String run) { + this.queryParameters.put("run", run); + return this; + } + + public static class Builder { + + /** + * @see CommitOptions#containerId + */ + public static CommitOptions containerId(String containerId) { + CommitOptions options = new CommitOptions(); + return options.containerId(containerId); + } + + /** + * @see CommitOptions#repository + */ + public static CommitOptions repository(String repository) { + CommitOptions options = new CommitOptions(); + return options.repository(repository); + } + + /** + * @see CommitOptions#tag + */ + public static CommitOptions tag(String tag) { + CommitOptions options = new CommitOptions(); + return options.tag(tag); + } + + /** + * @see CommitOptions#message + */ + public static CommitOptions message(String message) { + CommitOptions options = new CommitOptions(); + return options.message(message); + } + + /** + * @see CommitOptions#author + */ + public static CommitOptions author(String author) { + CommitOptions options = new CommitOptions(); + return options.author(author); + } + + /** + * @see CommitOptions#run + */ + public static CommitOptions run(String run) { + CommitOptions options = new CommitOptions(); + return options.run(run); + } + + } +} diff --git a/apis/docker/src/main/java/org/jclouds/docker/options/CreateImageOptions.java b/apis/docker/src/main/java/org/jclouds/docker/options/CreateImageOptions.java new file mode 100644 index 0000000000..51dc399d4d --- /dev/null +++ b/apis/docker/src/main/java/org/jclouds/docker/options/CreateImageOptions.java @@ -0,0 +1,95 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.options; + +import org.jclouds.http.options.BaseHttpRequestOptions; + +/** + * Options to customize container creation. + */ +public class CreateImageOptions extends BaseHttpRequestOptions { + + public static final CreateImageOptions NONE = new CreateImageOptions(); + + public CreateImageOptions fromImage(String fromImage) { + this.queryParameters.put("fromImage", fromImage); + return this; + } + + public CreateImageOptions fromSrc(String fromSrc) { + this.queryParameters.put("fromSrc", fromSrc); + return this; + } + + public CreateImageOptions repo(String repo) { + this.queryParameters.put("repo", repo); + return this; + } + + public CreateImageOptions tag(String tag) { + this.queryParameters.put("tag", tag); + return this; + } + + public CreateImageOptions registry(String registry) { + this.queryParameters.put("registry", registry); + return this; + } + + public static class Builder { + /** + * @see CreateImageOptions#fromImage + */ + public static CreateImageOptions fromImage(String fromImage) { + CreateImageOptions options = new CreateImageOptions(); + return options.fromImage(fromImage); + } + + /** + * @see CreateImageOptions#fromSrc + */ + public static CreateImageOptions fromSrc(String fromSrc) { + CreateImageOptions options = new CreateImageOptions(); + return options.fromSrc(fromSrc); + } + + /** + * @see CreateImageOptions#repo + */ + public static CreateImageOptions repo(String repo) { + CreateImageOptions options = new CreateImageOptions(); + return options.repo(repo); + } + + /** + * @see CreateImageOptions#tag + */ + public static CreateImageOptions tag(String tag) { + CreateImageOptions options = new CreateImageOptions(); + return options.tag(tag); + } + + /** + * @see CreateImageOptions#registry + */ + public static CreateImageOptions registry(String registry) { + CreateImageOptions options = new CreateImageOptions(); + return options.registry(registry); + } + + } +} diff --git a/apis/docker/src/main/java/org/jclouds/docker/options/DeleteImageOptions.java b/apis/docker/src/main/java/org/jclouds/docker/options/DeleteImageOptions.java new file mode 100644 index 0000000000..9438616b56 --- /dev/null +++ b/apis/docker/src/main/java/org/jclouds/docker/options/DeleteImageOptions.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.options; + +import org.jclouds.http.options.BaseHttpRequestOptions; + +/** + * Options to customize image deletion. + */ +public class DeleteImageOptions extends BaseHttpRequestOptions { + + public static final DeleteImageOptions NONE = new DeleteImageOptions(); + + public DeleteImageOptions force(Boolean force) { + this.queryParameters.put("force", force.toString()); + return this; + } + + public static class Builder { + + /** + * @see DeleteImageOptions#force + */ + public static DeleteImageOptions force(Boolean force) { + DeleteImageOptions options = new DeleteImageOptions(); + return options.force(force); + } + } + +} diff --git a/apis/docker/src/main/java/org/jclouds/docker/options/ListContainerOptions.java b/apis/docker/src/main/java/org/jclouds/docker/options/ListContainerOptions.java new file mode 100644 index 0000000000..af16664010 --- /dev/null +++ b/apis/docker/src/main/java/org/jclouds/docker/options/ListContainerOptions.java @@ -0,0 +1,97 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.options; + +import org.jclouds.http.options.BaseHttpRequestOptions; + +/** + * Options to customize container's listing. + */ +public class ListContainerOptions extends BaseHttpRequestOptions { + + public static final ListContainerOptions NONE = new ListContainerOptions(); + + public ListContainerOptions all(Boolean all) { + this.queryParameters.put("all", all.toString()); + return this; + } + + public ListContainerOptions limit(Integer limit) { + this.queryParameters.put("limit", limit.toString()); + return this; + } + + public ListContainerOptions since(Integer since) { + this.queryParameters.put("since", since.toString()); + return this; + } + + public ListContainerOptions before(Integer before) { + this.queryParameters.put("before", before.toString()); + return this; + } + + public ListContainerOptions size(Integer size) { + this.queryParameters.put("size", size.toString()); + return this; + } + + public static class Builder { + + /** + * @see ListContainerOptions#all + */ + public static ListContainerOptions all(Boolean all) { + ListContainerOptions options = new ListContainerOptions(); + return options.all(all); + } + + /** + * @see ListContainerOptions#limit(Integer) + */ + public static ListContainerOptions limit(Integer limit) { + ListContainerOptions options = new ListContainerOptions(); + return options.limit(limit); + } + + /** + * @see ListContainerOptions#since(Integer) + */ + public static ListContainerOptions since(Integer since) { + ListContainerOptions options = new ListContainerOptions(); + return options.since(since); + } + + /** + * @see ListContainerOptions#before(Integer) + */ + public static ListContainerOptions before(Integer before) { + ListContainerOptions options = new ListContainerOptions(); + return options.before(before); + } + + /** + * @see ListContainerOptions#limit(Integer) + */ + public static ListContainerOptions size(Integer size) { + ListContainerOptions options = new ListContainerOptions(); + return options.size(size); + } + + } + +} diff --git a/apis/docker/src/main/java/org/jclouds/docker/options/ListImageOptions.java b/apis/docker/src/main/java/org/jclouds/docker/options/ListImageOptions.java new file mode 100644 index 0000000000..fab75d4233 --- /dev/null +++ b/apis/docker/src/main/java/org/jclouds/docker/options/ListImageOptions.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.options; + +import org.jclouds.http.options.BaseHttpRequestOptions; + +/** + * Options to customize image's listing. + */ +public class ListImageOptions extends BaseHttpRequestOptions { + + public static final ListImageOptions NONE = new ListImageOptions(); + + public ListImageOptions all(Boolean all) { + this.queryParameters.put("all", all.toString()); + return this; + } + + public static class Builder { + /** + * @see ListImageOptions#all + */ + public static ListImageOptions all(Boolean all) { + ListImageOptions options = new ListImageOptions(); + return options.all(all); + } + } + +} diff --git a/apis/docker/src/main/java/org/jclouds/docker/options/RemoveContainerOptions.java b/apis/docker/src/main/java/org/jclouds/docker/options/RemoveContainerOptions.java new file mode 100644 index 0000000000..5c3abba3a3 --- /dev/null +++ b/apis/docker/src/main/java/org/jclouds/docker/options/RemoveContainerOptions.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.options; + +import org.jclouds.http.options.BaseHttpRequestOptions; + +/** + * Options to customize container removal. + */ +public class RemoveContainerOptions extends BaseHttpRequestOptions { + + public static final RemoveContainerOptions NONE = new RemoveContainerOptions(); + + public RemoveContainerOptions verbose(Boolean verbose) { + this.queryParameters.put("verbose", verbose.toString()); + return this; + } + + public RemoveContainerOptions force(Boolean force) { + this.queryParameters.put("force", force.toString()); + return this; + } + + public static class Builder { + /** + * @see RemoveContainerOptions#verbose + */ + public static RemoveContainerOptions verbose(Boolean verbose) { + RemoveContainerOptions options = new RemoveContainerOptions(); + return options.verbose(verbose); + } + + /** + * @see RemoveContainerOptions#force + */ + public static RemoveContainerOptions force(Boolean force) { + RemoveContainerOptions options = new RemoveContainerOptions(); + return options.force(force); + } + } +} diff --git a/apis/docker/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata b/apis/docker/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata new file mode 100644 index 0000000000..ca1a6cb4dc --- /dev/null +++ b/apis/docker/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata @@ -0,0 +1 @@ +org.jclouds.docker.DockerApiMetadata \ No newline at end of file diff --git a/apis/docker/src/test/java/org/jclouds/docker/DockerApiMetadataTest.java b/apis/docker/src/test/java/org/jclouds/docker/DockerApiMetadataTest.java new file mode 100644 index 0000000000..41d00679ef --- /dev/null +++ b/apis/docker/src/test/java/org/jclouds/docker/DockerApiMetadataTest.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; + +import org.jclouds.apis.ApiMetadata; +import org.jclouds.apis.Apis; +import org.jclouds.compute.internal.BaseComputeServiceApiMetadataTest; +import org.testng.annotations.Test; + +/** + * Unit tests for the {@link DockerApiMetadata} class. + */ +@Test(groups = "unit", testName = "AbiquoApiMetadataTest") +public class DockerApiMetadataTest extends BaseComputeServiceApiMetadataTest { + + public DockerApiMetadataTest() { + super(new DockerApiMetadata()); + } + + public void testDockerApiRegistered() { + ApiMetadata api = Apis.withId("docker"); + + assertNotNull(api); + assertTrue(api instanceof DockerApiMetadata); + assertEquals(api.getId(), "docker"); + } + +} + diff --git a/apis/docker/src/test/java/org/jclouds/docker/binders/BindInputStreamToRequestTest.java b/apis/docker/src/test/java/org/jclouds/docker/binders/BindInputStreamToRequestTest.java new file mode 100644 index 0000000000..a21999c6f3 --- /dev/null +++ b/apis/docker/src/test/java/org/jclouds/docker/binders/BindInputStreamToRequestTest.java @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.binders; + +import com.google.common.io.CharStreams; +import org.jclouds.http.HttpRequest; +import org.testng.annotations.Test; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStreamReader; + +import static org.testng.Assert.assertEquals; +import static org.testng.AssertJUnit.assertTrue; + +@Test(groups = "unit", testName = "BindInputStreamToRequestTest") +public class BindInputStreamToRequestTest { + + @Test + public void testBindInputStreamToRequest() throws IOException { + BindInputStreamToRequest binder = new BindInputStreamToRequest(); + + HttpRequest request = HttpRequest.builder().method("GET").endpoint("http://test").build(); + request = binder.bindToRequest(request, File.createTempFile("dockerfile", "")); + String rawContent = CharStreams.toString(new InputStreamReader((FileInputStream) request.getPayload().getRawContent(), "UTF-8")); + assertTrue(rawContent.startsWith("Dockerfile")); + assertEquals(request.getPayload().getContentMetadata().getContentType(), "application/tar"); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void testBindInputStreamToRequestWithObjectAsInput() throws IOException { + BindInputStreamToRequest binder = new BindInputStreamToRequest(); + + HttpRequest request = HttpRequest.builder().method("GET").endpoint("http://test").build(); + request = binder.bindToRequest(request, new Object()); + String rawContent = CharStreams.toString(new InputStreamReader((FileInputStream) request.getPayload().getRawContent(), "UTF-8")); + assertTrue(rawContent.startsWith("Dockerfile")); + assertEquals(request.getPayload().getContentMetadata().getContentType(), "application/tar"); + } + + @Test(expectedExceptions = NullPointerException.class) + public void testBindInputStreamToRequestWithNullInput() throws IOException { + BindInputStreamToRequest binder = new BindInputStreamToRequest(); + + HttpRequest request = HttpRequest.builder().method("GET").endpoint("http://test").build(); + request = binder.bindToRequest(request, null); + String rawContent = CharStreams.toString(new InputStreamReader((FileInputStream) request.getPayload().getRawContent(), "UTF-8")); + assertTrue(rawContent.startsWith("Dockerfile")); + assertEquals(request.getPayload().getContentMetadata().getContentType(), "application/tar"); + } +} diff --git a/apis/docker/src/test/java/org/jclouds/docker/compute/BaseDockerApiLiveTest.java b/apis/docker/src/test/java/org/jclouds/docker/compute/BaseDockerApiLiveTest.java new file mode 100644 index 0000000000..77115f248e --- /dev/null +++ b/apis/docker/src/test/java/org/jclouds/docker/compute/BaseDockerApiLiveTest.java @@ -0,0 +1,91 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.compute; + +import com.google.common.base.Charsets; +import com.google.common.collect.ImmutableSet; +import com.google.common.io.CharStreams; +import com.google.common.io.Closeables; +import com.google.common.io.Files; +import com.google.common.io.Resources; +import com.google.inject.Module; +import org.jclouds.Constants; +import org.jclouds.apis.BaseApiLiveTest; +import org.jclouds.docker.DockerApi; +import org.jclouds.docker.features.internal.Archives; +import org.jclouds.io.Payload; +import org.jclouds.io.Payloads; +import org.jclouds.sshj.config.SshjSshClientModule; +import org.testng.Assert; +import org.testng.annotations.Test; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URL; +import java.util.Properties; + +@Test(groups = "live") +public class BaseDockerApiLiveTest extends BaseApiLiveTest { + + public BaseDockerApiLiveTest() { + provider = "docker"; + } + + @Override + protected Iterable setupModules() { + return ImmutableSet.of(getLoggingModule(), new SshjSshClientModule()); + } + + @Override + protected Properties setupProperties() { + Properties overrides = super.setupProperties(); + overrides.setProperty(Constants.PROPERTY_MAX_RETRIES, "15"); + overrides.setProperty("jclouds.ssh.retry-auth", "true"); + return overrides; + } + + protected String consumeStream(InputStream stream, boolean swallowIOException) { + String result = null; + try { + result = CharStreams.toString(new InputStreamReader(stream, Charsets.UTF_8)); + Closeables.close(stream, swallowIOException); + } catch (IOException e) { + Assert.fail(); + } + return result; + } + + protected Payload createPayload() throws IOException { + String folderPath = System.getProperty("user.dir") + "/docker/src/test/resources"; + File parentDir = new File(folderPath + "/archive"); + parentDir.mkdirs(); + URL url = Resources.getResource("Dockerfile"); + String content = Resources.toString(url, Charsets.UTF_8); + final File dockerfile = new File(parentDir.getAbsolutePath() + File.separator + "Dockerfile"); + Files.write(content.getBytes(), dockerfile); + File archive = Archives.tar(parentDir, folderPath + "/archive/archive.tar"); + FileInputStream data = new FileInputStream(archive); + Payload payload = Payloads.newInputStreamPayload(data); + payload.getContentMetadata().setContentLength(data.getChannel().size()); + payload.getContentMetadata().setContentType("application/tar"); + return payload; + } + +} diff --git a/apis/docker/src/test/java/org/jclouds/docker/compute/DockerComputeServiceAdapterLiveTest.java b/apis/docker/src/test/java/org/jclouds/docker/compute/DockerComputeServiceAdapterLiveTest.java new file mode 100644 index 0000000000..faee98267e --- /dev/null +++ b/apis/docker/src/test/java/org/jclouds/docker/compute/DockerComputeServiceAdapterLiveTest.java @@ -0,0 +1,95 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.compute; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNotNull; +import java.util.Properties; +import java.util.Random; + +import org.jclouds.compute.ComputeServiceAdapter.NodeAndInitialCredentials; +import org.jclouds.compute.domain.Hardware; +import org.jclouds.compute.domain.Template; +import org.jclouds.compute.domain.TemplateBuilder; +import org.jclouds.docker.DockerApi; +import org.jclouds.docker.compute.strategy.DockerComputeServiceAdapter; +import org.jclouds.docker.domain.Container; +import org.jclouds.sshj.config.SshjSshClientModule; +import org.testng.annotations.AfterGroups; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; +import com.google.inject.Injector; +import com.google.inject.Module; + +@Test(groups = "live", singleThreaded = true, testName = "DockerComputeServiceAdapterLiveTest") +public class DockerComputeServiceAdapterLiveTest extends BaseDockerApiLiveTest { + + private DockerComputeServiceAdapter adapter; + private TemplateBuilder templateBuilder; + private NodeAndInitialCredentials guest; + + @Override + protected DockerApi create(Properties props, Iterable modules) { + Injector injector = newBuilder().modules(modules).overrides(props).buildInjector(); + adapter = injector.getInstance(DockerComputeServiceAdapter.class); + templateBuilder = injector.getInstance(TemplateBuilder.class); + return injector.getInstance(DockerApi.class); + } + + public void testCreateNodeWithGroupEncodedIntoNameThenStoreCredentials() { + String group = "foo"; + String name = "container-" + new Random().nextInt(); + + Template template = templateBuilder.smallest() + .osDescriptionMatches("jclouds/default:latest").build(); + + guest = adapter.createNodeWithGroupEncodedIntoName(group, name, template); + assertEquals(guest.getNodeId(), guest.getNode().getId() + ""); + } + + public void testListHardwareProfiles() { + Iterable profiles = adapter.listHardwareProfiles(); + assertFalse(Iterables.isEmpty(profiles)); + + for (Hardware profile : profiles) { + assertNotNull(profile); + } + } + + @AfterGroups(groups = "live") + protected void tearDown() { + if (guest != null) { + adapter.destroyNode(guest.getNode().getId() + ""); + } + super.tearDown(); + } + + @Override + protected Iterable setupModules() { + return ImmutableSet.of(getLoggingModule(), new SshjSshClientModule()); + } + + @Override + protected Properties setupProperties() { + Properties properties = super.setupProperties(); + properties.setProperty("jclouds.ssh.max-retries", "10"); + return properties; + } +} diff --git a/apis/docker/src/test/java/org/jclouds/docker/compute/DockerComputeServiceLiveTest.java b/apis/docker/src/test/java/org/jclouds/docker/compute/DockerComputeServiceLiveTest.java new file mode 100644 index 0000000000..cc460c3c4b --- /dev/null +++ b/apis/docker/src/test/java/org/jclouds/docker/compute/DockerComputeServiceLiveTest.java @@ -0,0 +1,142 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.compute; + +import org.jclouds.compute.domain.Image; +import org.jclouds.compute.domain.NodeMetadata; +import org.jclouds.compute.domain.Template; +import org.jclouds.compute.domain.TemplateBuilder; +import org.jclouds.compute.internal.BaseComputeServiceLiveTest; +import org.jclouds.sshj.config.SshjSshClientModule; +import org.testng.Assert; +import org.testng.annotations.Test; + +import com.google.common.base.Optional; +import com.google.common.base.Predicate; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; +import com.google.inject.Module; + +/** + * Live tests for the {@link org.jclouds.compute.ComputeService} integration. + */ +@Test(groups = "live", singleThreaded = true, testName = "DockerComputeServiceLiveTest") +public class DockerComputeServiceLiveTest extends BaseComputeServiceLiveTest { + + private static final String DEFAULT_JCLOUDS_IMAGE = "jclouds/default"; + private Image defaultImage; + + public DockerComputeServiceLiveTest() { + provider = "docker"; + } + + @Override + protected Module getSshModule() { + return new SshjSshClientModule(); + } + + @Override + protected void initializeContext() { + super.initializeContext(); + Optional optionalImage = Iterables.tryFind(client.listImages(), new Predicate() { + @Override + public boolean apply(Image image) { + return image.getName().equals(DEFAULT_JCLOUDS_IMAGE); + } + }); + if (optionalImage.isPresent()) { + defaultImage = optionalImage.get(); + } else { + Assert.fail("Please create an ssh-able image called " + DEFAULT_JCLOUDS_IMAGE); + } + } + + @Override + protected Template buildTemplate(TemplateBuilder templateBuilder) { + return templateBuilder.imageId(defaultImage.getId()).build(); + } + + @Override + public void testOptionToNotBlock() throws Exception { + // Docker ComputeService implementation has to block until the node + // is provisioned, to be able to return it. + } + + @Override + protected void checkTagsInNodeEquals(NodeMetadata node, ImmutableSet tags) { + // Docker does not support tags + } + + @Override + protected void checkUserMetadataContains(NodeMetadata node, ImmutableMap userMetadata) { + // Docker does not support user metadata + } + + @Override + public void testCreateAndRunAService() throws Exception { + // Docker does not support blockOnPort + } + + @Override + @Test(enabled = true, dependsOnMethods = { "testCompareSizes" }) + public void testAScriptExecutionAfterBootWithBasicTemplate() throws Exception { + super.testAScriptExecutionAfterBootWithBasicTemplate(); + } + + @Override + @Test(enabled = true, dependsOnMethods = "testReboot", expectedExceptions = UnsupportedOperationException.class) + public void testSuspendResume() throws Exception { + super.testSuspendResume(); + } + + @Override + @Test(enabled = true, dependsOnMethods = "testSuspendResume") + public void testGetNodesWithDetails() throws Exception { + super.testGetNodesWithDetails(); + } + + @Override + @Test(enabled = true, dependsOnMethods = "testSuspendResume") + public void testListNodes() throws Exception { + super.testListNodes(); + } + + @Override + @Test(enabled = true, dependsOnMethods = "testSuspendResume") + public void testListNodesByIds() throws Exception { + super.testListNodesByIds(); + } + + @Override + @Test(enabled = true, dependsOnMethods = { "testListNodes", "testGetNodesWithDetails", "testListNodesByIds" }) + public void testDestroyNodes() { + super.testDestroyNodes(); + } + + @Test(enabled = true, expectedExceptions = NullPointerException.class) + public void testCorrectExceptionRunningNodesNotFound() throws Exception { + super.testCorrectExceptionRunningNodesNotFound(); + } + + @Test(enabled = true, expectedExceptions = NullPointerException.class) + public void testCorrectAuthException() throws Exception { + // Docker does not support authentication yet + super.testCorrectAuthException(); + } + +} diff --git a/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadataTest.java b/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadataTest.java new file mode 100644 index 0000000000..62a7943ff9 --- /dev/null +++ b/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadataTest.java @@ -0,0 +1,204 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * work for additional information regarding copyright ownership. + * The ASF licenses file to You under the Apache License, Version 2.0 + * (the "License"); you may not use file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.compute.functions; + +import static org.easymock.EasyMock.anyObject; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.replay; +import static org.easymock.EasyMock.verify; +import static org.testng.Assert.assertEquals; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.easymock.EasyMock; +import org.jclouds.compute.domain.Image; +import org.jclouds.compute.domain.ImageBuilder; +import org.jclouds.compute.domain.NodeMetadata; +import org.jclouds.compute.domain.OperatingSystem; +import org.jclouds.compute.domain.OsFamily; +import org.jclouds.compute.functions.GroupNamingConvention; +import org.jclouds.docker.domain.Config; +import org.jclouds.docker.domain.Container; +import org.jclouds.docker.domain.HostConfig; +import org.jclouds.docker.domain.NetworkSettings; +import org.jclouds.docker.domain.Port; +import org.jclouds.docker.domain.State; +import org.jclouds.domain.Location; +import org.jclouds.domain.LocationBuilder; +import org.jclouds.domain.LocationScope; +import org.jclouds.providers.ProviderMetadata; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import com.google.common.base.Function; +import com.google.common.base.Supplier; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.inject.Guice; + +/** + * Unit tests for the {@link org.jclouds.docker.compute.functions.ContainerToNodeMetadata} class. + */ +@Test(groups = "unit", testName = "ContainerToNodeMetadataTest") +public class ContainerToNodeMetadataTest { + private ContainerToNodeMetadata function; + + private Container container; + + @BeforeMethod + public void setup() { + Config containerConfig = Config.builder() + .hostname("6d35806c1bd2") + .domainName("") + .user("") + .memory(0) + .memorySwap(0) + .cpuShares(0) + .attachStdin(false) + .attachStdout(false) + .attachStderr(false) + .exposedPorts(ImmutableMap.of("22/tcp", ImmutableMap.of())) + .tty(false) + .openStdin(false) + .stdinOnce(false) + .env(null) + .cmd(ImmutableList.of("/usr/sbin/sshd", "-D")) + .imageId("jclouds/ubuntu") + .volumesFrom("") + .workingDir("") + .entrypoint(null) + .networkDisabled(false) + .build(); + State state = State.builder() + .pid(3626) + .running(true) + .exitCode(0) + .startedAt("2014-03-24T20:28:37.537659054Z") + .finishedAt("0001-01-01T00:00:00Z") + .ghost(false) + .build(); + container = Container.builder() + .id("6d35806c1bd2b25cd92bba2d2c2c5169dc2156f53ab45c2b62d76e2d2fee14a9") + .name("/hopeful_mclean") + .created("2014-03-22T07:16:45.784120972Z") + .path("/usr/sbin/sshd") + .args(new String[] {"-D"}) + .containerConfig(containerConfig) + .state(state) + .image("af0f59f1c19eef9471c3b8c8d587c39b8f130560b54f3766931b37d76d5de4b6") + .networkSettings(NetworkSettings.builder() + .ipAddress("172.17.0.2") + .ipPrefixLen(16) + .gateway("172.17.42.1") + .bridge("docker0") + .ports(ImmutableMap.>>of("22/tcp", + ImmutableList.>of(ImmutableMap.of("HostIp", "0.0.0.0", "HostPort", + "49199")))) + .build()) + .resolvConfPath("/etc/resolv.conf") + .driver("aufs") + .execDriver("native-0.1") + .volumes(ImmutableMap.of()) + .volumesRW(ImmutableMap.of()) + .command("") + .status("") + .hostConfig(HostConfig.builder().publishAllPorts(true).build()) + .ports(ImmutableList.of()) + .build(); + ProviderMetadata providerMetadata = EasyMock.createMock(ProviderMetadata.class); + expect(providerMetadata.getEndpoint()).andReturn("http://127.0.0.1:4243"); + replay(providerMetadata); + + GroupNamingConvention.Factory namingConvention = Guice.createInjector().getInstance(GroupNamingConvention.Factory.class); + + Supplier> images = new Supplier>() { + @Override + public Map get() { + OperatingSystem os = OperatingSystem.builder() + .description("Ubuntu 12.04 64bit") + .family(OsFamily.UBUNTU) + .version("12.04") + .is64Bit(true) + .build(); + + return ImmutableMap.of("af0f59f1c19eef9471c3b8c8d587c39b8f130560b54f3766931b37d76d5de4b6", + new ImageBuilder() + .ids("af0f59f1c19eef9471c3b8c8d587c39b8f130560b54f3766931b37d76d5de4b6") + .name("ubuntu") + .description("Ubuntu 12.04 64bit") + .operatingSystem(os) + .status(Image.Status.AVAILABLE) + .build()); + } + }; + + Supplier> locations = new Supplier>() { + @Override + public Set get() { + + return ImmutableSet.of( + new LocationBuilder() + .id("docker") + .description("http://localhost:2375") + .scope(LocationScope.PROVIDER) + .build() + ); + } + }; + + function = new ContainerToNodeMetadata(providerMetadata, toPortableStatus(), namingConvention, images, locations); + } + + private Function toPortableStatus() { + StateToStatus function = EasyMock.createMock(StateToStatus.class); + expect(function.apply(anyObject(State.class))).andReturn(NodeMetadata.Status.RUNNING); + replay(function); + return function; + } + + public void testVirtualMachineToNodeMetadata() { + Container mockContainer = mockContainer(); + + NodeMetadata node = function.apply(mockContainer); + + verify(mockContainer); + + assertEquals(node.getId(), "6d35806c1bd2b25cd92bba2d2c2c5169dc2156f53ab45c2b62d76e2d2fee14a9"); + assertEquals(node.getGroup(), "hopeful_mclean"); + assertEquals(node.getImageId(), "af0f59f1c19eef9471c3b8c8d587c39b8f130560b54f3766931b37d76d5de4b6"); + assertEquals(node.getLoginPort(), 49199); + assertEquals(node.getPrivateAddresses().size(), 1); + assertEquals(node.getPublicAddresses().size(), 1); + } + + @SuppressWarnings("unchecked") + private Container mockContainer() { + Container mockContainer = EasyMock.createMock(Container.class); + + expect(mockContainer.getId()).andReturn(container.getId()); + expect(mockContainer.getName()).andReturn(container.getName()); + expect(mockContainer.getContainerConfig()).andReturn(container.getContainerConfig()).anyTimes(); + expect(mockContainer.getNetworkSettings()).andReturn(container.getNetworkSettings()).anyTimes(); + expect(mockContainer.getState()).andReturn(container.getState()); + expect(mockContainer.getImage()).andReturn(container.getImage()).anyTimes(); + replay(mockContainer); + + return mockContainer; + } +} diff --git a/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ImageToImageTest.java b/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ImageToImageTest.java new file mode 100644 index 0000000000..f295a38355 --- /dev/null +++ b/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ImageToImageTest.java @@ -0,0 +1,74 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * work for additional information regarding copyright ownership. + * The ASF licenses file to You under the Apache License, Version 2.0 + * (the "License"); you may not use file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.compute.functions; + +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.replay; +import static org.easymock.EasyMock.verify; +import static org.testng.Assert.assertEquals; + +import org.easymock.EasyMock; +import org.jclouds.compute.domain.Image; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableList; + +/** + * Unit tests for the {@link org.jclouds.docker.compute.functions.ImageToImage} class. + */ +@Test(groups = "unit", testName = "ImageToImageTest") +public class ImageToImageTest { + private ImageToImage function; + + private org.jclouds.docker.domain.Image image; + + @BeforeMethod + public void setup() { + image = org.jclouds.docker.domain.Image.builder() + .id("id") + .parent("parent") + .created("created") + .architecture("x86_64") + .repoTags(ImmutableList.of("repoTag1:version")) + .size(0l) + .build(); + function = new ImageToImage(); + } + + public void testImageToImage() { + org.jclouds.docker.domain.Image mockImage = mockImage(); + + Image image = function.apply(mockImage); + + verify(mockImage); + + assertEquals(mockImage.getId(), image.getId().toString()); + } + + @SuppressWarnings("unchecked") + private org.jclouds.docker.domain.Image mockImage() { + org.jclouds.docker.domain.Image mockImage = EasyMock.createMock(org.jclouds.docker.domain.Image.class); + + expect(mockImage.getId()).andReturn(image.getId()).anyTimes(); + expect(mockImage.getRepoTags()).andReturn(image.getRepoTags()).anyTimes(); + expect(mockImage.getArchitecture()).andReturn(image.getArchitecture()).anyTimes(); + replay(mockImage); + + return mockImage; + } +} diff --git a/apis/docker/src/test/java/org/jclouds/docker/compute/functions/StateToStatusTest.java b/apis/docker/src/test/java/org/jclouds/docker/compute/functions/StateToStatusTest.java new file mode 100644 index 0000000000..899a66c9d3 --- /dev/null +++ b/apis/docker/src/test/java/org/jclouds/docker/compute/functions/StateToStatusTest.java @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * work for additional information regarding copyright ownership. + * The ASF licenses file to You under the Apache License, Version 2.0 + * (the "License"); you may not use file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.compute.functions; + +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.replay; +import static org.easymock.EasyMock.verify; +import static org.testng.Assert.assertEquals; + +import org.easymock.EasyMock; +import org.jclouds.compute.domain.NodeMetadata; +import org.jclouds.docker.domain.State; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +/** + * Unit tests for the {@link StateToStatus} class. + */ +@Test(groups = "unit", testName = "StateToStatusTest") +public class StateToStatusTest { + private StateToStatus function; + + @BeforeMethod + public void setup() { + function = new StateToStatus(); + } + + public void testStateRunningToStatusRunning() { + State mockState = mockStateRunning(); + + NodeMetadata.Status status = function.apply(mockState); + + verify(mockState); + + assertEquals(mockState.isRunning(), true); + assertEquals(status, NodeMetadata.Status.RUNNING); + } + + public void testStateNotRunningToStatusTerminated() { + State mockState = mockStateNotRunning(); + + NodeMetadata.Status status = function.apply(mockState); + + verify(mockState); + + assertEquals(mockState.isRunning(), false); + assertEquals(status, NodeMetadata.Status.TERMINATED); + } + + private State mockStateRunning() { + State mockState = EasyMock.createMock(State.class); + + expect(mockState.isRunning()).andReturn(true).anyTimes(); + replay(mockState); + + return mockState; + } + + private State mockStateNotRunning() { + State mockState = EasyMock.createMock(State.class); + + expect(mockState.isRunning()).andReturn(false).anyTimes(); + replay(mockState); + + return mockState; + } +} diff --git a/apis/docker/src/test/java/org/jclouds/docker/compute/options/DockerTemplateOptionsTest.java b/apis/docker/src/test/java/org/jclouds/docker/compute/options/DockerTemplateOptionsTest.java new file mode 100644 index 0000000000..3a982288b8 --- /dev/null +++ b/apis/docker/src/test/java/org/jclouds/docker/compute/options/DockerTemplateOptionsTest.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.compute.options; + +import static org.testng.Assert.assertEquals; + +import org.jclouds.compute.options.TemplateOptions; +import org.testng.annotations.Test; + +import com.google.common.base.Optional; +import com.google.common.collect.ImmutableMap; + +/** + * Unit tests for the {@link DockerTemplateOptions} class. + */ +@Test(groups = "unit", testName = "DockerTemplateOptionsTest") +public class DockerTemplateOptionsTest { + + @Test + public void testHostname() { + TemplateOptions options = new DockerTemplateOptions().hostname("hostname"); + assertEquals(options.as(DockerTemplateOptions.class).getHostname(), Optional.of("hostname")); + } + + @Test + public void testMemory() { + TemplateOptions options = new DockerTemplateOptions().memory(1024); + assertEquals(options.as(DockerTemplateOptions.class).getMemory(), Optional.of(1024)); + } + + @Test + public void testCpuShares() { + TemplateOptions options = new DockerTemplateOptions().cpuShares(2); + assertEquals(options.as(DockerTemplateOptions.class).getCpuShares(), Optional.of(2)); + } + + @Test + public void testVolumes() { + TemplateOptions options = new DockerTemplateOptions().volumes(ImmutableMap.of("/tmp", "/tmp")); + assertEquals(options.as(DockerTemplateOptions.class).getVolumes(), Optional.of(ImmutableMap.of("/tmp", "/tmp"))); + } + + @Test + public void testDns() { + TemplateOptions options = new DockerTemplateOptions().dns("8.8.8.8"); + assertEquals(options.as(DockerTemplateOptions.class).getDns(), Optional.of("8.8.8.8")); + } +} diff --git a/apis/docker/src/test/java/org/jclouds/docker/config/DockerParserModuleTest.java b/apis/docker/src/test/java/org/jclouds/docker/config/DockerParserModuleTest.java new file mode 100644 index 0000000000..4f5ba7586e --- /dev/null +++ b/apis/docker/src/test/java/org/jclouds/docker/config/DockerParserModuleTest.java @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.config; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import org.jclouds.docker.domain.Container; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import static org.jclouds.docker.config.DockerParserModule.ContainerTypeAdapter; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; + +/** + * Unit tests for the {@link org.jclouds.docker.config.DockerParserModule} class. + */ +@Test(groups = "unit", testName = "DockerParserModuleTest") +public class DockerParserModuleTest { + + private Gson gson; + + @BeforeMethod + public void setup() { + gson = new GsonBuilder() + .registerTypeAdapter(Container.class, new ContainerTypeAdapter()) + .create(); + } + + @Test + public void testContainerWithVolumesNull() { + Container container = gson.fromJson( + "{ \"Volumes\": null }", Container.class); + assertNotNull(container); + assertEquals(container.getVolumes(), null); + } + +} diff --git a/apis/docker/src/test/java/org/jclouds/docker/features/RemoteApiLiveTest.java b/apis/docker/src/test/java/org/jclouds/docker/features/RemoteApiLiveTest.java new file mode 100644 index 0000000000..34fedddd65 --- /dev/null +++ b/apis/docker/src/test/java/org/jclouds/docker/features/RemoteApiLiveTest.java @@ -0,0 +1,121 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.features; + +import com.google.common.base.Splitter; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +import com.google.common.io.Resources; +import org.jclouds.docker.compute.BaseDockerApiLiveTest; +import org.jclouds.docker.domain.Config; +import org.jclouds.docker.domain.Container; +import org.jclouds.docker.domain.Image; +import org.jclouds.docker.options.BuildOptions; +import org.jclouds.docker.options.CreateImageOptions; +import org.jclouds.docker.options.DeleteImageOptions; +import org.jclouds.rest.ResourceNotFoundException; +import org.testng.annotations.Test; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URISyntaxException; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertNull; +import static org.testng.Assert.assertTrue; + +@Test(groups = "live", testName = "RemoteApiLiveTest", singleThreaded = true) +public class RemoteApiLiveTest extends BaseDockerApiLiveTest { + + private static final String BUSYBOX_IMAGE = "busybox"; + private Container container = null; + private Image image = null; + + @Test + public void testVersion() { + assertEquals(api().getVersion().getVersion(), "1.0.0"); + } + + @Test(dependsOnMethods = "testVersion") + public void testCreateImage() throws IOException, InterruptedException { + CreateImageOptions options = CreateImageOptions.Builder.fromImage(BUSYBOX_IMAGE); + InputStream createImageStream = api().createImage(options); + consumeStream(createImageStream, false); + image = api().inspectImage(BUSYBOX_IMAGE); + assertNotNull(image); + } + + @Test(dependsOnMethods = "testCreateImage") + public void testListImages() { + assertNotNull(api().listImages()); + } + + @Test(dependsOnMethods = "testListImages") + public void testCreateContainer() throws IOException, InterruptedException { + Config containerConfig = Config.builder().imageId(image.getId()) + .cmd(ImmutableList.of("/bin/sh", "-c", "while true; do echo hello world; sleep 1; done")) + .build(); + container = api().createContainer("testCreateContainer", containerConfig); + assertNotNull(container); + assertNotNull(container.getId()); + } + + @Test(dependsOnMethods = "testCreateContainer") + public void testStartContainer() throws IOException, InterruptedException { + api().startContainer(container.getId()); + assertTrue(api().inspectContainer(container.getId()).getState().isRunning()); + } + + @Test(dependsOnMethods = "testStartContainer") + public void testStopContainer() { + api().stopContainer(container.getId()); + assertFalse(api().inspectContainer(container.getId()).getState().isRunning()); + } + + @Test(dependsOnMethods = "testStopContainer", expectedExceptions = NullPointerException.class) + public void testRemoveContainer() { + api().removeContainer(container.getId()); + assertFalse(api().inspectContainer(container.getId()).getState().isRunning()); + } + + @Test(dependsOnMethods = "testRemoveContainer", expectedExceptions = ResourceNotFoundException.class) + public void testDeleteImage() { + InputStream deleteImageStream = api().deleteImage(image.getId()); + consumeStream(deleteImageStream, false); + assertNull(api().inspectImage(image.getId())); + } + + public void testBuildImage() throws IOException, InterruptedException, URISyntaxException { + BuildOptions options = BuildOptions.Builder.tag("testBuildImage").verbose(false).nocache(false); + InputStream buildImageStream = api().build(new File(Resources.getResource("Dockerfile").toURI()), options); + String buildStream = consumeStream(buildImageStream, false); + Iterable splitted = Splitter.on("\n").split(buildStream.replace("\r", "").trim()); + String lastStreamedLine = Iterables.getLast(splitted).trim(); + String rawImageId = Iterables.getLast(Splitter.on("Successfully built ").split(lastStreamedLine)); + String imageId = rawImageId.substring(0, 11); + Image image = api().inspectImage(imageId); + api().deleteImage(image.getId(), DeleteImageOptions.Builder.force(true)); + } + + private RemoteApi api() { + return api.getRemoteApi(); + } + +} diff --git a/apis/docker/src/test/java/org/jclouds/docker/features/RemoteApiMockTest.java b/apis/docker/src/test/java/org/jclouds/docker/features/RemoteApiMockTest.java new file mode 100644 index 0000000000..760f7234b3 --- /dev/null +++ b/apis/docker/src/test/java/org/jclouds/docker/features/RemoteApiMockTest.java @@ -0,0 +1,376 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.features; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMultimap; +import com.squareup.okhttp.mockwebserver.MockResponse; +import com.squareup.okhttp.mockwebserver.MockWebServer; +import org.jclouds.docker.DockerApi; +import org.jclouds.docker.domain.Config; +import org.jclouds.docker.domain.Container; +import org.jclouds.docker.internal.BaseDockerMockTest; +import org.jclouds.docker.options.BuildOptions; +import org.jclouds.docker.options.CreateImageOptions; +import org.jclouds.docker.options.ListContainerOptions; +import org.jclouds.io.Payload; +import org.jclouds.io.Payloads; +import org.jclouds.rest.ResourceNotFoundException; +import org.testng.annotations.Test; + +import java.io.File; +import java.io.FileInputStream; +import java.util.Set; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; + +/** + * Mock tests for the {@link org.jclouds.docker.DockerApi} class. + */ +@Test(groups = "unit", testName = "RemoteApiMockTest") +public class RemoteApiMockTest extends BaseDockerMockTest { + + public void testListContainers() throws Exception { + MockWebServer server = mockWebServer(); + server.enqueue(new MockResponse().setBody(payloadFromResource("/containers.json"))); + + DockerApi api = api(server.getUrl("/")); + RemoteApi remoteApi = api.getRemoteApi(); + + try { + Set containers = remoteApi.listContainers(); + assertRequestHasCommonFields(server.takeRequest(), "/containers/json"); + assertEquals(containers.size(), 1); + } finally { + api.close(); + server.shutdown(); + } + } + + public void testListNonexistentContainers() throws Exception { + MockWebServer server = mockWebServer(); + server.enqueue(new MockResponse().setResponseCode(404)); + + DockerApi api = api(server.getUrl("/")); + RemoteApi remoteApi = api.getRemoteApi(); + + try { + Set containers = remoteApi.listContainers(); + assertRequestHasCommonFields(server.takeRequest(), "/containers/json"); + assertTrue(containers.isEmpty()); + } finally { + api.close(); + server.shutdown(); + } + } + + + @Test(timeOut = 10000l) + public void testListAllContainers() throws Exception { + MockWebServer server = mockWebServer(); + server.enqueue(new MockResponse().setBody(payloadFromResource("/containers.json"))); + DockerApi api = api(server.getUrl("/")); + RemoteApi remoteApi = api.getRemoteApi(); + try { + Set containers = remoteApi.listContainers(ListContainerOptions.Builder.all(true)); + assertRequestHasParameters(server.takeRequest(), "/containers/json", ImmutableMultimap.of("all", "true")); + assertEquals(containers.size(), 1); + } finally { + api.close(); + server.shutdown(); + } + } + + public void testGetContainer() throws Exception { + MockWebServer server = mockWebServer(); + server.enqueue(new MockResponse().setBody(payloadFromResource("/container.json"))); + DockerApi api = api(server.getUrl("/")); + RemoteApi remoteApi = api.getRemoteApi(); + String containerId = "b03d4cd15b76f8876110615cdeed15eadf77c9beb408d62f1687dcc69192cd6d"; + try { + Container container = remoteApi.inspectContainer(containerId); + assertRequestHasCommonFields(server.takeRequest(), "/containers/" + containerId + "/json"); + assertNotNull(container); + assertNotNull(container.getId(), containerId); + assertNotNull(container.getContainerConfig()); + assertNotNull(container.getHostConfig()); + assertEquals(container.getName(), "/tender_lumiere"); + assertEquals(container.getState().isRunning(), true); + } finally { + api.close(); + server.shutdown(); + } + } + + public void testGetNonExistingContainer() throws Exception { + MockWebServer server = mockWebServer(); + server.enqueue(new MockResponse().setResponseCode(404)); + DockerApi api = api(server.getUrl("/")); + RemoteApi remoteApi = api.getRemoteApi(); + String containerId = "notExisting"; + try { + Container container = remoteApi.inspectContainer(containerId); + assertRequestHasCommonFields(server.takeRequest(), "/containers/" + containerId + "/json"); + } finally { + api.close(); + server.shutdown(); + } + } + + public void testCreateContainer() throws Exception { + MockWebServer server = mockWebServer(); + server.enqueue(new MockResponse().setBody(payloadFromResource("/container-creation.json"))); + + DockerApi api = api(server.getUrl("/")); + RemoteApi remoteApi = api.getRemoteApi(); + Config containerConfig = Config.builder().cmd(ImmutableList.of("date")) + .attachStdin(false) + .attachStderr(true) + .attachStdout(true) + .tty(false) + .imageId("base") + .build(); + try { + Container container = remoteApi.createContainer("test", containerConfig); + assertRequestHasCommonFields(server.takeRequest(), "POST", "/containers/create?name=test"); + assertNotNull(container); + assertEquals(container.getId(), "c6c74153ae4b1d1633d68890a68d89c40aa5e284a1ea016cbc6ef0e634ee37b2"); + } finally { + api.close(); + server.shutdown(); + } + } + + public void testRemoveContainer() throws Exception { + MockWebServer server = mockWebServer(); + server.enqueue(new MockResponse().setResponseCode(204)); + + DockerApi api = api(server.getUrl("/")); + RemoteApi remoteApi = api.getRemoteApi(); + String containerId = "6d35806c1bd2b25cd92bba2d2c2c5169dc2156f53ab45c2b62d76e2d2fee14a9"; + + try { + remoteApi.removeContainer(containerId); + assertRequestHasCommonFields(server.takeRequest(), "DELETE", "/containers/" + containerId); + } finally { + api.close(); + server.shutdown(); + } + } + + public void testRemoveNonExistingContainer() throws Exception { + MockWebServer server = mockWebServer(); + server.enqueue(new MockResponse().setResponseCode(404)); + DockerApi api = api(server.getUrl("/")); + RemoteApi remoteApi = api.getRemoteApi(); + String containerId = "nonExisting"; + try { + remoteApi.removeContainer(containerId); + fail("Remove container must fail on 404"); + } catch (ResourceNotFoundException ex) { + // Expected exception + } finally { + api.close(); + server.shutdown(); + } + } + + public void testStartContainer() throws Exception { + MockWebServer server = mockWebServer(); + server.enqueue(new MockResponse().setResponseCode(200)); + DockerApi api = api(server.getUrl("/")); + RemoteApi remoteApi = api.getRemoteApi(); + try { + remoteApi.startContainer("1"); + assertRequestHasCommonFields(server.takeRequest(), "POST", "/containers/1/start"); + } finally { + api.close(); + server.shutdown(); + } + } + + public void testStartNonExistingContainer() throws Exception { + MockWebServer server = mockWebServer(); + server.enqueue(new MockResponse().setResponseCode(404)); + DockerApi api = api(server.getUrl("/")); + RemoteApi remoteApi = api.getRemoteApi(); + try { + try { + remoteApi.startContainer("1"); + fail("Start container must fail on 404"); + } catch (ResourceNotFoundException ex) { + // Expected exception + } + } finally { + api.close(); + server.shutdown(); + } + } + + public void testStopContainer() throws Exception { + MockWebServer server = mockWebServer(); + server.enqueue(new MockResponse().setResponseCode(200)); + DockerApi api = api(server.getUrl("/")); + RemoteApi remoteApi = api.getRemoteApi(); + try { + remoteApi.stopContainer("1"); + assertRequestHasCommonFields(server.takeRequest(), "POST", "/containers/1/stop"); + } finally { + api.close(); + server.shutdown(); + } + } + + public void testStopNonExistingContainer() throws Exception { + MockWebServer server = mockWebServer(); + server.enqueue(new MockResponse().setResponseCode(404)); + DockerApi api = api(server.getUrl("/")); + RemoteApi remoteApi = api.getRemoteApi(); + try { + remoteApi.stopContainer("1"); + fail("Stop container must fail on 404"); + } catch (ResourceNotFoundException ex) { + // Expected exception + } finally { + api.close(); + server.shutdown(); + } + } + + public void testCreateImage() throws Exception { + MockWebServer server = mockWebServer(); + server.enqueue(new MockResponse().setResponseCode(200)); + DockerApi api = api(server.getUrl("/")); + RemoteApi remoteApi = api.getRemoteApi(); + try { + remoteApi.createImage(CreateImageOptions.Builder.fromImage("base")); + assertRequestHasParameters(server.takeRequest(), "POST", "/images/create", ImmutableMultimap.of("fromImage", + "base")); + } finally { + api.close(); + server.shutdown(); + } + } + + public void testCreateImageFailure() throws Exception { + MockWebServer server = mockWebServer(); + server.enqueue(new MockResponse().setResponseCode(404)); + DockerApi api = api(server.getUrl("/")); + RemoteApi remoteApi = api.getRemoteApi(); + try { + remoteApi.createImage(CreateImageOptions.Builder.fromImage("base")); + fail("Create image must fail on 404"); + } catch (ResourceNotFoundException ex) { + // Expected exception + } finally { + api.close(); + server.shutdown(); + } + } + + public void testDeleteImage() throws Exception { + MockWebServer server = mockWebServer(); + server.enqueue(new MockResponse().setResponseCode(204)); + DockerApi api = api(server.getUrl("/")); + RemoteApi remoteApi = api.getRemoteApi(); + try { + remoteApi.deleteImage("1"); + assertRequestHasCommonFields(server.takeRequest(), "DELETE", "/images/1"); + } finally { + api.close(); + server.shutdown(); + } + } + + public void testDeleteNotExistingImage() throws Exception { + MockWebServer server = mockWebServer(); + server.enqueue(new MockResponse().setResponseCode(404)); + DockerApi api = api(server.getUrl("/")); + RemoteApi remoteApi = api.getRemoteApi(); + try { + remoteApi.deleteImage("1"); + fail("Delete image must fail on 404"); + } catch (ResourceNotFoundException ex) { + // Expected exception + } finally { + api.close(); + server.shutdown(); + } + } + + public void testBuildContainer() throws Exception { + MockWebServer server = mockWebServer(); + server.enqueue(new MockResponse().setResponseCode(200)); + DockerApi api = api(server.getUrl("/")); + RemoteApi remoteApi = api.getRemoteApi(); + File dockerFile = File.createTempFile("docker", "tmp"); + try { + remoteApi.build(dockerFile, BuildOptions.NONE); + assertRequestHasCommonFields(server.takeRequest(), "POST", "/build"); + } finally { + dockerFile.delete(); + api.close(); + server.shutdown(); + } + } + + public void testBuildContainerUsingPayload() throws Exception { + MockWebServer server = mockWebServer(); + server.enqueue(new MockResponse().setResponseCode(200)); + DockerApi api = api(server.getUrl("/")); + RemoteApi remoteApi = api.getRemoteApi(); + + File file = File.createTempFile("docker", "tmp"); + FileInputStream data = new FileInputStream(file); + Payload payload = Payloads.newInputStreamPayload(data); + payload.getContentMetadata().setContentLength(file.length()); + + try { + remoteApi.build(payload, BuildOptions.NONE); + assertRequestHasCommonFields(server.takeRequest(), "POST", "/build"); + } finally { + api.close(); + server.shutdown(); + } + } + + public void testBuildNonexistentContainer() throws Exception { + MockWebServer server = mockWebServer(); + server.enqueue(new MockResponse().setResponseCode(404)); + + DockerApi api = api(server.getUrl("/")); + RemoteApi remoteApi = api.getRemoteApi(); + + File dockerFile = File.createTempFile("docker", "tmp"); + try { + try { + remoteApi.build(dockerFile, BuildOptions.NONE); + fail("Build container must fail on 404"); + } catch (ResourceNotFoundException ex) { + // Expected exception + } + } finally { + dockerFile.delete(); + api.close(); + server.shutdown(); + } + } + +} diff --git a/apis/docker/src/test/java/org/jclouds/docker/features/internal/ArchivesTest.java b/apis/docker/src/test/java/org/jclouds/docker/features/internal/ArchivesTest.java new file mode 100644 index 0000000000..15eb3ffe69 --- /dev/null +++ b/apis/docker/src/test/java/org/jclouds/docker/features/internal/ArchivesTest.java @@ -0,0 +1,112 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.features.internal; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.Iterables.getOnlyElement; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.List; + +import javax.annotation.Resource; +import javax.inject.Named; + +import org.apache.commons.compress.archivers.ArchiveStreamFactory; +import org.apache.commons.compress.archivers.tar.TarArchiveEntry; +import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; +import org.apache.commons.compress.archivers.tar.TarUtils; +import org.jclouds.compute.reference.ComputeServiceConstants; +import org.jclouds.logging.Logger; +import org.testng.Assert; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import com.beust.jcommander.internal.Lists; +import com.google.common.io.ByteStreams; +import com.google.common.io.Files; + +@Test(groups = "unit", testName = "ArchivesTest") +public class ArchivesTest { + + @Resource + @Named(ComputeServiceConstants.COMPUTE_LOGGER) + protected Logger logger = Logger.NULL; + + private File tmpDir; + private File outputDir; + private long checkSum; + + @BeforeClass + private void init() throws IOException { + tmpDir = Files.createTempDir(); + outputDir = Files.createTempDir(); + File sampleFile = writeSampleFile("test", "this is a test to tar a hierarchy of folders and files\n"); + checkSum = TarUtils.computeCheckSum(Files.asByteSource(sampleFile).read()); + } + + public void testTarSingleFile() throws Exception { + File archive = Archives.tar(tmpDir, new File(outputDir + File.separator + "test.tar.gz")); + List untarredFiles = unTar(archive, outputDir); + File untarredSampleFile = getOnlyElement(untarredFiles, null); + assertNotNull(untarredSampleFile); + assertTrue(checkSum == TarUtils.computeCheckSum(Files.asByteSource(untarredSampleFile).read())); + } + + private List unTar(final File inputFile, final File outputDir) throws Exception { + final List untarredFiles = Lists.newArrayList(); + final InputStream is = new FileInputStream(inputFile); + final TarArchiveInputStream tarArchiveInputStream = (TarArchiveInputStream) + new ArchiveStreamFactory().createArchiveInputStream("tar", is); + TarArchiveEntry entry; + while ((entry = (TarArchiveEntry) tarArchiveInputStream.getNextEntry()) != null) { + final File outputFile = new File(outputDir, entry.getName()); + if (entry.isDirectory()) { + if (!outputFile.exists()) { + if (!outputFile.mkdirs()) { + throw new IllegalStateException(String.format("Couldn't create directory %s.", outputFile.getAbsolutePath())); + } + } + } else { + OutputStream outputFileStream = new FileOutputStream(outputFile); + ByteStreams.copy(tarArchiveInputStream, outputFileStream); + outputFileStream.close(); + } + untarredFiles.add(outputFile); + } + tarArchiveInputStream.close(); + return untarredFiles; + } + + private File writeSampleFile(String fileName, final String contents) { + checkNotNull(fileName, "Provided file name for writing must NOT be null."); + checkNotNull(contents, "Unable to write null contents."); + File sampleFile = new File(tmpDir + File.separator + fileName); + try { + Files.write(contents.getBytes(), sampleFile); + } catch (IOException e) { + logger.error("ERROR trying to write to file '" + fileName + "' - " + e.toString()); + Assert.fail(); + } + return sampleFile; + } +} diff --git a/apis/docker/src/test/java/org/jclouds/docker/internal/BaseDockerMockTest.java b/apis/docker/src/test/java/org/jclouds/docker/internal/BaseDockerMockTest.java new file mode 100644 index 0000000000..146b2a051d --- /dev/null +++ b/apis/docker/src/test/java/org/jclouds/docker/internal/BaseDockerMockTest.java @@ -0,0 +1,118 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.internal; + +import static com.google.common.util.concurrent.MoreExecutors.sameThreadExecutor; +import static org.jclouds.http.utils.Queries.encodeQueryLine; +import static org.jclouds.util.Strings2.toStringAndClose; +import static org.testng.Assert.assertEquals; +import java.io.IOException; +import java.net.URL; +import java.util.Properties; +import java.util.Set; + +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.MediaType; + +import org.jclouds.ContextBuilder; +import org.jclouds.concurrent.config.ExecutorServiceModule; +import org.jclouds.docker.DockerApi; + +import com.google.common.base.Charsets; +import com.google.common.base.Throwables; +import com.google.common.collect.ImmutableMultimap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Multimap; +import com.google.inject.Module; +import com.squareup.okhttp.mockwebserver.MockWebServer; +import com.squareup.okhttp.mockwebserver.RecordedRequest; + +/** + * Base class for all Docker mock tests. + */ +public class BaseDockerMockTest { + private final Set modules = ImmutableSet. of(new ExecutorServiceModule(sameThreadExecutor(), + sameThreadExecutor())); + + protected String provider; + + public BaseDockerMockTest() { + provider = "docker"; + } + + public DockerApi api(URL url) { + return ContextBuilder.newBuilder(provider) + .credentials("clientid", "apikey") + .endpoint(url.toString()) + .modules(modules) + .overrides(setupProperties()) + .buildApi(DockerApi.class); + } + + protected Properties setupProperties() { + return new Properties(); + } + + public static MockWebServer mockWebServer() throws IOException { + MockWebServer server = new MockWebServer(); + server.play(); + return server; + } + + public byte[] payloadFromResource(String resource) { + try { + return toStringAndClose(getClass().getResourceAsStream(resource)).getBytes(Charsets.UTF_8); + } catch (IOException e) { + throw Throwables.propagate(e); + } + } + + protected static void assertRequestHasCommonFields(final RecordedRequest request, final String path) + throws InterruptedException { + assertRequestHasParameters(request, "GET", path, ImmutableMultimap. of()); + } + + protected static void assertRequestHasCommonFields(final RecordedRequest request, + final String verb, final String path) + throws InterruptedException { + assertRequestHasParameters(request, verb, path, ImmutableMultimap. of()); + } + + protected static void assertRequestHasParameters(final RecordedRequest request, final String path, + Multimap parameters) throws InterruptedException { + assertRequestHasParameters(request, "GET", path, parameters); + } + + protected static void assertRequestHasParameters(final RecordedRequest request, String verb, final String path, + Multimap parameters) throws InterruptedException { + String queryParameters = ""; + if (!parameters.isEmpty()) { + Multimap allparams = ImmutableMultimap.builder() + .putAll(parameters) + .build(); + + assertRequestHasAcceptHeader(request); + queryParameters = "?" + encodeQueryLine(allparams); + } + assertEquals(request.getRequestLine(), verb + " " + path + queryParameters + " HTTP/1.1"); + } + + protected static void assertRequestHasAcceptHeader(final RecordedRequest request) throws InterruptedException { + assertEquals(request.getHeader(HttpHeaders.ACCEPT), MediaType.APPLICATION_JSON); + } + +} diff --git a/apis/docker/src/test/resources/Dockerfile b/apis/docker/src/test/resources/Dockerfile new file mode 100644 index 0000000000..131871544c --- /dev/null +++ b/apis/docker/src/test/resources/Dockerfile @@ -0,0 +1,29 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +FROM centos:6.4 +MAINTAINER Andrea Turli + +# RUN yum -y groupinstall 'Development Tools' +RUN yum -y install openssh-server openssh-clients + +RUN chkconfig sshd on +RUN service sshd start +RUN echo 'root:password' | chpasswd + +EXPOSE 22 +CMD ["/usr/sbin/sshd", "-D"] \ No newline at end of file diff --git a/apis/docker/src/test/resources/container-creation.json b/apis/docker/src/test/resources/container-creation.json new file mode 100644 index 0000000000..3e34e0bbb5 --- /dev/null +++ b/apis/docker/src/test/resources/container-creation.json @@ -0,0 +1 @@ +{"Id":"c6c74153ae4b1d1633d68890a68d89c40aa5e284a1ea016cbc6ef0e634ee37b2","Warnings":null} \ No newline at end of file diff --git a/apis/docker/src/test/resources/container.json b/apis/docker/src/test/resources/container.json new file mode 100644 index 0000000000..b35301278f --- /dev/null +++ b/apis/docker/src/test/resources/container.json @@ -0,0 +1,81 @@ +{ + "Args": [ + "-c", + "yum -y install openssh-server openssh-clients" + ], + "Config": { + "AttachStderr": false, + "AttachStdin": false, + "AttachStdout": false, + "Cmd": [ + "/bin/sh", + "-c", + "yum -y install openssh-server openssh-clients" + ], + "CpuShares": 0, + "Cpuset": "", + "Domainname": "", + "Entrypoint": null, + "Env": [ + "HOME=/", + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + ], + "ExposedPorts": null, + "Hostname": "9088c45a9592", + "Image": "1e2d12a45cd57ae3fe3c31ede149d800aaf6a711c61646fad340080f531775c8", + "Memory": 0, + "MemorySwap": 0, + "NetworkDisabled": false, + "OnBuild": [], + "OpenStdin": false, + "PortSpecs": null, + "StdinOnce": false, + "Tty": false, + "User": "", + "Volumes": null, + "WorkingDir": "" + }, + "Created": "2014-06-18T08:49:25.36448943Z", + "Driver": "aufs", + "ExecDriver": "native-0.2", + "HostConfig": { + "Binds": null, + "ContainerIDFile": "", + "Dns": null, + "DnsSearch": null, + "Links": null, + "LxcConf": null, + "NetworkMode": "", + "PortBindings": null, + "Privileged": false, + "PublishAllPorts": false, + "VolumesFrom": null + }, + "HostnamePath": "/mnt/sda1/var/lib/docker/containers/be1d295c091720abc9a3105219ab75a0a7367d74156cc6048aa599fcc7d650e2/hostname", + "HostsPath": "/mnt/sda1/var/lib/docker/containers/be1d295c091720abc9a3105219ab75a0a7367d74156cc6048aa599fcc7d650e2/hosts", + "Id": "be1d295c091720abc9a3105219ab75a0a7367d74156cc6048aa599fcc7d650e2", + "Image": "1e2d12a45cd57ae3fe3c31ede149d800aaf6a711c61646fad340080f531775c8", + "MountLabel": "", + "Name": "/tender_lumiere", + "NetworkSettings": { + "Bridge": "docker0", + "Gateway": "172.17.42.1", + "IPAddress": "172.17.0.100", + "IPPrefixLen": 16, + "PortMapping": null, + "Ports": {} + }, + "Path": "/bin/sh", + "ProcessLabel": "", + "ResolvConfPath": "/mnt/sda1/var/lib/docker/containers/be1d295c091720abc9a3105219ab75a0a7367d74156cc6048aa599fcc7d650e2/resolv.conf", + "State": { + "ExitCode": 0, + "FinishedAt": "0001-01-01T00:00:00Z", + "Paused": false, + "Pid": 16422, + "Running": true, + "StartedAt": "2014-06-18T08:49:25.63685385Z" + }, + "Volumes": {}, + "VolumesRW": {} +} \ No newline at end of file diff --git a/apis/docker/src/test/resources/containers.json b/apis/docker/src/test/resources/containers.json new file mode 100644 index 0000000000..8f789b7100 --- /dev/null +++ b/apis/docker/src/test/resources/containers.json @@ -0,0 +1,20 @@ +[ + { + "Command": "/usr/sbin/sshd -D", + "Created": 1395472605, + "Id": "6d35806c1bd2b25cd92bba2d2c2c5169dc2156f53ab45c2b62d76e2d2fee14a9", + "Image": "jclouds/ubuntu:latest", + "Names": [ + "/hopeful_mclean" + ], + "Ports": [ + { + "IP": "0.0.0.0", + "PrivatePort": 22, + "PublicPort": 49231, + "Type": "tcp" + } + ], + "Status": "Up 55 seconds" + } +] \ No newline at end of file diff --git a/apis/docker/src/test/resources/logback.xml b/apis/docker/src/test/resources/logback.xml new file mode 100644 index 0000000000..94f593832e --- /dev/null +++ b/apis/docker/src/test/resources/logback.xml @@ -0,0 +1,34 @@ + + + + + + - %msg%n + + + + + + + + + + + \ No newline at end of file From 9536338f61f9b58a0972f7c4d19b8c02fa0d45fe Mon Sep 17 00:00:00 2001 From: Aled Sage Date: Thu, 17 Jul 2014 17:00:20 +0100 Subject: [PATCH 02/81] Fix use of deprecated Strings2.toString(InputSupplier) --- .../docker/handlers/DockerErrorHandler.java | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/apis/docker/src/main/java/org/jclouds/docker/handlers/DockerErrorHandler.java b/apis/docker/src/main/java/org/jclouds/docker/handlers/DockerErrorHandler.java index 855f25fda8..72d3069fba 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/handlers/DockerErrorHandler.java +++ b/apis/docker/src/main/java/org/jclouds/docker/handlers/DockerErrorHandler.java @@ -16,8 +16,10 @@ */ package org.jclouds.docker.handlers; -import com.google.common.base.Throwables; -import com.google.common.io.Closeables; +import java.io.IOException; + +import javax.annotation.Resource; + import org.jclouds.http.HttpCommand; import org.jclouds.http.HttpErrorHandler; import org.jclouds.http.HttpResponse; @@ -27,8 +29,8 @@ import org.jclouds.rest.AuthorizationException; import org.jclouds.rest.ResourceNotFoundException; import org.jclouds.util.Strings2; -import javax.annotation.Resource; -import java.io.IOException; +import com.google.common.base.Throwables; +import com.google.common.io.Closeables; /** * This will parse and set an appropriate exception on the command object. @@ -92,15 +94,9 @@ public class DockerErrorHandler implements HttpErrorHandler { if (response.getPayload() == null) return null; try { - return Strings2.toString(response.getPayload()); + return Strings2.toStringAndClose(response.getPayload().openStream()); } catch (IOException e) { throw Throwables.propagate(e); - } finally { - try { - response.getPayload().close(); - } catch (IOException e) { - throw Throwables.propagate(e); - } } } } From 718e3ec81f08d4b54c42d2302c9b62bb657bb406 Mon Sep 17 00:00:00 2001 From: Aled Sage Date: Thu, 17 Jul 2014 21:35:50 +0100 Subject: [PATCH 03/81] Fix minor compilation warnings --- .../docker/compute/config/DockerComputeServiceContextModule.java | 1 + .../docker/compute/functions/ContainerToNodeMetadataTest.java | 1 - .../org/jclouds/docker/compute/functions/ImageToImageTest.java | 1 - 3 files changed, 1 insertion(+), 2 deletions(-) diff --git a/apis/docker/src/main/java/org/jclouds/docker/compute/config/DockerComputeServiceContextModule.java b/apis/docker/src/main/java/org/jclouds/docker/compute/config/DockerComputeServiceContextModule.java index b1cfee2da7..063d6c9e87 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/compute/config/DockerComputeServiceContextModule.java +++ b/apis/docker/src/main/java/org/jclouds/docker/compute/config/DockerComputeServiceContextModule.java @@ -37,6 +37,7 @@ import org.jclouds.functions.IdentityFunction; public class DockerComputeServiceContextModule extends ComputeServiceAdapterContextModule { + @SuppressWarnings("unchecked") @Override protected void configure() { super.configure(); diff --git a/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadataTest.java b/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadataTest.java index 62a7943ff9..f7a3b57637 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadataTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadataTest.java @@ -187,7 +187,6 @@ public class ContainerToNodeMetadataTest { assertEquals(node.getPublicAddresses().size(), 1); } - @SuppressWarnings("unchecked") private Container mockContainer() { Container mockContainer = EasyMock.createMock(Container.class); diff --git a/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ImageToImageTest.java b/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ImageToImageTest.java index f295a38355..e9754d0101 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ImageToImageTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ImageToImageTest.java @@ -60,7 +60,6 @@ public class ImageToImageTest { assertEquals(mockImage.getId(), image.getId().toString()); } - @SuppressWarnings("unchecked") private org.jclouds.docker.domain.Image mockImage() { org.jclouds.docker.domain.Image mockImage = EasyMock.createMock(org.jclouds.docker.domain.Image.class); From e20afb8e99fa5c8bb27d560a790d7eec9e5d3744 Mon Sep 17 00:00:00 2001 From: Andrew Phillips Date: Tue, 5 Aug 2014 11:02:40 +0200 Subject: [PATCH 04/81] Updating subproject versions to 2.0.0-SNAPSHOT Follow-up to eed3a06 --- apis/docker/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apis/docker/pom.xml b/apis/docker/pom.xml index 42b9d4d204..d179a41550 100644 --- a/apis/docker/pom.xml +++ b/apis/docker/pom.xml @@ -23,7 +23,7 @@ org.apache.jclouds.labs jclouds-labs - 1.8.0-SNAPSHOT + 2.0.0-SNAPSHOT From a0a165eaa38ab123114c8f03e2a4d2e2b1efeb4a Mon Sep 17 00:00:00 2001 From: Andrew Gaul Date: Wed, 6 Aug 2014 16:28:57 -0700 Subject: [PATCH 05/81] JCLOUDS-653: Address Guava 18 deprecations Mostly renaming Objects.toStringHelper to MoreObjects.toStringHelper. --- .../docker/compute/options/DockerTemplateOptions.java | 3 ++- .../src/main/java/org/jclouds/docker/domain/Config.java | 3 ++- .../src/main/java/org/jclouds/docker/domain/Container.java | 3 ++- .../main/java/org/jclouds/docker/domain/ExposedPorts.java | 3 ++- .../src/main/java/org/jclouds/docker/domain/HostConfig.java | 3 ++- .../src/main/java/org/jclouds/docker/domain/Image.java | 3 ++- .../java/org/jclouds/docker/domain/NetworkSettings.java | 3 ++- .../src/main/java/org/jclouds/docker/domain/Port.java | 3 ++- .../src/main/java/org/jclouds/docker/domain/State.java | 3 ++- .../src/main/java/org/jclouds/docker/domain/Version.java | 3 ++- .../org/jclouds/docker/internal/BaseDockerMockTest.java | 6 +++--- 11 files changed, 23 insertions(+), 13 deletions(-) diff --git a/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java b/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java index 5e4669b9ea..36029da762 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java +++ b/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java @@ -16,6 +16,7 @@ */ package org.jclouds.docker.compute.options; +import com.google.common.base.MoreObjects; import com.google.common.base.Objects; import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; @@ -109,7 +110,7 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable @Override public String toString() { - return Objects.toStringHelper(this) + return MoreObjects.toStringHelper(this) .add("dns", dns) .add("hostname", hostname) .add("memory", memory) diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/Config.java b/apis/docker/src/main/java/org/jclouds/docker/domain/Config.java index 9c80472d96..3674d6dece 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/Config.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/Config.java @@ -16,6 +16,7 @@ */ package org.jclouds.docker.domain; +import com.google.common.base.MoreObjects; import com.google.common.base.Objects; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; @@ -245,7 +246,7 @@ public class Config { @Override public String toString() { - return Objects.toStringHelper(this) + return MoreObjects.toStringHelper(this) .add("hostname", hostname) .add("domainName", domainName) .add("user", user) diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/Container.java b/apis/docker/src/main/java/org/jclouds/docker/domain/Container.java index 27525292e0..0f0c727b18 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/Container.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/Container.java @@ -16,6 +16,7 @@ */ package org.jclouds.docker.domain; +import com.google.common.base.MoreObjects; import com.google.common.base.Objects; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; @@ -210,7 +211,7 @@ public class Container { @Override public String toString() { - return Objects.toStringHelper(this) + return MoreObjects.toStringHelper(this) .add("id", id) .add("name", name) .add("created", created) diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/ExposedPorts.java b/apis/docker/src/main/java/org/jclouds/docker/domain/ExposedPorts.java index b7a831a64c..347ad3a5ea 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/ExposedPorts.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/ExposedPorts.java @@ -22,6 +22,7 @@ import java.util.Set; import org.jclouds.javax.annotation.Nullable; +import com.google.common.base.MoreObjects; import com.google.common.base.Objects; import com.google.common.collect.ImmutableSet; import com.google.gson.annotations.SerializedName; @@ -65,7 +66,7 @@ public class ExposedPorts { @Override public String toString() { - return Objects.toStringHelper(this) + return MoreObjects.toStringHelper(this) .add("portAndProtocol", portAndProtocol) .add("hostPorts", hostPorts) .toString(); diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/HostConfig.java b/apis/docker/src/main/java/org/jclouds/docker/domain/HostConfig.java index 275961fe9e..94155c36fe 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/HostConfig.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/HostConfig.java @@ -23,6 +23,7 @@ import java.util.Map; import org.jclouds.javax.annotation.Nullable; +import com.google.common.base.MoreObjects; import com.google.common.base.Objects; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; @@ -135,7 +136,7 @@ public class HostConfig { @Override public String toString() { - return Objects.toStringHelper(this) + return MoreObjects.toStringHelper(this) .add("containerIDFile", containerIDFile) .add("binds", binds) .add("lxcConf", lxcConf) diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/Image.java b/apis/docker/src/main/java/org/jclouds/docker/domain/Image.java index 186ece32f4..80d6f6818a 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/Image.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/Image.java @@ -16,6 +16,7 @@ */ package org.jclouds.docker.domain; +import com.google.common.base.MoreObjects; import com.google.common.base.Objects; import com.google.common.collect.ImmutableList; import com.google.gson.annotations.SerializedName; @@ -132,7 +133,7 @@ public class Image { @Override public String toString() { - return Objects.toStringHelper(this) + return MoreObjects.toStringHelper(this) .add("id", id) .add("parent", parent) .add("created", created) diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/NetworkSettings.java b/apis/docker/src/main/java/org/jclouds/docker/domain/NetworkSettings.java index 23f3fbede1..0c60719bf1 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/NetworkSettings.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/NetworkSettings.java @@ -16,6 +16,7 @@ */ package org.jclouds.docker.domain; +import com.google.common.base.MoreObjects; import com.google.common.base.Objects; import com.google.common.collect.ImmutableMap; import com.google.gson.annotations.SerializedName; @@ -99,7 +100,7 @@ public class NetworkSettings { @Override public String toString() { - return Objects.toStringHelper(this) + return MoreObjects.toStringHelper(this) .add("ipAddress", ipAddress) .add("ipPrefixLen", ipPrefixLen) .add("gateway", gateway) diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/Port.java b/apis/docker/src/main/java/org/jclouds/docker/domain/Port.java index b49dfa6945..3a43ec55ec 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/Port.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/Port.java @@ -16,6 +16,7 @@ */ package org.jclouds.docker.domain; +import com.google.common.base.MoreObjects; import com.google.common.base.Objects; import com.google.gson.annotations.SerializedName; @@ -78,7 +79,7 @@ public class Port { @Override public String toString() { - return Objects.toStringHelper(this) + return MoreObjects.toStringHelper(this) .add("privatePort", privatePort) .add("publicPort", publicPort) .add("type", type) diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/State.java b/apis/docker/src/main/java/org/jclouds/docker/domain/State.java index ac1ba85e01..cc665c8b09 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/State.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/State.java @@ -19,6 +19,7 @@ package org.jclouds.docker.domain; import static com.google.common.base.Preconditions.checkNotNull; import java.beans.ConstructorProperties; +import com.google.common.base.MoreObjects; import com.google.common.base.Objects; import com.google.gson.annotations.SerializedName; @@ -92,7 +93,7 @@ public class State { @Override public String toString() { - return Objects.toStringHelper(this) + return MoreObjects.toStringHelper(this) .add("pid", pid) .add("running", running) .add("exitCode", exitCode) diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/Version.java b/apis/docker/src/main/java/org/jclouds/docker/domain/Version.java index 445a225c3d..f6dbb4942d 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/Version.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/Version.java @@ -19,6 +19,7 @@ package org.jclouds.docker.domain; import static com.google.common.base.Preconditions.checkNotNull; import java.beans.ConstructorProperties; +import com.google.common.base.MoreObjects; import com.google.common.base.Objects; import com.google.gson.annotations.SerializedName; @@ -92,7 +93,7 @@ public class Version { @Override public String toString() { - return Objects.toStringHelper(this) + return MoreObjects.toStringHelper(this) .add("arch", arch) .add("gitCommit", gitCommit) .add("goVersion", goVersion) diff --git a/apis/docker/src/test/java/org/jclouds/docker/internal/BaseDockerMockTest.java b/apis/docker/src/test/java/org/jclouds/docker/internal/BaseDockerMockTest.java index 146b2a051d..04020b6275 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/internal/BaseDockerMockTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/internal/BaseDockerMockTest.java @@ -16,7 +16,7 @@ */ package org.jclouds.docker.internal; -import static com.google.common.util.concurrent.MoreExecutors.sameThreadExecutor; +import static com.google.common.util.concurrent.MoreExecutors.newDirectExecutorService; import static org.jclouds.http.utils.Queries.encodeQueryLine; import static org.jclouds.util.Strings2.toStringAndClose; import static org.testng.Assert.assertEquals; @@ -45,8 +45,8 @@ import com.squareup.okhttp.mockwebserver.RecordedRequest; * Base class for all Docker mock tests. */ public class BaseDockerMockTest { - private final Set modules = ImmutableSet. of(new ExecutorServiceModule(sameThreadExecutor(), - sameThreadExecutor())); + private final Set modules = ImmutableSet. of(new ExecutorServiceModule(newDirectExecutorService(), + newDirectExecutorService())); protected String provider; From db09fca8a916d0d1323152e9634094c31be78677 Mon Sep 17 00:00:00 2001 From: Andrew Gaul Date: Sat, 9 Aug 2014 22:46:37 -0700 Subject: [PATCH 06/81] Correct license headers --- .../compute/functions/ContainerToNodeMetadataTest.java | 6 +++--- .../jclouds/docker/compute/functions/ImageToImageTest.java | 6 +++--- .../jclouds/docker/compute/functions/StateToStatusTest.java | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadataTest.java b/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadataTest.java index f7a3b57637..b062f2fbf9 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadataTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadataTest.java @@ -1,9 +1,9 @@ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with - * work for additional information regarding copyright ownership. - * The ASF licenses file to You under the Apache License, Version 2.0 - * (the "License"); you may not use file except in compliance with + * 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 diff --git a/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ImageToImageTest.java b/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ImageToImageTest.java index e9754d0101..0141c70d58 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ImageToImageTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ImageToImageTest.java @@ -1,9 +1,9 @@ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with - * work for additional information regarding copyright ownership. - * The ASF licenses file to You under the Apache License, Version 2.0 - * (the "License"); you may not use file except in compliance with + * 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 diff --git a/apis/docker/src/test/java/org/jclouds/docker/compute/functions/StateToStatusTest.java b/apis/docker/src/test/java/org/jclouds/docker/compute/functions/StateToStatusTest.java index 899a66c9d3..b9739abc6e 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/compute/functions/StateToStatusTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/compute/functions/StateToStatusTest.java @@ -1,9 +1,9 @@ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with - * work for additional information regarding copyright ownership. - * The ASF licenses file to You under the Apache License, Version 2.0 - * (the "License"); you may not use file except in compliance with + * 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 From 19f071915ad26408dfe1c5c028f38391553ecf35 Mon Sep 17 00:00:00 2001 From: Andrew Gaul Date: Sun, 31 Aug 2014 21:39:09 -0700 Subject: [PATCH 07/81] Address FindBugs warnings --- .../src/main/java/org/jclouds/docker/domain/Container.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/Container.java b/apis/docker/src/main/java/org/jclouds/docker/domain/Container.java index 0f0c727b18..4e7c059c9d 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/Container.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/Container.java @@ -24,6 +24,7 @@ import com.google.gson.annotations.SerializedName; import org.jclouds.javax.annotation.Nullable; import java.beans.ConstructorProperties; +import java.util.Arrays; import java.util.List; import java.util.Map; @@ -186,7 +187,7 @@ public class Container { Objects.equal(this.name, that.name) && Objects.equal(this.created, that.created) && Objects.equal(this.path, that.path) && - Objects.equal(this.args, that.args) && + Arrays.equals(this.args, that.args) && Objects.equal(this.containerConfig, that.containerConfig) && Objects.equal(this.state, that.state) && Objects.equal(this.image, that.image) && From 96446269d2de218642693ae931a89421ff312c7e Mon Sep 17 00:00:00 2001 From: Andrew Gaul Date: Sun, 21 Sep 2014 09:28:10 -0700 Subject: [PATCH 08/81] Prefer Charsets.UTF_8 over string literal Found with modernizer-maven-plugin. --- .../docker/binders/BindInputStreamToRequestTest.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/apis/docker/src/test/java/org/jclouds/docker/binders/BindInputStreamToRequestTest.java b/apis/docker/src/test/java/org/jclouds/docker/binders/BindInputStreamToRequestTest.java index a21999c6f3..1aa7611e5e 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/binders/BindInputStreamToRequestTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/binders/BindInputStreamToRequestTest.java @@ -16,6 +16,7 @@ */ package org.jclouds.docker.binders; +import com.google.common.base.Charsets; import com.google.common.io.CharStreams; import org.jclouds.http.HttpRequest; import org.testng.annotations.Test; @@ -37,7 +38,7 @@ public class BindInputStreamToRequestTest { HttpRequest request = HttpRequest.builder().method("GET").endpoint("http://test").build(); request = binder.bindToRequest(request, File.createTempFile("dockerfile", "")); - String rawContent = CharStreams.toString(new InputStreamReader((FileInputStream) request.getPayload().getRawContent(), "UTF-8")); + String rawContent = CharStreams.toString(new InputStreamReader((FileInputStream) request.getPayload().getRawContent(), Charsets.UTF_8)); assertTrue(rawContent.startsWith("Dockerfile")); assertEquals(request.getPayload().getContentMetadata().getContentType(), "application/tar"); } @@ -48,7 +49,7 @@ public class BindInputStreamToRequestTest { HttpRequest request = HttpRequest.builder().method("GET").endpoint("http://test").build(); request = binder.bindToRequest(request, new Object()); - String rawContent = CharStreams.toString(new InputStreamReader((FileInputStream) request.getPayload().getRawContent(), "UTF-8")); + String rawContent = CharStreams.toString(new InputStreamReader((FileInputStream) request.getPayload().getRawContent(), Charsets.UTF_8)); assertTrue(rawContent.startsWith("Dockerfile")); assertEquals(request.getPayload().getContentMetadata().getContentType(), "application/tar"); } @@ -59,7 +60,7 @@ public class BindInputStreamToRequestTest { HttpRequest request = HttpRequest.builder().method("GET").endpoint("http://test").build(); request = binder.bindToRequest(request, null); - String rawContent = CharStreams.toString(new InputStreamReader((FileInputStream) request.getPayload().getRawContent(), "UTF-8")); + String rawContent = CharStreams.toString(new InputStreamReader((FileInputStream) request.getPayload().getRawContent(), Charsets.UTF_8)); assertTrue(rawContent.startsWith("Dockerfile")); assertEquals(request.getPayload().getContentMetadata().getContentType(), "application/tar"); } From 1c5a805a4dc5a1599c5d15500ed1012c76e74d4b Mon Sep 17 00:00:00 2001 From: Andrew Kennedy Date: Fri, 10 Oct 2014 18:50:28 +0100 Subject: [PATCH 09/81] Entrypoint should be a JSON array --- .../main/java/org/jclouds/docker/domain/Config.java | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/Config.java b/apis/docker/src/main/java/org/jclouds/docker/domain/Config.java index 3674d6dece..5dd2b777d3 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/Config.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/Config.java @@ -16,7 +16,6 @@ */ package org.jclouds.docker.domain; -import com.google.common.base.MoreObjects; import com.google.common.base.Objects; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; @@ -72,7 +71,7 @@ public class Config { @SerializedName("WorkingDir") private final String workingDir; @SerializedName("Entrypoint") - private final String entrypoint; + private final List entrypoint; @SerializedName("NetworkDisabled") private final boolean networkDisabled; @SerializedName("OnBuild") @@ -87,7 +86,7 @@ public class Config { boolean attachStderr, Map exposedPorts, boolean tty, boolean openStdin, boolean stdinOnce, @Nullable List env, @Nullable List cmd, @Nullable List dns, String imageId, @Nullable Map volumes, - @Nullable String volumesFrom, @Nullable String workingDir, @Nullable String entrypoint, + @Nullable String volumesFrom, @Nullable String workingDir, @Nullable List entrypoint, @Nullable boolean networkDisabled, @Nullable List onBuild) { this.hostname = hostname; this.domainName = domainName; @@ -194,7 +193,7 @@ public class Config { return workingDir; } - public String getEntrypoint() { + public List getEntrypoint() { return entrypoint; } @@ -246,7 +245,7 @@ public class Config { @Override public String toString() { - return MoreObjects.toStringHelper(this) + return Objects.toStringHelper(this) .add("hostname", hostname) .add("domainName", domainName) .add("user", user) @@ -302,7 +301,7 @@ public class Config { private Map volumes = ImmutableMap.of(); private String volumesFrom; private String workingDir; - private String entrypoint; + private List entrypoint = ImmutableList.of(); private boolean networkDisabled; private List onBuild = ImmutableList.of(); @@ -406,7 +405,7 @@ public class Config { return this; } - public Builder entrypoint(String entrypoint) { + public Builder entrypoint(List entrypoint) { this.entrypoint = entrypoint; return this; } From 0126d322fa01091883f088d78f19fe74fec68916 Mon Sep 17 00:00:00 2001 From: Andrew Kennedy Date: Fri, 10 Oct 2014 18:51:18 +0100 Subject: [PATCH 10/81] Fix Container conversion when image ID not present --- .../compute/functions/ContainerToNodeMetadata.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/apis/docker/src/main/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadata.java b/apis/docker/src/main/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadata.java index 72c16af55c..6d54507926 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadata.java +++ b/apis/docker/src/main/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadata.java @@ -18,6 +18,7 @@ package org.jclouds.docker.compute.functions; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.collect.Iterables.getOnlyElement; + import java.net.URI; import java.util.List; import java.util.Map; @@ -86,9 +87,12 @@ public class ContainerToNodeMetadata implements Function Date: Fri, 10 Oct 2014 18:51:46 +0100 Subject: [PATCH 11/81] Set hostname in template options --- .../docker/compute/strategy/DockerComputeServiceAdapter.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java b/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java index 7bbead38fa..a4bcd3a9da 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java +++ b/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java @@ -100,6 +100,10 @@ public class DockerComputeServiceAdapter implements containerConfigBuilder.memory(templateOptions.getMemory().get()); } + if (templateOptions.getHostname().isPresent()) { + containerConfigBuilder.hostname(templateOptions.getHostname().get()); + } + if (templateOptions.getCpuShares().isPresent()) { containerConfigBuilder.cpuShares(templateOptions.getCpuShares().get()); } From 1af34c49b1f8690f52a3434eced6d3981b17e4a5 Mon Sep 17 00:00:00 2001 From: Andrew Kennedy Date: Fri, 10 Oct 2014 19:02:48 +0100 Subject: [PATCH 12/81] Handle missing login port --- .../docker/compute/functions/ContainerToNodeMetadata.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apis/docker/src/main/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadata.java b/apis/docker/src/main/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadata.java index 6d54507926..580e5f1377 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadata.java +++ b/apis/docker/src/main/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadata.java @@ -114,7 +114,7 @@ public class ContainerToNodeMetadata implements Function>> ports = container.getNetworkSettings().getPorts(); - if (ports != null) { + if (ports != null && ports.containsKey("22/tcp")) { return Integer.parseInt(getOnlyElement(ports.get("22/tcp")).get("HostPort")); } // this is needed in case the container list is coming from listContainers @@ -125,6 +125,6 @@ public class ContainerToNodeMetadata implements Function Date: Fri, 10 Oct 2014 21:19:57 +0100 Subject: [PATCH 13/81] Document magic number in NodeMetadata for Docker --- .../compute/functions/ContainerToNodeMetadata.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/apis/docker/src/main/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadata.java b/apis/docker/src/main/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadata.java index 580e5f1377..3ec7f5cb82 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadata.java +++ b/apis/docker/src/main/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadata.java @@ -48,6 +48,14 @@ import com.google.inject.Singleton; @Singleton public class ContainerToNodeMetadata implements Function { + /** + * This value is used when a container does not have an accessible + * login port (i.e. the SSH daemon is not running) due to being + * started outside jclouds. Client code should check for this value + * when accessing NodeMetadata from Docker. + */ + public static final Integer NO_LOGIN_PORT = Integer.valueOf(-1); + private final ProviderMetadata providerMetadata; private final Function toPortableStatus; private final GroupNamingConvention nodeNamingConvention; @@ -125,6 +133,6 @@ public class ContainerToNodeMetadata implements Function Date: Sat, 11 Oct 2014 17:01:32 +0100 Subject: [PATCH 14/81] Add environment config option for Docker containers --- .../options/DockerTemplateOptions.java | 48 ++++++++++++++----- .../strategy/DockerComputeServiceAdapter.java | 4 ++ .../org/jclouds/docker/domain/Config.java | 15 +++--- 3 files changed, 47 insertions(+), 20 deletions(-) diff --git a/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java b/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java index 36029da762..9568f33e6e 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java +++ b/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java @@ -16,7 +16,6 @@ */ package org.jclouds.docker.compute.options; -import com.google.common.base.MoreObjects; import com.google.common.base.Objects; import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; @@ -54,6 +53,7 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable protected Optional cpuShares = Optional.absent(); protected Optional> commands = Optional.absent(); protected Optional> volumes = Optional.absent(); + protected Optional> env = Optional.absent(); @Override public DockerTemplateOptions clone() { @@ -83,8 +83,11 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable eTo.commands(commands.get()); } if (cpuShares.isPresent()) { - eTo.cpuShares(cpuShares.get()); - } + eTo.cpuShares(cpuShares.get()); + } + if (env.isPresent()) { + eTo.env(env.get()); + } } } @@ -100,23 +103,25 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable equal(this.dns, that.dns) && equal(this.memory, that.memory) && equal(this.commands, that.commands) && - equal(this.cpuShares, that.cpuShares); + equal(this.cpuShares, that.cpuShares) && + equal(this.env, that.env); } @Override public int hashCode() { - return Objects.hashCode(super.hashCode(), volumes, hostname, dns, memory, commands, cpuShares); + return Objects.hashCode(super.hashCode(), volumes, hostname, dns, memory, commands, cpuShares, env); } @Override public String toString() { - return MoreObjects.toStringHelper(this) + return Objects.toStringHelper(this) .add("dns", dns) .add("hostname", hostname) .add("memory", memory) .add("cpuShares", cpuShares) .add("commands", commands) .add("volumes", volumes) + .add("env", env) .toString(); } @@ -158,6 +163,11 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable return this; } + public DockerTemplateOptions env(List env) { + this.env = Optional.> of(ImmutableList.copyOf(env)); + return this; + } + public Optional> getVolumes() { return volumes; } @@ -174,15 +184,19 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable public Optional getCpuShares() { return cpuShares; } + public Optional> getEnv() { + return env; + } + public static class Builder { - /** - * @see DockerTemplateOptions#volumes(java.util.Map) - */ - public static DockerTemplateOptions volumes(Map volumes) { - DockerTemplateOptions options = new DockerTemplateOptions(); - return DockerTemplateOptions.class.cast(options.volumes(volumes)); - } + /** + * @see DockerTemplateOptions#volumes(java.util.Map) + */ + public static DockerTemplateOptions volumes(Map volumes) { + DockerTemplateOptions options = new DockerTemplateOptions(); + return DockerTemplateOptions.class.cast(options.volumes(volumes)); + } /** * @see DockerTemplateOptions#dns(String) @@ -229,6 +243,14 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable return DockerTemplateOptions.class.cast(options.cpuShares(cpuShares)); } + /** + * @see DockerTemplateOptions#env(java.util.List) + */ + public static DockerTemplateOptions env(List env) { + DockerTemplateOptions options = new DockerTemplateOptions(); + return DockerTemplateOptions.class.cast(options.env(env)); + } + // methods that only facilitate returning the correct object type /** diff --git a/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java b/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java index a4bcd3a9da..d1c1260ddd 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java +++ b/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java @@ -108,6 +108,10 @@ public class DockerComputeServiceAdapter implements containerConfigBuilder.cpuShares(templateOptions.getCpuShares().get()); } + if (templateOptions.getEnv().isPresent()) { + containerConfigBuilder.env(templateOptions.getEnv().get()); + } + if (templateOptions.getVolumes().isPresent()) { Map volumes = Maps.newLinkedHashMap(); for (String containerDir : templateOptions.getVolumes().get().values()) { diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/Config.java b/apis/docker/src/main/java/org/jclouds/docker/domain/Config.java index 5dd2b777d3..f5190fa600 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/Config.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/Config.java @@ -16,17 +16,18 @@ */ package org.jclouds.docker.domain; -import com.google.common.base.Objects; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.gson.annotations.SerializedName; -import org.jclouds.javax.annotation.Nullable; +import static com.google.common.base.Preconditions.checkNotNull; import java.beans.ConstructorProperties; import java.util.List; import java.util.Map; -import static com.google.common.base.Preconditions.checkNotNull; +import org.jclouds.javax.annotation.Nullable; + +import com.google.common.base.Objects; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.gson.annotations.SerializedName; public class Config { @@ -291,10 +292,10 @@ public class Config { private boolean attachStdout; private boolean attachStderr; private Map exposedPorts = ImmutableMap.of(); + private List env = ImmutableList.of(); private boolean tty; private boolean openStdin; private boolean stdinOnce; - private List env = ImmutableList.of(); private List cmd = ImmutableList.of(); private List dns = ImmutableList.of(); private String imageId; From 01f498c2514ceb4715add994f6b0ba92cc800f8a Mon Sep 17 00:00:00 2001 From: Andrew Kennedy Date: Sun, 12 Oct 2014 01:52:05 +0100 Subject: [PATCH 15/81] Added non-null Entrypoint data to test JSON file for parsing --- apis/docker/src/test/resources/container.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apis/docker/src/test/resources/container.json b/apis/docker/src/test/resources/container.json index b35301278f..839abb6de2 100644 --- a/apis/docker/src/test/resources/container.json +++ b/apis/docker/src/test/resources/container.json @@ -15,7 +15,7 @@ "CpuShares": 0, "Cpuset": "", "Domainname": "", - "Entrypoint": null, + "Entrypoint": [ "/usr/bin/sshd", "-d" ], "Env": [ "HOME=/", "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" From 6121757377f719231306e920d0ce73832cb61f36 Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Thu, 23 Oct 2014 10:32:47 -0700 Subject: [PATCH 16/81] Revert "JCLOUDS-653: Address Guava 18 deprecations" This reverts commit b851f0ac3a6fd23720d67dbfad6954b2153bbc4b. The above change made the floor guava version of jclouds latest guava. This is disasterous for compatibility. --- .../src/main/java/org/jclouds/docker/domain/Container.java | 3 +-- .../main/java/org/jclouds/docker/domain/ExposedPorts.java | 3 +-- .../src/main/java/org/jclouds/docker/domain/HostConfig.java | 3 +-- .../src/main/java/org/jclouds/docker/domain/Image.java | 3 +-- .../java/org/jclouds/docker/domain/NetworkSettings.java | 3 +-- .../src/main/java/org/jclouds/docker/domain/Port.java | 3 +-- .../src/main/java/org/jclouds/docker/domain/State.java | 3 +-- .../src/main/java/org/jclouds/docker/domain/Version.java | 3 +-- .../org/jclouds/docker/internal/BaseDockerMockTest.java | 6 +++--- 9 files changed, 11 insertions(+), 19 deletions(-) diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/Container.java b/apis/docker/src/main/java/org/jclouds/docker/domain/Container.java index 4e7c059c9d..efaf5b515d 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/Container.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/Container.java @@ -16,7 +16,6 @@ */ package org.jclouds.docker.domain; -import com.google.common.base.MoreObjects; import com.google.common.base.Objects; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; @@ -212,7 +211,7 @@ public class Container { @Override public String toString() { - return MoreObjects.toStringHelper(this) + return Objects.toStringHelper(this) .add("id", id) .add("name", name) .add("created", created) diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/ExposedPorts.java b/apis/docker/src/main/java/org/jclouds/docker/domain/ExposedPorts.java index 347ad3a5ea..b7a831a64c 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/ExposedPorts.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/ExposedPorts.java @@ -22,7 +22,6 @@ import java.util.Set; import org.jclouds.javax.annotation.Nullable; -import com.google.common.base.MoreObjects; import com.google.common.base.Objects; import com.google.common.collect.ImmutableSet; import com.google.gson.annotations.SerializedName; @@ -66,7 +65,7 @@ public class ExposedPorts { @Override public String toString() { - return MoreObjects.toStringHelper(this) + return Objects.toStringHelper(this) .add("portAndProtocol", portAndProtocol) .add("hostPorts", hostPorts) .toString(); diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/HostConfig.java b/apis/docker/src/main/java/org/jclouds/docker/domain/HostConfig.java index 94155c36fe..275961fe9e 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/HostConfig.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/HostConfig.java @@ -23,7 +23,6 @@ import java.util.Map; import org.jclouds.javax.annotation.Nullable; -import com.google.common.base.MoreObjects; import com.google.common.base.Objects; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; @@ -136,7 +135,7 @@ public class HostConfig { @Override public String toString() { - return MoreObjects.toStringHelper(this) + return Objects.toStringHelper(this) .add("containerIDFile", containerIDFile) .add("binds", binds) .add("lxcConf", lxcConf) diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/Image.java b/apis/docker/src/main/java/org/jclouds/docker/domain/Image.java index 80d6f6818a..186ece32f4 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/Image.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/Image.java @@ -16,7 +16,6 @@ */ package org.jclouds.docker.domain; -import com.google.common.base.MoreObjects; import com.google.common.base.Objects; import com.google.common.collect.ImmutableList; import com.google.gson.annotations.SerializedName; @@ -133,7 +132,7 @@ public class Image { @Override public String toString() { - return MoreObjects.toStringHelper(this) + return Objects.toStringHelper(this) .add("id", id) .add("parent", parent) .add("created", created) diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/NetworkSettings.java b/apis/docker/src/main/java/org/jclouds/docker/domain/NetworkSettings.java index 0c60719bf1..23f3fbede1 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/NetworkSettings.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/NetworkSettings.java @@ -16,7 +16,6 @@ */ package org.jclouds.docker.domain; -import com.google.common.base.MoreObjects; import com.google.common.base.Objects; import com.google.common.collect.ImmutableMap; import com.google.gson.annotations.SerializedName; @@ -100,7 +99,7 @@ public class NetworkSettings { @Override public String toString() { - return MoreObjects.toStringHelper(this) + return Objects.toStringHelper(this) .add("ipAddress", ipAddress) .add("ipPrefixLen", ipPrefixLen) .add("gateway", gateway) diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/Port.java b/apis/docker/src/main/java/org/jclouds/docker/domain/Port.java index 3a43ec55ec..b49dfa6945 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/Port.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/Port.java @@ -16,7 +16,6 @@ */ package org.jclouds.docker.domain; -import com.google.common.base.MoreObjects; import com.google.common.base.Objects; import com.google.gson.annotations.SerializedName; @@ -79,7 +78,7 @@ public class Port { @Override public String toString() { - return MoreObjects.toStringHelper(this) + return Objects.toStringHelper(this) .add("privatePort", privatePort) .add("publicPort", publicPort) .add("type", type) diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/State.java b/apis/docker/src/main/java/org/jclouds/docker/domain/State.java index cc665c8b09..ac1ba85e01 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/State.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/State.java @@ -19,7 +19,6 @@ package org.jclouds.docker.domain; import static com.google.common.base.Preconditions.checkNotNull; import java.beans.ConstructorProperties; -import com.google.common.base.MoreObjects; import com.google.common.base.Objects; import com.google.gson.annotations.SerializedName; @@ -93,7 +92,7 @@ public class State { @Override public String toString() { - return MoreObjects.toStringHelper(this) + return Objects.toStringHelper(this) .add("pid", pid) .add("running", running) .add("exitCode", exitCode) diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/Version.java b/apis/docker/src/main/java/org/jclouds/docker/domain/Version.java index f6dbb4942d..445a225c3d 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/Version.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/Version.java @@ -19,7 +19,6 @@ package org.jclouds.docker.domain; import static com.google.common.base.Preconditions.checkNotNull; import java.beans.ConstructorProperties; -import com.google.common.base.MoreObjects; import com.google.common.base.Objects; import com.google.gson.annotations.SerializedName; @@ -93,7 +92,7 @@ public class Version { @Override public String toString() { - return MoreObjects.toStringHelper(this) + return Objects.toStringHelper(this) .add("arch", arch) .add("gitCommit", gitCommit) .add("goVersion", goVersion) diff --git a/apis/docker/src/test/java/org/jclouds/docker/internal/BaseDockerMockTest.java b/apis/docker/src/test/java/org/jclouds/docker/internal/BaseDockerMockTest.java index 04020b6275..146b2a051d 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/internal/BaseDockerMockTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/internal/BaseDockerMockTest.java @@ -16,7 +16,7 @@ */ package org.jclouds.docker.internal; -import static com.google.common.util.concurrent.MoreExecutors.newDirectExecutorService; +import static com.google.common.util.concurrent.MoreExecutors.sameThreadExecutor; import static org.jclouds.http.utils.Queries.encodeQueryLine; import static org.jclouds.util.Strings2.toStringAndClose; import static org.testng.Assert.assertEquals; @@ -45,8 +45,8 @@ import com.squareup.okhttp.mockwebserver.RecordedRequest; * Base class for all Docker mock tests. */ public class BaseDockerMockTest { - private final Set modules = ImmutableSet. of(new ExecutorServiceModule(newDirectExecutorService(), - newDirectExecutorService())); + private final Set modules = ImmutableSet. of(new ExecutorServiceModule(sameThreadExecutor(), + sameThreadExecutor())); protected String provider; From 7bdff3f9caaa40180bf4374a821a49c2d94a5fcd Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Sun, 26 Oct 2014 12:28:39 -0700 Subject: [PATCH 17/81] JCLOUDS-750 AutoValue all Docker value types. --- apis/docker/pom.xml | 7 +- .../functions/ContainerToNodeMetadata.java | 31 +- .../compute/functions/ImageToImage.java | 8 +- .../compute/functions/StateToStatus.java | 2 +- .../strategy/DockerComputeServiceAdapter.java | 28 +- .../docker/config/DockerParserModule.java | 56 ++-- .../org/jclouds/docker/domain/Config.java | 311 ++++-------------- .../org/jclouds/docker/domain/Container.java | 266 +++------------ .../jclouds/docker/domain/ExposedPorts.java | 91 +---- .../org/jclouds/docker/domain/HostConfig.java | 167 ++-------- .../java/org/jclouds/docker/domain/Image.java | 226 ++----------- .../docker/domain/NetworkSettings.java | 109 ++---- .../java/org/jclouds/docker/domain/Port.java | 73 +--- .../java/org/jclouds/docker/domain/State.java | 152 +-------- .../org/jclouds/docker/domain/Version.java | 152 +-------- .../docker/internal/NullSafeCopies.java | 39 +++ .../DockerComputeServiceAdapterLiveTest.java | 4 +- .../ContainerToNodeMetadataTest.java | 38 ++- .../compute/functions/ImageToImageTest.java | 28 +- .../compute/functions/StateToStatusTest.java | 8 +- .../docker/config/DockerParserModuleTest.java | 47 +-- .../docker/features/RemoteApiLiveTest.java | 24 +- .../docker/features/RemoteApiMockTest.java | 16 +- 23 files changed, 425 insertions(+), 1458 deletions(-) create mode 100644 apis/docker/src/main/java/org/jclouds/docker/internal/NullSafeCopies.java diff --git a/apis/docker/pom.xml b/apis/docker/pom.xml index d179a41550..ae9c675c84 100644 --- a/apis/docker/pom.xml +++ b/apis/docker/pom.xml @@ -63,7 +63,12 @@ commons-compress 1.5 - + + com.google.auto.value + auto-value + 1.0-rc2 + provided + org.apache.jclouds.driver jclouds-sshj diff --git a/apis/docker/src/main/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadata.java b/apis/docker/src/main/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadata.java index 3ec7f5cb82..7b070fbfbe 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadata.java +++ b/apis/docker/src/main/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadata.java @@ -76,26 +76,25 @@ public class ContainerToNodeMetadata implements Function getPrivateIpAddresses(Container container) { - if (container.getNetworkSettings() == null) return ImmutableList.of(); - return ImmutableList.of(container.getNetworkSettings().getIpAddress()); + if (container.networkSettings() == null) return ImmutableList.of(); + return ImmutableList.of(container.networkSettings().ipAddress()); } private List getPublicIpAddresses() { @@ -120,16 +119,16 @@ public class ContainerToNodeMetadata implements Function>> ports = container.getNetworkSettings().getPorts(); + if (container.networkSettings() != null) { + Map>> ports = container.networkSettings().ports(); if (ports != null && ports.containsKey("22/tcp")) { return Integer.parseInt(getOnlyElement(ports.get("22/tcp")).get("HostPort")); } // this is needed in case the container list is coming from listContainers - } else if (container.getPorts() != null) { - for (Port port : container.getPorts()) { - if (port.getPrivatePort() == 22) { - return port.getPublicPort(); + } else if (container.ports() != null) { + for (Port port : container.ports()) { + if (port.privatePort() == 22) { + return port.publicPort(); } } } diff --git a/apis/docker/src/main/java/org/jclouds/docker/compute/functions/ImageToImage.java b/apis/docker/src/main/java/org/jclouds/docker/compute/functions/ImageToImage.java index 4e3358df44..551c441040 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/compute/functions/ImageToImage.java +++ b/apis/docker/src/main/java/org/jclouds/docker/compute/functions/ImageToImage.java @@ -44,7 +44,7 @@ public class ImageToImage implements Function { @Override public Status apply(final State state) { if (state == null) return Status.UNRECOGNIZED; - return state.isRunning() ? Status.RUNNING : Status.TERMINATED; + return state.running() ? Status.RUNNING : Status.TERMINATED; } } diff --git a/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java b/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java index d1c1260ddd..fa83ffc8f6 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java +++ b/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java @@ -89,7 +89,7 @@ public class DockerComputeServiceAdapter implements } Config.Builder containerConfigBuilder = Config.builder() - .imageId(imageId) + .image(imageId) .exposedPorts(exposedPorts); if (templateOptions.getCommands().isPresent()) { @@ -123,7 +123,7 @@ public class DockerComputeServiceAdapter implements logger.debug(">> creating new container with containerConfig(%s)", containerConfig); Container container = api.getRemoteApi().createContainer(name, containerConfig); - logger.trace("<< container(%s)", container.getId()); + logger.trace("<< container(%s)", container.id()); HostConfig.Builder hostConfigBuilder = HostConfig.builder() .publishAllPorts(true) @@ -140,13 +140,13 @@ public class DockerComputeServiceAdapter implements } HostConfig hostConfig = hostConfigBuilder.build(); - api.getRemoteApi().startContainer(container.getId(), hostConfig); - container = api.getRemoteApi().inspectContainer(container.getId()); - if (container.getState().getExitCode() != 0) { - destroyNode(container.getId()); - throw new IllegalStateException(String.format("Container %s has not started correctly", container.getId())); + api.getRemoteApi().startContainer(container.id(), hostConfig); + container = api.getRemoteApi().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.getId(), + return new NodeAndInitialCredentials(container, container.id(), LoginCredentials.builder().user(loginUser).password(loginUserPassword).build()); } @@ -166,9 +166,11 @@ public class DockerComputeServiceAdapter implements Set images = Sets.newHashSet(); for (Image image : api.getRemoteApi().listImages()) { // less efficient than just listImages but returns richer json that needs repoTags coming from listImages - Image inspected = api.getRemoteApi().inspectImage(image.getId()); - if (inspected.getRepoTags().isEmpty()) { - inspected = Image.builder().fromImage(inspected).repoTags(image.getRepoTags()).build(); + Image inspected = api.getRemoteApi().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()); } images.add(inspected); } @@ -182,7 +184,7 @@ public class DockerComputeServiceAdapter implements @Override public boolean apply(Image input) { - return input.getId().equals(imageId); + return input.id().equals(imageId); } }, null); } @@ -192,7 +194,7 @@ public class DockerComputeServiceAdapter implements Set containers = Sets.newHashSet(); for (Container container : api.getRemoteApi().listContainers(ListContainerOptions.Builder.all(true))) { // less efficient than just listNodes but returns richer json - containers.add(api.getRemoteApi().inspectContainer(container.getId())); + containers.add(api.getRemoteApi().inspectContainer(container.id())); } return containers; } diff --git a/apis/docker/src/main/java/org/jclouds/docker/config/DockerParserModule.java b/apis/docker/src/main/java/org/jclouds/docker/config/DockerParserModule.java index 533184f82e..ea777a4192 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/config/DockerParserModule.java +++ b/apis/docker/src/main/java/org/jclouds/docker/config/DockerParserModule.java @@ -16,47 +16,35 @@ */ package org.jclouds.docker.config; -import com.google.common.collect.ImmutableMap; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonDeserializationContext; -import com.google.gson.JsonDeserializer; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; -import com.google.inject.AbstractModule; -import com.google.inject.Provides; -import org.jclouds.docker.domain.Container; +import java.lang.reflect.Field; + import org.jclouds.json.config.GsonModule; -import javax.inject.Singleton; -import java.lang.reflect.Type; -import java.util.Map; +import com.google.gson.FieldNamingPolicy; +import com.google.gson.FieldNamingStrategy; +import com.google.inject.AbstractModule; public class DockerParserModule extends AbstractModule { - - @Override - protected void configure() { + @Override protected void configure() { + bind(FieldNamingStrategy.class).toInstance(FIELD_NAMING_STRATEGY); bind(GsonModule.DateAdapter.class).to(GsonModule.Iso8601DateAdapter.class); } - @Provides - @Singleton - public Map provideCustomAdapterBindings() { - return new ImmutableMap.Builder() - .put(Container.class, new ContainerTypeAdapter()) - .build(); - } + /** When serializing, Most fields are UpperCamelCase, with some exceptions. */ + private static final FieldNamingStrategy FIELD_NAMING_STRATEGY = new FieldNamingStrategy() { + private final FieldNamingStrategy delegate = FieldNamingPolicy.UPPER_CAMEL_CASE; - protected static class ContainerTypeAdapter implements JsonDeserializer { - - @Override - public Container deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws - JsonParseException { - Gson gson = new GsonBuilder().serializeNulls().create(); - final JsonObject jsonObject = json.getAsJsonObject(); - return gson.fromJson(jsonObject, Container.class); + @Override public String translateName(Field f) { + String result = delegate.translateName(f); + // IP not Ip as code wins over docs https://github.com/docker/docker/blob/master/daemon/network_settings.go + if (result.equals("IpAddress")) { + return "IPAddress"; + } else if (result.equals("IpPrefixLen")) { + return "IPPrefixLen"; + } else if (result.equals("Ip")) { + return "IP"; + } + return result; } - } - + }; } diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/Config.java b/apis/docker/src/main/java/org/jclouds/docker/domain/Config.java index f5190fa600..396ab2f3fa 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/Config.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/Config.java @@ -17,260 +17,78 @@ package org.jclouds.docker.domain; import static com.google.common.base.Preconditions.checkNotNull; +import static org.jclouds.docker.internal.NullSafeCopies.copyOf; -import java.beans.ConstructorProperties; import java.util.List; import java.util.Map; import org.jclouds.javax.annotation.Nullable; +import org.jclouds.json.SerializedNames; -import com.google.common.base.Objects; +import com.google.auto.value.AutoValue; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import com.google.gson.annotations.SerializedName; -public class Config { +@AutoValue +public abstract class Config { + @Nullable public abstract String hostname(); - @SerializedName("Hostname") - private final String hostname; - @SerializedName("Domainname") - private final String domainName; - @SerializedName("User") - private final String user; - @SerializedName("Memory") - private final int memory; - @SerializedName("MemorySwap") - private final int memorySwap; - @SerializedName("CpuShares") - private final int cpuShares; - @SerializedName("AttachStdin") - private final boolean attachStdin; - @SerializedName("AttachStdout") - private final boolean attachStdout; - @SerializedName("AttachStderr") - private final boolean attachStderr; - @SerializedName("ExposedPorts") - private final Map exposedPorts; - @SerializedName("Tty") - private final boolean tty; - @SerializedName("OpenStdin") - private final boolean openStdin; - @SerializedName("StdinOnce") - private final boolean stdinOnce; - @SerializedName("Env") - private final List env; - @SerializedName("Cmd") - private final List cmd; - @SerializedName("Dns") - private final List dns; - @SerializedName("Image") - private final String imageId; - @SerializedName("Volumes") - private final Map volumes; - @SerializedName("VolumesFrom") - private final String volumesFrom; - @SerializedName("WorkingDir") - private final String workingDir; - @SerializedName("Entrypoint") - private final List entrypoint; - @SerializedName("NetworkDisabled") - private final boolean networkDisabled; - @SerializedName("OnBuild") - private final List onBuild; + @Nullable public abstract String domainname(); + @Nullable public abstract String user(); - @ConstructorProperties({ "Hostname", "Domainname", "User", "Memory", "MemorySwap", "CpuShares", "AttachStdin", - "AttachStdout", "AttachStderr", "ExposedPorts", "Tty", "OpenStdin", "StdinOnce", "Env", "Cmd", - "Dns", "Image", "Volumes", "VolumesFrom", "WorkingDir", "Entrypoint", "NetworkDisabled", "OnBuild" }) - protected Config(@Nullable String hostname, @Nullable String domainName, @Nullable String user, - int memory, int memorySwap, int cpuShares, boolean attachStdin, boolean attachStdout, - boolean attachStderr, Map exposedPorts, boolean tty, boolean openStdin, - boolean stdinOnce, @Nullable List env, @Nullable List cmd, - @Nullable List dns, String imageId, @Nullable Map volumes, - @Nullable String volumesFrom, @Nullable String workingDir, @Nullable List entrypoint, - @Nullable boolean networkDisabled, @Nullable List onBuild) { - this.hostname = hostname; - this.domainName = domainName; - this.user = user; - this.memory = checkNotNull(memory, "memory"); - this.memorySwap = checkNotNull(memorySwap, "memorySwap"); - this.cpuShares = checkNotNull(cpuShares, "cpuShares"); - this.attachStdin = checkNotNull(attachStdin, "attachStdin"); - this.attachStdout = checkNotNull(attachStdout, "attachStdout"); - this.attachStderr = checkNotNull(attachStderr, "attachStderr"); - this.exposedPorts = exposedPorts != null ? ImmutableMap.copyOf(exposedPorts) : ImmutableMap. of(); - this.tty = checkNotNull(tty, "tty"); - this.openStdin = checkNotNull(openStdin, "openStdin"); - this.stdinOnce = checkNotNull(stdinOnce, "stdinOnce"); - this.env = env != null ? ImmutableList.copyOf(env) : ImmutableList. of(); - this.cmd = cmd != null ? ImmutableList.copyOf(cmd) : ImmutableList. of(); - this.dns = dns != null ? ImmutableList.copyOf(dns) : ImmutableList. of(); - this.imageId = checkNotNull(imageId, "imageId"); - this.volumes = volumes != null ? ImmutableMap.copyOf(volumes) : ImmutableMap. of(); - this.volumesFrom = volumesFrom; - this.workingDir = workingDir; - this.entrypoint = entrypoint; - this.networkDisabled = networkDisabled; - this.onBuild = onBuild != null ? ImmutableList.copyOf(onBuild) : ImmutableList. of(); - } + public abstract int memory(); - public String getHostname() { - return hostname; - } + public abstract int memorySwap(); - public String getDomainName() { - return domainName; - } + public abstract int cpuShares(); - public String getUser() { - return user; - } + public abstract boolean attachStdin(); - public int getMemory() { - return memory; - } + public abstract boolean attachStdout(); - public int getMemorySwap() { - return memorySwap; - } + public abstract boolean attachStderr(); - public int getCpuShares() { - return cpuShares; - } + public abstract Map exposedPorts(); - public boolean isAttachStdin() { - return attachStdin; - } + public abstract boolean tty(); - public boolean isAttachStdout() { - return attachStdout; - } + public abstract boolean openStdin(); - public boolean isAttachStderr() { - return attachStderr; - } + public abstract boolean stdinOnce(); - public Map getExposedPorts() { - return exposedPorts; - } + public abstract List env(); - public boolean isTty() { - return tty; - } + public abstract List cmd(); - public boolean isOpenStdin() { - return openStdin; - } + public abstract List dns(); - public boolean isStdinOnce() { - return stdinOnce; - } + public abstract String image(); - public List getEnv() { - return env; - } + public abstract Map volumes(); - public List getCmd() { - return cmd; - } + @Nullable public abstract String volumesFrom(); - public List getDns() { - return dns; - } + @Nullable public abstract String workingDir(); - public String getImageId() { - return imageId; - } + public abstract List entrypoint(); - public Map getVolumes() { - return volumes; - } + public abstract boolean networkDisabled(); - public String getVolumesFrom() { - return volumesFrom; - } + public abstract List onBuild(); - public String getWorkingDir() { - return workingDir; - } - - public List getEntrypoint() { - return entrypoint; - } - - public boolean isNetworkDisabled() { - return networkDisabled; - } - - public List getOnBuild() { - return onBuild; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - Config that = (Config) o; - - return Objects.equal(this.hostname, that.hostname) && - Objects.equal(this.domainName, that.domainName) && - Objects.equal(this.user, that.user) && - Objects.equal(this.memory, that.memory) && - Objects.equal(this.memorySwap, that.memorySwap) && - Objects.equal(this.cpuShares, that.cpuShares) && - Objects.equal(this.attachStdin, that.attachStdin) && - Objects.equal(this.attachStdout, that.attachStdout) && - Objects.equal(this.attachStderr, that.attachStderr) && - Objects.equal(this.exposedPorts, that.exposedPorts) && - Objects.equal(this.tty, that.tty) && - Objects.equal(this.openStdin, that.openStdin) && - Objects.equal(this.stdinOnce, that.stdinOnce) && - Objects.equal(this.env, that.env) && - Objects.equal(this.cmd, that.cmd) && - Objects.equal(this.dns, that.dns) && - Objects.equal(this.imageId, that.imageId) && - Objects.equal(this.volumes, that.volumes) && - Objects.equal(this.volumesFrom, that.volumesFrom) && - Objects.equal(this.workingDir, that.workingDir) && - Objects.equal(this.entrypoint, that.entrypoint) && - Objects.equal(this.onBuild, that.onBuild); - } - - @Override - public int hashCode() { - return Objects.hashCode(hostname, domainName, user, memory, memorySwap, cpuShares, attachStdin, attachStdout, - attachStderr, exposedPorts, tty, openStdin, stdinOnce, env, cmd, dns, imageId, volumes, - volumesFrom, workingDir, entrypoint, networkDisabled, onBuild); - } - - @Override - public String toString() { - return Objects.toStringHelper(this) - .add("hostname", hostname) - .add("domainName", domainName) - .add("user", user) - .add("memory", memory) - .add("memorySwap", memorySwap) - .add("cpuShares", cpuShares) - .add("attachStdin", attachStdin) - .add("attachStdout", attachStdout) - .add("attachStderr", attachStderr) - .add("exposedPorts", exposedPorts) - .add("tty", tty) - .add("openStdin", openStdin) - .add("stdinOnce", stdinOnce) - .add("env", env) - .add("cmd", cmd) - .add("dns", dns) - .add("imageId", imageId) - .add("volumes", volumes) - .add("volumesFrom", volumesFrom) - .add("workingDir", workingDir) - .add("entrypoint", entrypoint) - .add("networkDisabled", networkDisabled) - .add("onBuild", onBuild) - .toString(); + @SerializedNames( + { "Hostname", "Domainname", "User", "Memory", "MemorySwap", "CpuShares", "AttachStdin", "AttachStdout", + "AttachStderr", "ExposedPorts", "Tty", "OpenStdin", "StdinOnce", "Env", "Cmd", "Dns", "Image", "Volumes", + "VolumesFrom", "WorkingDir", "Entrypoint", "NetworkDisabled", "OnBuild" }) + public static Config create(String hostname, String domainname, String user, int memory, int memorySwap, + int cpuShares, boolean attachStdin, boolean attachStdout, boolean attachStderr, Map exposedPorts, + boolean tty, boolean openStdin, boolean stdinOnce, List env, List cmd, List dns, + String image, Map volumes, String volumesFrom, String workingDir, List entrypoint, + boolean networkDisabled, List onBuild) { + return new AutoValue_Config(hostname, domainname, user, memory, memorySwap, cpuShares, attachStdin, attachStdout, + attachStderr, copyOf(exposedPorts), tty, openStdin, stdinOnce, copyOf(env), copyOf(cmd), copyOf(dns), image, + copyOf(volumes), volumesFrom, workingDir, copyOf(entrypoint), networkDisabled, copyOf(onBuild)); } public static Builder builder() { @@ -283,7 +101,7 @@ public class Config { public static final class Builder { private String hostname; - private String domainName; + private String domainname; private String user; private int memory; private int memorySwap; @@ -298,7 +116,7 @@ public class Config { private boolean stdinOnce; private List cmd = ImmutableList.of(); private List dns = ImmutableList.of(); - private String imageId; + private String image; private Map volumes = ImmutableMap.of(); private String volumesFrom; private String workingDir; @@ -311,8 +129,8 @@ public class Config { return this; } - public Builder domainName(String domainName) { - this.domainName = domainName; + public Builder domainname(String domainname) { + this.domainname = domainname; return this; } @@ -386,8 +204,8 @@ public class Config { return this; } - public Builder imageId(String imageId) { - this.imageId = imageId; + public Builder image(String image) { + this.image = image; return this; } @@ -422,36 +240,19 @@ public class Config { } public Config build() { - return new Config(hostname, domainName, user, memory, memorySwap, cpuShares, attachStdin, attachStdout, - attachStderr, exposedPorts, tty, openStdin, stdinOnce, env, cmd, dns, imageId, volumes, - volumesFrom, workingDir, entrypoint, networkDisabled, onBuild); + return Config.create(hostname, domainname, user, memory, memorySwap, cpuShares, attachStdin, attachStdout, + attachStderr, exposedPorts, tty, openStdin, stdinOnce, env, cmd, dns, image, volumes, volumesFrom, + workingDir, entrypoint, networkDisabled, onBuild); } public Builder fromConfig(Config in) { - return this - .hostname(in.getHostname()) - .domainName(in.getDomainName()) - .user(in.getUser()) - .memory(in.getMemory()) - .memorySwap(in.getMemorySwap()) - .cpuShares(in.getCpuShares()) - .attachStdin(in.isAttachStdin()) - .attachStdout(in.isAttachStdout()) - .attachStderr(in.isAttachStderr()) - .exposedPorts(in.getExposedPorts()) - .tty(in.isTty()) - .openStdin(in.isOpenStdin()) - .stdinOnce(in.isStdinOnce()) - .env(in.getEnv()) - .cmd(in.getCmd()) - .dns(in.getDns()) - .imageId(in.getImageId()) - .volumes(in.getVolumes()) - .volumesFrom(in.getVolumesFrom()) - .workingDir(in.getWorkingDir()) - .entrypoint(in.getEntrypoint()) - .networkDisabled(in.isNetworkDisabled()) - .onBuild(in.getOnBuild()); + return hostname(in.hostname()).domainname(in.domainname()).user(in.user()).memory(in.memory()) + .memorySwap(in.memorySwap()).cpuShares(in.cpuShares()).attachStdin(in.attachStdin()) + .attachStdout(in.attachStdout()).attachStderr(in.attachStderr()).exposedPorts(in.exposedPorts()) + .tty(in.tty()).openStdin(in.openStdin()).stdinOnce(in.stdinOnce()).env(in.env()).cmd(in.cmd()) + .dns(in.dns()).image(in.image()).volumes(in.volumes()).volumesFrom(in.volumesFrom()) + .workingDir(in.workingDir()).entrypoint(in.entrypoint()).networkDisabled(in.networkDisabled()) + .onBuild(in.onBuild()); } } diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/Container.java b/apis/docker/src/main/java/org/jclouds/docker/domain/Container.java index efaf5b515d..b06c3a56f9 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/Container.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/Container.java @@ -16,222 +16,69 @@ */ package org.jclouds.docker.domain; -import com.google.common.base.Objects; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.gson.annotations.SerializedName; -import org.jclouds.javax.annotation.Nullable; +import static org.jclouds.docker.internal.NullSafeCopies.copyOf; -import java.beans.ConstructorProperties; -import java.util.Arrays; import java.util.List; import java.util.Map; -import static com.google.common.base.Preconditions.checkNotNull; +import org.jclouds.javax.annotation.Nullable; +import org.jclouds.json.SerializedNames; -public class Container { +import com.google.auto.value.AutoValue; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; - @SerializedName("Id") - private final String id; - @SerializedName("Name") - private final String name; - @SerializedName("Created") - private final String created; - @SerializedName("Path") - private final String path; - @SerializedName("Args") - private final String[] args; - @SerializedName("Config") - private final Config containerConfig; - @SerializedName("State") - private final State state; - @SerializedName("Image") - private final String image; - @SerializedName("NetworkSettings") - private final NetworkSettings networkSettings; - @SerializedName("ResolvConfPath") - private final String resolvConfPath; - @SerializedName("Driver") - private final String driver; - @SerializedName("ExecDriver") - private final String execDriver; - @SerializedName("Volumes") - private final Map volumes; - @SerializedName("VolumesRW") - private final Map volumesRW; - @SerializedName("Command") - private final String command; - @SerializedName("Status") - private final String status; - @SerializedName("HostConfig") - private final HostConfig hostConfig; - @SerializedName("Ports") - private final List ports; - @SerializedName("HostnamePath") - private final String hostnamePath; +@AutoValue +public abstract class Container { + public abstract String id(); - @ConstructorProperties({ "Id", "Name", "Created", "Path", "Args", "Config", "State", "Image", "NetworkSettings", - "ResolvConfPath", "Driver", "ExecDriver", "Volumes", "VolumesRW", "Command", "Status", "HostConfig", - "Ports", "HostnamePath" }) - protected Container(String id, @Nullable String name, @Nullable String created, @Nullable String path, @Nullable String[] args, - @Nullable Config containerConfig, @Nullable State state, @Nullable String image, @Nullable NetworkSettings networkSettings, - @Nullable String resolvConfPath, @Nullable String driver, @Nullable String execDriver, @Nullable Map volumes, - @Nullable Map volumesRW, @Nullable String command, @Nullable String status, - @Nullable HostConfig hostConfig, @Nullable List ports, @Nullable String hostnamePath) { - this.id = checkNotNull(id, "id"); - this.name = name; - this.created = created; - this.path = path; - this.args = args; - this.containerConfig = containerConfig; - this.state = state; - this.image = image; - this.networkSettings = networkSettings; - this.resolvConfPath = resolvConfPath; - this.driver = driver; - this.execDriver = execDriver; - this.volumes = volumes != null ? ImmutableMap.copyOf(volumes) : ImmutableMap.of(); - this.volumesRW = volumesRW != null ? ImmutableMap.copyOf(volumesRW) : ImmutableMap.of(); - this.command = command; - this.status = status; - this.hostConfig = hostConfig; - this.ports = ports != null ? ImmutableList.copyOf(ports) : ImmutableList.of(); - this.hostnamePath = hostnamePath; - } + @Nullable public abstract String name(); - public String getId() { - return id; - } + @Nullable public abstract String created(); - public String getName() { - return name; - } + @Nullable public abstract String path(); - public String getCreated() { - return created; - } + public abstract List args(); - public String getPath() { - return path; - } + @Nullable public abstract Config config(); - public String[] getArgs() { - return args; - } + @Nullable public abstract State state(); - public Config getContainerConfig() { - return containerConfig; - } + @Nullable public abstract String image(); - public State getState() { - return state; - } + @Nullable public abstract NetworkSettings networkSettings(); - public String getImage() { - return image; - } + @Nullable public abstract String resolvConfPath(); - public NetworkSettings getNetworkSettings() { - return networkSettings; - } + @Nullable public abstract String driver(); - public String getResolvConfPath() { - return resolvConfPath; - } + @Nullable public abstract String execDriver(); - public String getDriver() { - return driver; - } + public abstract Map volumes(); - public String getExecDriver() { - return execDriver; - } + public abstract Map volumesRW(); - public Map getVolumes() { - return volumes; - } + @Nullable public abstract String command(); - public Map getvolumesRW() { - return volumesRW; - } + @Nullable public abstract String status(); - public String getCommand() { - return command; - } + @Nullable public abstract HostConfig hostConfig(); - public String getStatus() { - return status; - } + public abstract List ports(); - public HostConfig getHostConfig() { - return hostConfig; - } + @Nullable public abstract String hostnamePath(); - public List getPorts() { - return ports; - } - - public String getHostnamePath() { - return hostnamePath; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - Container that = (Container) o; - - return Objects.equal(this.id, that.id) && - Objects.equal(this.name, that.name) && - Objects.equal(this.created, that.created) && - Objects.equal(this.path, that.path) && - Arrays.equals(this.args, that.args) && - Objects.equal(this.containerConfig, that.containerConfig) && - Objects.equal(this.state, that.state) && - Objects.equal(this.image, that.image) && - Objects.equal(this.networkSettings, that.networkSettings) && - Objects.equal(this.resolvConfPath, that.resolvConfPath) && - Objects.equal(this.driver, that.driver) && - Objects.equal(this.execDriver, that.execDriver) && - Objects.equal(this.volumes, that.volumes) && - Objects.equal(this.volumesRW, that.volumesRW) && - Objects.equal(this.command, that.command) && - Objects.equal(this.status, that.status) && - Objects.equal(this.hostConfig, that.hostConfig) && - Objects.equal(this.ports, that.ports) && - Objects.equal(this.hostnamePath, that.hostnamePath); - } - - @Override - public int hashCode() { - return Objects.hashCode(id, name, created, path, args, containerConfig, state, image, networkSettings, resolvConfPath, - driver, execDriver, volumes, volumesRW, command, status, hostConfig, ports, hostnamePath); - } - - @Override - public String toString() { - return Objects.toStringHelper(this) - .add("id", id) - .add("name", name) - .add("created", created) - .add("path", path) - .add("args", args) - .add("containerConfig", containerConfig) - .add("state", state) - .add("image", image) - .add("networkSettings", networkSettings) - .add("resolvConfPath", resolvConfPath) - .add("driver", driver) - .add("execDriver", execDriver) - .add("volumes", volumes) - .add("volumesRW", volumesRW) - .add("command", command) - .add("status", status) - .add("hostConfig", hostConfig) - .add("ports", ports) - .add("hostnamePath", hostnamePath) - .toString(); + @SerializedNames( + { "Id", "Name", "Created", "Path", "Args", "Config", "State", "Image", "NetworkSettings", "ResolvConfPath", + "Driver", "ExecDriver", "Volumes", "VolumesRW", "Command", "Status", "HostConfig", "Ports", + "HostnamePath" }) + public static Container create(String id, String name, String created, String path, List args, Config config, + State state, String image, NetworkSettings networkSettings, String resolvConfPath, String driver, + String execDriver, Map volumes, Map volumesRW, String command, String status, + HostConfig hostConfig, List ports, String hostnamePath) { + return new AutoValue_Container(id, name, created, path, copyOf(args), config, state, image, networkSettings, + resolvConfPath, driver, execDriver, copyOf(volumes), copyOf(volumesRW), command, status, hostConfig, + copyOf(ports), hostnamePath); } public static Builder builder() { @@ -248,8 +95,8 @@ public class Container { private String name; private String created; private String path; - private String[] args; - private Config containerConfig; + private List args; + private Config config; private State state; private String image; private NetworkSettings networkSettings; @@ -284,13 +131,13 @@ public class Container { return this; } - public Builder args(String[] args) { + public Builder args(List args) { this.args = args; return this; } - public Builder containerConfig(Config containerConfig) { - this.containerConfig = containerConfig; + public Builder config(Config config) { + this.config = config; return this; } @@ -360,31 +207,16 @@ public class Container { } public Container build() { - return new Container(id, name, created, path, args, containerConfig, state, image, networkSettings, resolvConfPath, + return Container.create(id, name, created, path, args, config, state, image, networkSettings, resolvConfPath, driver, execDriver, volumes, volumesRW, command, status, hostConfig, ports, hostnamePath); } public Builder fromContainer(Container in) { - return this - .id(in.getId()) - .name(in.getName()) - .created(in.getCreated()) - .path(in.getPath()) - .args(in.getArgs()) - .containerConfig(in.getContainerConfig()) - .state(in.getState()) - .image(in.getImage()) - .networkSettings(in.getNetworkSettings()) - .resolvConfPath(in.getResolvConfPath()) - .driver(in.getDriver()) - .execDriver(in.getExecDriver()) - .volumes(in.getVolumes()) - .volumesRW(in.getvolumesRW()) - .command(in.getCommand()) - .status(in.getStatus()) - .hostConfig(in.getHostConfig()) - .ports(in.getPorts()) - .hostnamePath(in.getHostnamePath()); + return this.id(in.id()).name(in.name()).created(in.created()).path(in.path()).args(in.args()) + .config(in.config()).state(in.state()).image(in.image()).networkSettings(in.networkSettings()) + .resolvConfPath(in.resolvConfPath()).driver(in.driver()).execDriver(in.execDriver()) + .volumes(in.volumes()).volumesRW(in.volumesRW()).command(in.command()).status(in.status()) + .hostConfig(in.hostConfig()).ports(in.ports()).hostnamePath(in.hostnamePath()); } } } diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/ExposedPorts.java b/apis/docker/src/main/java/org/jclouds/docker/domain/ExposedPorts.java index b7a831a64c..413c84f498 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/ExposedPorts.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/ExposedPorts.java @@ -16,91 +16,22 @@ */ package org.jclouds.docker.domain; -import static com.google.common.base.Preconditions.checkNotNull; -import java.beans.ConstructorProperties; -import java.util.Set; +import static org.jclouds.docker.internal.NullSafeCopies.copyOf; -import org.jclouds.javax.annotation.Nullable; +import java.util.List; -import com.google.common.base.Objects; -import com.google.common.collect.ImmutableSet; -import com.google.gson.annotations.SerializedName; +import org.jclouds.json.SerializedNames; -public class ExposedPorts { +import com.google.auto.value.AutoValue; - @SerializedName("PortAndProtocol") - private final String portAndProtocol; - @SerializedName("HostPorts") - private final Set hostPorts; +@AutoValue +public abstract class ExposedPorts { + public abstract String portAndProtocol(); - @ConstructorProperties({ "PortAndProtocol", "HostPorts" }) - protected ExposedPorts(String portAndProtocol, @Nullable Set hostPorts) { - this.portAndProtocol = checkNotNull(portAndProtocol, "portAndProtocol"); - this.hostPorts = hostPorts != null ? ImmutableSet.copyOf(hostPorts) : ImmutableSet. of(); - } + public abstract List hostPorts(); - public String getPortAndProtocol() { - return portAndProtocol; - } - - public Set getHostPorts() { - return hostPorts; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - ExposedPorts that = (ExposedPorts) o; - - return Objects.equal(this.portAndProtocol, that.portAndProtocol) && - Objects.equal(this.hostPorts, that.hostPorts); - } - - @Override - public int hashCode() { - return Objects.hashCode(portAndProtocol, hostPorts); - } - - @Override - public String toString() { - return Objects.toStringHelper(this) - .add("portAndProtocol", portAndProtocol) - .add("hostPorts", hostPorts) - .toString(); - } - - public static Builder builder() { - return new Builder(); - } - - public Builder toBuilder() { - return builder().fromExposedPorts(this); - } - - public static final class Builder { - - private String portAndProtocol; - private Set hostPorts = ImmutableSet.of(); - - public Builder portAndProtocol(String portAndProtocol) { - this.portAndProtocol = portAndProtocol; - return this; - } - - public Builder hostPorts(Set hostPorts) { - this.hostPorts = ImmutableSet.copyOf(checkNotNull(hostPorts, "hostPorts")); - return this; - } - - public ExposedPorts build() { - return new ExposedPorts(portAndProtocol, hostPorts); - } - - public Builder fromExposedPorts(ExposedPorts in) { - return this.portAndProtocol(in.getPortAndProtocol()) - .hostPorts(in.getHostPorts()); - } + @SerializedNames({ "PortAndProtocol", "HostPorts" }) + public static ExposedPorts create(String portAndProtocol, List hostPorts) { + return new AutoValue_ExposedPorts(portAndProtocol, copyOf(hostPorts)); } } diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/HostConfig.java b/apis/docker/src/main/java/org/jclouds/docker/domain/HostConfig.java index 275961fe9e..757157c457 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/HostConfig.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/HostConfig.java @@ -17,136 +17,47 @@ package org.jclouds.docker.domain; import static com.google.common.base.Preconditions.checkNotNull; -import java.beans.ConstructorProperties; +import static org.jclouds.docker.internal.NullSafeCopies.copyOf; + import java.util.List; import java.util.Map; import org.jclouds.javax.annotation.Nullable; +import org.jclouds.json.SerializedNames; -import com.google.common.base.Objects; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; +import com.google.auto.value.AutoValue; import com.google.common.collect.Lists; import com.google.common.collect.Maps; -import com.google.gson.annotations.SerializedName; -public class HostConfig { +@AutoValue +public abstract class HostConfig { + @Nullable public abstract String containerIDFile(); - @SerializedName("ContainerIDFile") - private final String containerIDFile; - @SerializedName("Binds") - private final List binds; - @SerializedName("LxcConf") - private final Map lxcConf; - @SerializedName("Privileged") - private final boolean privileged; - @SerializedName("Dns") - private final String dns; - @SerializedName("DnsSearch") - private final String dnsSearch; - @SerializedName("PortBindings") - private final Map>> portBindings; - @SerializedName("Links") - private final List links; - @SerializedName("PublishAllPorts") - private final boolean publishAllPorts; - @SerializedName("VolumesFrom") - private final List volumesFrom; + public abstract List binds(); - @ConstructorProperties({ "ContainerIDFile", "Binds", "LxcConf", "Privileged", "Dns", "DnsSearch", "PortBindings", + public abstract Map lxcConf(); + + public abstract boolean privileged(); + + @Nullable public abstract String dns(); + + @Nullable public abstract String dnsSearch(); + + public abstract Map>> portBindings(); + + public abstract List links(); + + public abstract boolean publishAllPorts(); + + public abstract List volumesFrom(); + + @SerializedNames({ "ContainerIDFile", "Binds", "LxcConf", "Privileged", "Dns", "DnsSearch", "PortBindings", "Links", "PublishAllPorts", "VolumesFrom" }) - protected HostConfig(@Nullable String containerIDFile, @Nullable List binds, - Map lxcConf, boolean privileged, @Nullable String dns, - @Nullable String dnsSearch, @Nullable Map>> portBindings, - @Nullable List links, boolean publishAllPorts, @Nullable List volumesFrom) { - this.containerIDFile = containerIDFile; - this.binds = binds != null ? ImmutableList.copyOf(binds) : ImmutableList. of(); - this.lxcConf = lxcConf != null ? ImmutableMap.copyOf(lxcConf) : ImmutableMap. of(); - this.privileged = checkNotNull(privileged, "privileged"); - this.dns = dns; - this.dnsSearch = dnsSearch; - this.portBindings = portBindings != null ? ImmutableMap.copyOf(portBindings) : ImmutableMap.>> of(); - this.links = links != null ? ImmutableList.copyOf(links) : ImmutableList. of(); - this.publishAllPorts = checkNotNull(publishAllPorts, "publishAllPorts"); - this.volumesFrom = volumesFrom != null ? ImmutableList.copyOf(volumesFrom) : ImmutableList. of(); - } - - public String getContainerIDFile() { - return containerIDFile; - } - - public List getBinds() { - return binds; - } - - public Map getLxcConf() { - return lxcConf; - } - - public boolean isPrivileged() { - return privileged; - } - - public String getDns() { return dns; } - - public String getDnsSearch() { return dnsSearch; } - - public Map>> getPortBindings() { - return portBindings; - } - - @Nullable - public List getLinks() { - return links; - } - - public boolean isPublishAllPorts() { - return publishAllPorts; - } - - public List getVolumesFrom() { - return volumesFrom; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - HostConfig that = (HostConfig) o; - - return Objects.equal(this.containerIDFile, that.containerIDFile) && - Objects.equal(this.binds, that.binds) && - Objects.equal(this.lxcConf, that.lxcConf) && - Objects.equal(this.privileged, that.privileged) && - Objects.equal(this.dns, that.dns) && - Objects.equal(this.dnsSearch, that.dnsSearch) && - Objects.equal(this.portBindings, that.portBindings) && - Objects.equal(this.links, that.links) && - Objects.equal(this.publishAllPorts, that.publishAllPorts) && - Objects.equal(this.volumesFrom, that.volumesFrom); - } - - @Override - public int hashCode() { - return Objects.hashCode(containerIDFile, binds, lxcConf, privileged, dns, dnsSearch, portBindings, links, - publishAllPorts, volumesFrom); - } - - @Override - public String toString() { - return Objects.toStringHelper(this) - .add("containerIDFile", containerIDFile) - .add("binds", binds) - .add("lxcConf", lxcConf) - .add("privileged", privileged) - .add("dns", dns) - .add("dnsSearch", dnsSearch) - .add("portBindings", portBindings) - .add("links", links) - .add("publishAllPorts", publishAllPorts) - .add("volumesFrom", volumesFrom) - .toString(); + public static HostConfig create(String containerIDFile, List binds, Map lxcConf, + boolean privileged, String dns, String dnsSearch, Map>> portBindings, + List links, boolean publishAllPorts, List volumesFrom) { + return new AutoValue_HostConfig(containerIDFile, copyOf(binds), copyOf(lxcConf), privileged, dns, dnsSearch, + copyOf(portBindings), copyOf(links), publishAllPorts, copyOf(volumesFrom)); } public static Builder builder() { @@ -221,22 +132,14 @@ public class HostConfig { } public HostConfig build() { - return new HostConfig(containerIDFile, binds, lxcConf, privileged, dns, dnsSearch, portBindings, links, - publishAllPorts, volumesFrom); + return HostConfig.create(containerIDFile, binds, lxcConf, privileged, dns, dnsSearch, portBindings, links, + publishAllPorts, volumesFrom); } public Builder fromHostConfig(HostConfig in) { - return this - .containerIDFile(in.getContainerIDFile()) - .binds(in.getBinds()) - .lxcConf(in.getLxcConf()) - .privileged(in.isPrivileged()) - .dns(in.getDns()) - .dnsSearch(in.getDnsSearch()) - .links(in.getLinks()) - .portBindings(in.getPortBindings()) - .publishAllPorts(in.isPublishAllPorts()) - .volumesFrom(in.getVolumesFrom()); + return this.containerIDFile(in.containerIDFile()).binds(in.binds()).lxcConf(in.lxcConf()) + .privileged(in.privileged()).dns(in.dns()).dnsSearch(in.dnsSearch()).links(in.links()) + .portBindings(in.portBindings()).publishAllPorts(in.publishAllPorts()).volumesFrom(in.volumesFrom()); } } } diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/Image.java b/apis/docker/src/main/java/org/jclouds/docker/domain/Image.java index 186ece32f4..99b25943e9 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/Image.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/Image.java @@ -16,224 +16,42 @@ */ package org.jclouds.docker.domain; -import com.google.common.base.Objects; -import com.google.common.collect.ImmutableList; -import com.google.gson.annotations.SerializedName; -import org.jclouds.javax.annotation.Nullable; +import static org.jclouds.docker.internal.NullSafeCopies.copyOf; -import java.beans.ConstructorProperties; import java.util.List; -import static com.google.common.base.Preconditions.checkNotNull; +import org.jclouds.javax.annotation.Nullable; +import org.jclouds.json.SerializedNames; -public class Image { +import com.google.auto.value.AutoValue; - @SerializedName("Id") - private final String id; - @SerializedName("Parent") - private final String parent; - @SerializedName("Created") - private final String created; - @SerializedName("Container") - private final String container; - @SerializedName("DockerVersion") - private final String dockerVersion; - @SerializedName("Architecture") - private final String architecture; - @SerializedName("Os") - private final String os; - @SerializedName("Size") - private final long size; - @SerializedName("VirtualSize") - private final long virtualSize; - @SerializedName("RepoTags") - private final List repoTags; +@AutoValue +public abstract class Image { + public abstract String id(); - @ConstructorProperties({ "Id", "Parent", "Created", "Container", "DockerVersion", "Architecture", "Os", "Size", - "VirtualSize", "RepoTags", "Architecture" }) - protected Image(String id, @Nullable String parent, @Nullable String created, @Nullable String container, - @Nullable String dockerVersion, @Nullable String architecture, @Nullable String os, long size, - @Nullable long virtualSize, @Nullable List repoTags) { - this.id = checkNotNull(id, "id"); - this.parent = parent; - this.created = created; - this.container = container; - this.dockerVersion = dockerVersion; - this.architecture = architecture; - this.os = os; - this.size = size; - this.virtualSize = virtualSize; - this.repoTags = repoTags != null ? ImmutableList.copyOf(repoTags) : ImmutableList. of(); - } + @Nullable public abstract String parent(); - public String getId() { - return id; - } + @Nullable public abstract String created(); - public String getParent() { - return parent; - } + @Nullable public abstract String container(); - public String getCreated() { - return created; - } + @Nullable public abstract String dockerVersion(); - public String getContainer() { - return container; - } + @Nullable public abstract String architecture(); - public String getDockerVersion() { - return dockerVersion; - } + @Nullable public abstract String os(); - public String getArchitecture() { - return architecture; - } + public abstract long size(); - public String getOs() { - return os; - } + @Nullable public abstract long virtualSize(); - public long getSize() { - return size; - } + public abstract List repoTags(); - public long getVirtualSize() { - return virtualSize; - } - - public List getRepoTags() { - return repoTags; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - Image that = (Image) o; - - return Objects.equal(this.id, that.id) && - Objects.equal(this.parent, that.parent) && - Objects.equal(this.created, that.created) && - Objects.equal(this.container, that.container) && - Objects.equal(this.dockerVersion, that.dockerVersion) && - Objects.equal(this.architecture, that.architecture) && - Objects.equal(this.os, that.os) && - Objects.equal(this.size, that.size) && - Objects.equal(this.virtualSize, that.virtualSize); - } - - @Override - public int hashCode() { - return Objects.hashCode(id, parent, created, container, dockerVersion, architecture, os, size, - virtualSize); - } - - @Override - public String toString() { - return Objects.toStringHelper(this) - .add("id", id) - .add("parent", parent) - .add("created", created) - .add("container", container) - .add("dockerVersion", dockerVersion) - .add("architecture", architecture) - .add("os", os) - .add("size", size) - .add("virtualSize", virtualSize) - .add("repoTags", repoTags) - .toString(); - } - - public static Builder builder() { - return new Builder(); - } - - public Builder toBuilder() { - return builder().fromImage(this); - } - - public static final class Builder { - - private String id; - private String parent; - private String created; - private String container; - private String dockerVersion; - private String architecture; - private String os; - private long size; - private long virtualSize; - private List repoTags = ImmutableList.of(); - - public Builder id(String id) { - this.id = id; - return this; - } - - public Builder parent(String parent) { - this.parent = parent; - return this; - } - - public Builder created(String created) { - this.created = created; - return this; - } - - public Builder container(String container) { - this.container = container; - return this; - } - - public Builder dockerVersion(String dockerVersion) { - this.dockerVersion = dockerVersion; - return this; - } - - public Builder architecture(String architecture) { - this.architecture = architecture; - return this; - } - - public Builder os(String os) { - this.os = os; - return this; - } - - public Builder size(long size) { - this.size = size; - return this; - } - - public Builder virtualSize(long virtualSize) { - this.virtualSize = virtualSize; - return this; - } - - public Builder repoTags(List repoTags) { - this.repoTags = ImmutableList.copyOf(checkNotNull(repoTags, "repoTags")); - return this; - } - - public Image build() { - return new Image(id, parent, created, container, dockerVersion, architecture, os, size, - virtualSize, repoTags); - } - - public Builder fromImage(Image in) { - return this - .id(in.getId()) - .parent(in.getParent()) - .created(in.getCreated()) - .container(in.getContainer()) - .dockerVersion(in.getDockerVersion()) - .architecture(in.getArchitecture()) - .os(in.getOs()) - .size(in.getSize()) - .virtualSize(in.getVirtualSize()); - //DO NOT add .repoTags(in.getRepoTags()); - } + @SerializedNames({ "Id", "Parent", "Created", "Container", "DockerVersion", "Architecture", "Os", "Size", + "VirtualSize", "RepoTags", "Architecture" }) + public static Image create(String id, String parent, String created, String container, String dockerVersion, + String architecture, String os, long size, long virtualSize, List repoTags) { + return new AutoValue_Image(id, parent, created, container, dockerVersion, architecture, os, size, virtualSize, + copyOf(repoTags)); } } diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/NetworkSettings.java b/apis/docker/src/main/java/org/jclouds/docker/domain/NetworkSettings.java index 23f3fbede1..f99bd7f3dd 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/NetworkSettings.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/NetworkSettings.java @@ -16,97 +16,36 @@ */ package org.jclouds.docker.domain; -import com.google.common.base.Objects; -import com.google.common.collect.ImmutableMap; -import com.google.gson.annotations.SerializedName; -import org.jclouds.javax.annotation.Nullable; +import static com.google.common.base.Preconditions.checkNotNull; +import static org.jclouds.docker.internal.NullSafeCopies.copyOf; -import java.beans.ConstructorProperties; import java.util.List; import java.util.Map; -import static com.google.common.base.Preconditions.checkNotNull; +import org.jclouds.javax.annotation.Nullable; +import org.jclouds.json.SerializedNames; -public class NetworkSettings { +import com.google.auto.value.AutoValue; +import com.google.common.collect.ImmutableMap; - @SerializedName("IPAddress") - private final String ipAddress; - @SerializedName("IPPrefixLen") - private final int ipPrefixLen; - @SerializedName("Gateway") - private final String gateway; - @SerializedName("Bridge") - private final String bridge; - @SerializedName("PortMapping") - private final String portMapping; - @SerializedName("Ports") - private final Map>> ports; +@AutoValue +public abstract class NetworkSettings { + public abstract String ipAddress(); - @ConstructorProperties({ "IPAddress", "IPPrefixLen", "Gateway", "Bridge", "PortMapping", "Ports" }) - protected NetworkSettings(String ipAddress, int ipPrefixLen, String gateway, String bridge, - @Nullable String portMapping, @Nullable Map>> ports) { - this.ipAddress = checkNotNull(ipAddress, "ipAddress"); - this.ipPrefixLen = checkNotNull(ipPrefixLen, "ipPrefixLen"); - this.gateway = checkNotNull(gateway, "gateway"); - this.bridge = checkNotNull(bridge, "bridge"); - this.portMapping = portMapping; - this.ports = ports != null ? ImmutableMap.copyOf(ports) : ImmutableMap.>> of(); - } + public abstract int ipPrefixLen(); - public String getIpAddress() { - return ipAddress; - } + public abstract String gateway(); - public int getIpPrefixLen() { - return ipPrefixLen; - } + public abstract String bridge(); - public String getGateway() { - return gateway; - } + @Nullable public abstract String portMapping(); - public String getBridge() { - return bridge; - } + public abstract Map>> ports(); - public String getPortMapping() { - return portMapping; - } - - public Map>> getPorts() { - return ports; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - NetworkSettings that = (NetworkSettings) o; - - return Objects.equal(this.ipAddress, that.ipAddress) && - Objects.equal(this.ipPrefixLen, that.ipPrefixLen) && - Objects.equal(this.gateway, that.gateway) && - Objects.equal(this.bridge, that.bridge) && - Objects.equal(this.portMapping, that.portMapping) && - Objects.equal(this.ports, that.ports); - } - - @Override - public int hashCode() { - return Objects.hashCode(ipAddress, ipPrefixLen, gateway, bridge, portMapping, ports); - } - - @Override - public String toString() { - return Objects.toStringHelper(this) - .add("ipAddress", ipAddress) - .add("ipPrefixLen", ipPrefixLen) - .add("gateway", gateway) - .add("bridge", bridge) - .add("portMapping", portMapping) - .add("ports", ports) - .toString(); + @SerializedNames({ "IPAddress", "IPPrefixLen", "Gateway", "Bridge", "PortMapping", "Ports" }) + public static NetworkSettings create(String ipAddress, int ipPrefixLen, String gateway, String bridge, + String portMapping, Map>> ports) { + return new AutoValue_NetworkSettings(ipAddress, ipPrefixLen, gateway, bridge, portMapping, copyOf(ports)); } public static Builder builder() { @@ -157,19 +96,13 @@ public class NetworkSettings { } public NetworkSettings build() { - return new NetworkSettings(ipAddress, ipPrefixLen, gateway, bridge, portMapping, ports); + return NetworkSettings.create(ipAddress, ipPrefixLen, gateway, bridge, portMapping, ports); } public Builder fromNetworkSettings(NetworkSettings in) { - return this - .ipAddress(in.getIpAddress()) - .ipPrefixLen(in.getIpPrefixLen()) - .gateway(in.getGateway()) - .bridge(in.getBridge()) - .portMapping(in.getPortMapping()) - .ports(in.getPorts()); + return this.ipAddress(in.ipAddress()).ipPrefixLen(in.ipPrefixLen()).gateway(in.gateway()).bridge(in.bridge()) + .portMapping(in.portMapping()).ports(in.ports()); } - } } diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/Port.java b/apis/docker/src/main/java/org/jclouds/docker/domain/Port.java index b49dfa6945..0522be185d 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/Port.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/Port.java @@ -16,73 +16,22 @@ */ package org.jclouds.docker.domain; -import com.google.common.base.Objects; -import com.google.gson.annotations.SerializedName; +import org.jclouds.json.SerializedNames; -import java.beans.ConstructorProperties; +import com.google.auto.value.AutoValue; -import static com.google.common.base.Preconditions.checkNotNull; +@AutoValue +public abstract class Port { + public abstract String ip(); -public class Port { + public abstract int privatePort(); - @SerializedName("PrivatePort") - private final int privatePort; - @SerializedName("PublicPort") - private final int publicPort; - @SerializedName("Type") - private final String type; - @SerializedName("IP") - private final String ip; + public abstract int publicPort(); - @ConstructorProperties({ "PrivatePort", "PublicPort", "Type", "IP" }) - protected Port(int privatePort, int publicPort, String type, String ip) { - this.privatePort = checkNotNull(privatePort, "privatePort"); - this.publicPort = checkNotNull(publicPort, "publicPort"); - this.type = checkNotNull(type, "type"); - this.ip = checkNotNull(ip, "ip"); - } + public abstract String type(); - public int getPrivatePort() { - return privatePort; - } - - public int getPublicPort() { - return publicPort; - } - - public String getType() { - return type; - } - - public String getIp() { - return ip; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - Port that = (Port) o; - - return Objects.equal(this.privatePort, that.privatePort) && - Objects.equal(this.publicPort, that.publicPort) && - Objects.equal(this.type, that.type) && - Objects.equal(this.ip, that.ip); - } - - @Override - public int hashCode() { - return Objects.hashCode(privatePort, publicPort, type, ip); - } - - @Override - public String toString() { - return Objects.toStringHelper(this) - .add("privatePort", privatePort) - .add("publicPort", publicPort) - .add("type", type) - .add("ip", ip) - .toString(); + @SerializedNames({ "IP", "PrivatePort", "PublicPort", "Type" }) + public static Port create(String ip, int privatePort, int publicPort, String type) { + return new AutoValue_Port(ip, privatePort, publicPort, type); } } diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/State.java b/apis/docker/src/main/java/org/jclouds/docker/domain/State.java index ac1ba85e01..c0a8586029 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/State.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/State.java @@ -16,151 +16,27 @@ */ package org.jclouds.docker.domain; -import static com.google.common.base.Preconditions.checkNotNull; -import java.beans.ConstructorProperties; +import org.jclouds.json.SerializedNames; -import com.google.common.base.Objects; -import com.google.gson.annotations.SerializedName; +import com.google.auto.value.AutoValue; -public class State { - @SerializedName("Pid") - private final int pid; - @SerializedName("Running") - private final boolean running; - @SerializedName("ExitCode") - private final int exitCode; - @SerializedName("StartedAt") - private final String startedAt; - @SerializedName("FinishedAt") - private final String finishedAt; - @SerializedName("Ghost") - private final boolean ghost; +@AutoValue +public abstract class State { + public abstract int pid(); - @ConstructorProperties({ "Pid", "Running", "ExitCode", "StartedAt", "FinishedAt", "Ghost" }) - protected State(int pid, boolean running, int exitCode, String startedAt, String finishedAt, boolean ghost) { - this.pid = checkNotNull(pid, "pid"); - this.running = checkNotNull(running, "running"); - this.exitCode = checkNotNull(exitCode, "exitCode"); - this.startedAt = checkNotNull(startedAt, "startedAt"); - this.finishedAt = checkNotNull(finishedAt, "finishedAt"); - this.ghost = checkNotNull(ghost, "ghost"); - } + public abstract boolean running(); - public int getPid() { - return pid; - } + public abstract int exitCode(); - public boolean isRunning() { - return running; - } + public abstract String startedAt(); - public int getExitCode() { - return exitCode; - } + public abstract String finishedAt(); - public String getStartedAt() { - return startedAt; - } + public abstract boolean ghost(); - public String getFinishedAt() { - return finishedAt; - } - - public boolean isGhost() { - return ghost; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - State that = (State) o; - - return Objects.equal(this.pid, that.pid) && - Objects.equal(this.running, that.running) && - Objects.equal(this.exitCode, that.exitCode) && - Objects.equal(this.startedAt, that.startedAt) && - Objects.equal(this.finishedAt, that.finishedAt) && - Objects.equal(this.ghost, that.ghost); - } - - @Override - public int hashCode() { - return Objects.hashCode(pid, running, exitCode, startedAt, finishedAt, ghost); - } - - @Override - public String toString() { - return Objects.toStringHelper(this) - .add("pid", pid) - .add("running", running) - .add("exitCode", exitCode) - .add("startedAt", startedAt) - .add("finishedAt", finishedAt) - .add("ghost", ghost) - .toString(); - } - - public static Builder builder() { - return new Builder(); - } - - public Builder toBuilder() { - return builder().fromState(this); - } - - public static final class Builder { - - private int pid; - private boolean running; - private int exitCode; - private String startedAt; - private String finishedAt; - private boolean ghost; - - public Builder pid(int pid) { - this.pid = pid; - return this; - } - - public Builder running(boolean running) { - this.running = running; - return this; - } - - public Builder exitCode(int exitCode) { - this.exitCode = exitCode; - return this; - } - - public Builder startedAt(String startedAt) { - this.startedAt = startedAt; - return this; - } - - public Builder finishedAt(String finishedAt) { - this.finishedAt = finishedAt; - return this; - } - - public Builder ghost(boolean ghost) { - this.ghost = ghost; - return this; - } - - public State build() { - return new State(pid, running, exitCode, startedAt, finishedAt, ghost); - } - - public Builder fromState(State in) { - return this - .pid(in.getPid()) - .running(in.isRunning()) - .exitCode(in.getExitCode()) - .startedAt(in.getStartedAt()) - .finishedAt(in.getFinishedAt()) - .ghost(in.isGhost()); - } + @SerializedNames({ "Pid", "Running", "ExitCode", "StartedAt", "FinishedAt", "Ghost" }) + public static State create(int pid, boolean running, int exitCode, String startedAt, String finishedAt, + boolean ghost) { + return new AutoValue_State(pid, running, exitCode, startedAt, finishedAt, ghost); } } diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/Version.java b/apis/docker/src/main/java/org/jclouds/docker/domain/Version.java index 445a225c3d..ee0ba1972b 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/Version.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/Version.java @@ -16,151 +16,27 @@ */ package org.jclouds.docker.domain; -import static com.google.common.base.Preconditions.checkNotNull; -import java.beans.ConstructorProperties; +import org.jclouds.json.SerializedNames; -import com.google.common.base.Objects; -import com.google.gson.annotations.SerializedName; +import com.google.auto.value.AutoValue; -public class Version { - @SerializedName("Arch") - private final String arch; - @SerializedName("GitCommit") - private final String gitCommit; - @SerializedName("GoVersion") - private final String goVersion; - @SerializedName("KernelVersion") - private final String kernelVersion; - @SerializedName("Os") - private final String os; - @SerializedName("Version") - private final String version; +@AutoValue +public abstract class Version { + public abstract String arch(); - @ConstructorProperties({ "Arch", "GitCommit", "GoVersion", "KernelVersion", "Os", "Version" }) - protected Version(String arch, String gitCommit, String goVersion, String kernelVersion, String os, String version) { - this.arch = checkNotNull(arch, "arch"); - this.gitCommit = checkNotNull(gitCommit, "gitCommit"); - this.goVersion = checkNotNull(goVersion, "goVersion"); - this.kernelVersion = checkNotNull(kernelVersion, "kernelVersion"); - this.os = checkNotNull(os, "os"); - this.version = checkNotNull(version, "version"); - } + public abstract String gitCommit(); - public String getArch() { - return arch; - } + public abstract String goVersion(); - public String getGitCommit() { - return gitCommit; - } + public abstract String kernelVersion(); - public String getGoVersion() { - return goVersion; - } + public abstract String os(); - public String getKernelVersion() { - return kernelVersion; - } + public abstract String version(); - public String getOs() { - return os; - } - - public String getVersion() { - return version; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - Version that = (Version) o; - - return Objects.equal(this.arch, that.arch) && - Objects.equal(this.gitCommit, that.gitCommit) && - Objects.equal(this.goVersion, that.goVersion) && - Objects.equal(this.kernelVersion, that.kernelVersion) && - Objects.equal(this.os, that.os) && - Objects.equal(this.version, that.version); - } - - @Override - public int hashCode() { - return Objects.hashCode(arch, gitCommit, goVersion, kernelVersion, os, version); - } - - @Override - public String toString() { - return Objects.toStringHelper(this) - .add("arch", arch) - .add("gitCommit", gitCommit) - .add("goVersion", goVersion) - .add("kernelVersion", kernelVersion) - .add("os", os) - .add("version", version) - .toString(); - } - - public static Builder builder() { - return new Builder(); - } - - public Builder toBuilder() { - return builder().fromVersion(this); - } - - public static final class Builder { - - private String arch; - private String gitCommit; - private String goVersion; - private String kernelVersion; - private String os; - private String version; - - public Builder arch(String arch) { - this.arch = arch; - return this; - } - - public Builder gitCommit(String gitCommit) { - this.gitCommit = gitCommit; - return this; - } - - public Builder goVersion(String goVersion) { - this.goVersion = goVersion; - return this; - } - - public Builder kernelVersion(String kernelVersion) { - this.kernelVersion = kernelVersion; - return this; - } - - public Builder os(String os) { - this.os = os; - return this; - } - - public Builder version(String version) { - this.version = version; - return this; - } - - public Version build() { - return new Version(arch, gitCommit, goVersion, kernelVersion, os, version); - } - - public Builder fromVersion(Version in) { - return this - .arch(in.getArch()) - .gitCommit(in.getGitCommit()) - .goVersion(in.getGoVersion()) - .kernelVersion(in.getKernelVersion()) - .os(in.getOs()) - .version(in.getVersion()); - } + @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); } } diff --git a/apis/docker/src/main/java/org/jclouds/docker/internal/NullSafeCopies.java b/apis/docker/src/main/java/org/jclouds/docker/internal/NullSafeCopies.java new file mode 100644 index 0000000000..c02d449525 --- /dev/null +++ b/apis/docker/src/main/java/org/jclouds/docker/internal/NullSafeCopies.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.internal; + +import java.util.List; +import java.util.Map; + +import org.jclouds.javax.annotation.Nullable; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; + +public class NullSafeCopies { + + public static Map copyOf(@Nullable Map map) { + return map != null ? ImmutableMap.copyOf(map) : ImmutableMap.of(); + } + + public static List copyOf(@Nullable List list) { + return list != null ? ImmutableList.copyOf(list) : ImmutableList.of(); + } + + private NullSafeCopies() { + } +} diff --git a/apis/docker/src/test/java/org/jclouds/docker/compute/DockerComputeServiceAdapterLiveTest.java b/apis/docker/src/test/java/org/jclouds/docker/compute/DockerComputeServiceAdapterLiveTest.java index faee98267e..b64dace919 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/compute/DockerComputeServiceAdapterLiveTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/compute/DockerComputeServiceAdapterLiveTest.java @@ -61,7 +61,7 @@ public class DockerComputeServiceAdapterLiveTest extends BaseDockerApiLiveTest { .osDescriptionMatches("jclouds/default:latest").build(); guest = adapter.createNodeWithGroupEncodedIntoName(group, name, template); - assertEquals(guest.getNodeId(), guest.getNode().getId() + ""); + assertEquals(guest.getNodeId(), guest.getNode().id() + ""); } public void testListHardwareProfiles() { @@ -76,7 +76,7 @@ public class DockerComputeServiceAdapterLiveTest extends BaseDockerApiLiveTest { @AfterGroups(groups = "live") protected void tearDown() { if (guest != null) { - adapter.destroyNode(guest.getNode().getId() + ""); + adapter.destroyNode(guest.getNode().id() + ""); } super.tearDown(); } diff --git a/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadataTest.java b/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadataTest.java index b062f2fbf9..6e79685e99 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadataTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadataTest.java @@ -21,6 +21,8 @@ import static org.easymock.EasyMock.expect; import static org.easymock.EasyMock.replay; import static org.easymock.EasyMock.verify; import static org.testng.Assert.assertEquals; + +import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Set; @@ -65,7 +67,7 @@ public class ContainerToNodeMetadataTest { public void setup() { Config containerConfig = Config.builder() .hostname("6d35806c1bd2") - .domainName("") + .domainname("") .user("") .memory(0) .memorySwap(0) @@ -79,27 +81,27 @@ public class ContainerToNodeMetadataTest { .stdinOnce(false) .env(null) .cmd(ImmutableList.of("/usr/sbin/sshd", "-D")) - .imageId("jclouds/ubuntu") + .image("jclouds/ubuntu") .volumesFrom("") .workingDir("") .entrypoint(null) .networkDisabled(false) .build(); - State state = State.builder() - .pid(3626) - .running(true) - .exitCode(0) - .startedAt("2014-03-24T20:28:37.537659054Z") - .finishedAt("0001-01-01T00:00:00Z") - .ghost(false) - .build(); + State state = State.create( // + 3626, // pid + true, // running + 0, // exitCode + "2014-03-24T20:28:37.537659054Z", // startedAt + "0001-01-01T00:00:00Z", // finishedAt + false // ghost + ); container = Container.builder() .id("6d35806c1bd2b25cd92bba2d2c2c5169dc2156f53ab45c2b62d76e2d2fee14a9") .name("/hopeful_mclean") .created("2014-03-22T07:16:45.784120972Z") .path("/usr/sbin/sshd") - .args(new String[] {"-D"}) - .containerConfig(containerConfig) + .args(Arrays.asList("-D")) + .config(containerConfig) .state(state) .image("af0f59f1c19eef9471c3b8c8d587c39b8f130560b54f3766931b37d76d5de4b6") .networkSettings(NetworkSettings.builder() @@ -190,12 +192,12 @@ public class ContainerToNodeMetadataTest { private Container mockContainer() { Container mockContainer = EasyMock.createMock(Container.class); - expect(mockContainer.getId()).andReturn(container.getId()); - expect(mockContainer.getName()).andReturn(container.getName()); - expect(mockContainer.getContainerConfig()).andReturn(container.getContainerConfig()).anyTimes(); - expect(mockContainer.getNetworkSettings()).andReturn(container.getNetworkSettings()).anyTimes(); - expect(mockContainer.getState()).andReturn(container.getState()); - expect(mockContainer.getImage()).andReturn(container.getImage()).anyTimes(); + expect(mockContainer.id()).andReturn(container.id()); + expect(mockContainer.name()).andReturn(container.name()); + expect(mockContainer.config()).andReturn(container.config()).anyTimes(); + expect(mockContainer.networkSettings()).andReturn(container.networkSettings()).anyTimes(); + expect(mockContainer.state()).andReturn(container.state()); + expect(mockContainer.image()).andReturn(container.image()).anyTimes(); replay(mockContainer); return mockContainer; diff --git a/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ImageToImageTest.java b/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ImageToImageTest.java index 0141c70d58..0d357929e8 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ImageToImageTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ImageToImageTest.java @@ -39,14 +39,18 @@ public class ImageToImageTest { @BeforeMethod public void setup() { - image = org.jclouds.docker.domain.Image.builder() - .id("id") - .parent("parent") - .created("created") - .architecture("x86_64") - .repoTags(ImmutableList.of("repoTag1:version")) - .size(0l) - .build(); + image = org.jclouds.docker.domain.Image.create( + "id", // id + "parent", // parent + "created", // created + null, // container + null, // dockerVersion + "x86_64", // architecture + null, // os + 0l, // size + 0l, // virtualSize + ImmutableList.of("repoTag1:version") // repoTags + ); function = new ImageToImage(); } @@ -57,15 +61,15 @@ public class ImageToImageTest { verify(mockImage); - assertEquals(mockImage.getId(), image.getId().toString()); + assertEquals(mockImage.id(), image.getId().toString()); } private org.jclouds.docker.domain.Image mockImage() { org.jclouds.docker.domain.Image mockImage = EasyMock.createMock(org.jclouds.docker.domain.Image.class); - expect(mockImage.getId()).andReturn(image.getId()).anyTimes(); - expect(mockImage.getRepoTags()).andReturn(image.getRepoTags()).anyTimes(); - expect(mockImage.getArchitecture()).andReturn(image.getArchitecture()).anyTimes(); + expect(mockImage.id()).andReturn(image.id()).anyTimes(); + expect(mockImage.repoTags()).andReturn(image.repoTags()).anyTimes(); + expect(mockImage.architecture()).andReturn(image.architecture()).anyTimes(); replay(mockImage); return mockImage; diff --git a/apis/docker/src/test/java/org/jclouds/docker/compute/functions/StateToStatusTest.java b/apis/docker/src/test/java/org/jclouds/docker/compute/functions/StateToStatusTest.java index b9739abc6e..f66f040480 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/compute/functions/StateToStatusTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/compute/functions/StateToStatusTest.java @@ -46,7 +46,7 @@ public class StateToStatusTest { verify(mockState); - assertEquals(mockState.isRunning(), true); + assertEquals(mockState.running(), true); assertEquals(status, NodeMetadata.Status.RUNNING); } @@ -57,14 +57,14 @@ public class StateToStatusTest { verify(mockState); - assertEquals(mockState.isRunning(), false); + assertEquals(mockState.running(), false); assertEquals(status, NodeMetadata.Status.TERMINATED); } private State mockStateRunning() { State mockState = EasyMock.createMock(State.class); - expect(mockState.isRunning()).andReturn(true).anyTimes(); + expect(mockState.running()).andReturn(true).anyTimes(); replay(mockState); return mockState; @@ -73,7 +73,7 @@ public class StateToStatusTest { private State mockStateNotRunning() { State mockState = EasyMock.createMock(State.class); - expect(mockState.isRunning()).andReturn(false).anyTimes(); + expect(mockState.running()).andReturn(false).anyTimes(); replay(mockState); return mockState; diff --git a/apis/docker/src/test/java/org/jclouds/docker/config/DockerParserModuleTest.java b/apis/docker/src/test/java/org/jclouds/docker/config/DockerParserModuleTest.java index 4f5ba7586e..57659c5970 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/config/DockerParserModuleTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/config/DockerParserModuleTest.java @@ -16,37 +16,46 @@ */ package org.jclouds.docker.config; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import org.jclouds.docker.domain.Container; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; - -import static org.jclouds.docker.config.DockerParserModule.ContainerTypeAdapter; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNotNull; +import org.jclouds.docker.domain.Container; +import org.jclouds.docker.domain.NetworkSettings; +import org.jclouds.docker.domain.Port; +import org.jclouds.json.Json; +import org.jclouds.json.config.GsonModule; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableMap; +import com.google.inject.Guice; + /** * Unit tests for the {@link org.jclouds.docker.config.DockerParserModule} class. */ @Test(groups = "unit", testName = "DockerParserModuleTest") public class DockerParserModuleTest { - private Gson gson; + private Json json = Guice.createInjector(new GsonModule(), new DockerParserModule()).getInstance(Json.class); - @BeforeMethod - public void setup() { - gson = new GsonBuilder() - .registerTypeAdapter(Container.class, new ContainerTypeAdapter()) - .create(); - } - - @Test public void testContainerWithVolumesNull() { - Container container = gson.fromJson( - "{ \"Volumes\": null }", Container.class); + Container container = json.fromJson("{ \"Id\": \"foo\", \"Volumes\": null }", Container.class); assertNotNull(container); - assertEquals(container.getVolumes(), null); + assertEquals(container.id(), "foo"); + assertEquals(container.volumes(), ImmutableMap.of()); } + public void port() { + // Note IP, not Ip + String text = "{\"IP\":\"0.0.0.0\",\"PrivatePort\":4567,\"PublicPort\":49155,\"Type\":\"tcp\"}"; + Port port = Port.create("0.0.0.0", 4567, 49155, "tcp"); + assertEquals(json.fromJson(text, Port.class), port); + assertEquals(json.toJson(port), text); + } + + public void networkSettings() { + String text = "{\"IPAddress\":\"XX.XX.206.98\",\"IPPrefixLen\":27,\"Gateway\":\"XX.XX.206.105\",\"Bridge\":\"public\",\"Ports\":{}}"; + NetworkSettings settings = NetworkSettings.create("XX.XX.206.98", 27, "XX.XX.206.105", "public", null, null); + assertEquals(json.fromJson(text, NetworkSettings.class), settings); + assertEquals(json.toJson(settings), text); + } } diff --git a/apis/docker/src/test/java/org/jclouds/docker/features/RemoteApiLiveTest.java b/apis/docker/src/test/java/org/jclouds/docker/features/RemoteApiLiveTest.java index 34fedddd65..30fd171f2f 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/features/RemoteApiLiveTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/features/RemoteApiLiveTest.java @@ -50,7 +50,7 @@ public class RemoteApiLiveTest extends BaseDockerApiLiveTest { @Test public void testVersion() { - assertEquals(api().getVersion().getVersion(), "1.0.0"); + assertEquals(api().getVersion().version(), "1.0.0"); } @Test(dependsOnMethods = "testVersion") @@ -69,37 +69,37 @@ public class RemoteApiLiveTest extends BaseDockerApiLiveTest { @Test(dependsOnMethods = "testListImages") public void testCreateContainer() throws IOException, InterruptedException { - Config containerConfig = Config.builder().imageId(image.getId()) + 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.getId()); + assertNotNull(container.id()); } @Test(dependsOnMethods = "testCreateContainer") public void testStartContainer() throws IOException, InterruptedException { - api().startContainer(container.getId()); - assertTrue(api().inspectContainer(container.getId()).getState().isRunning()); + api().startContainer(container.id()); + assertTrue(api().inspectContainer(container.id()).state().running()); } @Test(dependsOnMethods = "testStartContainer") public void testStopContainer() { - api().stopContainer(container.getId()); - assertFalse(api().inspectContainer(container.getId()).getState().isRunning()); + api().stopContainer(container.id()); + assertFalse(api().inspectContainer(container.id()).state().running()); } @Test(dependsOnMethods = "testStopContainer", expectedExceptions = NullPointerException.class) public void testRemoveContainer() { - api().removeContainer(container.getId()); - assertFalse(api().inspectContainer(container.getId()).getState().isRunning()); + api().removeContainer(container.id()); + assertFalse(api().inspectContainer(container.id()).state().running()); } @Test(dependsOnMethods = "testRemoveContainer", expectedExceptions = ResourceNotFoundException.class) public void testDeleteImage() { - InputStream deleteImageStream = api().deleteImage(image.getId()); + InputStream deleteImageStream = api().deleteImage(image.id()); consumeStream(deleteImageStream, false); - assertNull(api().inspectImage(image.getId())); + assertNull(api().inspectImage(image.id())); } public void testBuildImage() throws IOException, InterruptedException, URISyntaxException { @@ -111,7 +111,7 @@ public class RemoteApiLiveTest extends BaseDockerApiLiveTest { String rawImageId = Iterables.getLast(Splitter.on("Successfully built ").split(lastStreamedLine)); String imageId = rawImageId.substring(0, 11); Image image = api().inspectImage(imageId); - api().deleteImage(image.getId(), DeleteImageOptions.Builder.force(true)); + api().deleteImage(image.id(), DeleteImageOptions.Builder.force(true)); } private RemoteApi api() { diff --git a/apis/docker/src/test/java/org/jclouds/docker/features/RemoteApiMockTest.java b/apis/docker/src/test/java/org/jclouds/docker/features/RemoteApiMockTest.java index 760f7234b3..515d98ddb1 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/features/RemoteApiMockTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/features/RemoteApiMockTest.java @@ -108,11 +108,11 @@ public class RemoteApiMockTest extends BaseDockerMockTest { Container container = remoteApi.inspectContainer(containerId); assertRequestHasCommonFields(server.takeRequest(), "/containers/" + containerId + "/json"); assertNotNull(container); - assertNotNull(container.getId(), containerId); - assertNotNull(container.getContainerConfig()); - assertNotNull(container.getHostConfig()); - assertEquals(container.getName(), "/tender_lumiere"); - assertEquals(container.getState().isRunning(), true); + assertNotNull(container.id(), containerId); + assertNotNull(container.config()); + assertNotNull(container.hostConfig()); + assertEquals(container.name(), "/tender_lumiere"); + assertEquals(container.state().running(), true); } finally { api.close(); server.shutdown(); @@ -126,7 +126,7 @@ public class RemoteApiMockTest extends BaseDockerMockTest { RemoteApi remoteApi = api.getRemoteApi(); String containerId = "notExisting"; try { - Container container = remoteApi.inspectContainer(containerId); + remoteApi.inspectContainer(containerId); assertRequestHasCommonFields(server.takeRequest(), "/containers/" + containerId + "/json"); } finally { api.close(); @@ -145,13 +145,13 @@ public class RemoteApiMockTest extends BaseDockerMockTest { .attachStderr(true) .attachStdout(true) .tty(false) - .imageId("base") + .image("base") .build(); try { Container container = remoteApi.createContainer("test", containerConfig); assertRequestHasCommonFields(server.takeRequest(), "POST", "/containers/create?name=test"); assertNotNull(container); - assertEquals(container.getId(), "c6c74153ae4b1d1633d68890a68d89c40aa5e284a1ea016cbc6ef0e634ee37b2"); + assertEquals(container.id(), "c6c74153ae4b1d1633d68890a68d89c40aa5e284a1ea016cbc6ef0e634ee37b2"); } finally { api.close(); server.shutdown(); From ef59a121bfff42402d17e2978d20630c0e8aac02 Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Tue, 28 Oct 2014 16:51:19 -0700 Subject: [PATCH 18/81] Remove tar creation responsibility from docker, avoiding filesystem bias. --- apis/docker/pom.xml | 12 +- .../binders/BindInputStreamToRequest.java | 68 ----------- .../jclouds/docker/features/RemoteApi.java | 16 --- .../docker/features/internal/Archives.java | 60 ---------- .../binders/BindInputStreamToRequestTest.java | 67 ----------- .../docker/compute/BaseDockerApiLiveTest.java | 52 ++++---- .../docker/features/RemoteApiLiveTest.java | 30 +++-- .../docker/features/RemoteApiMockTest.java | 39 ------ .../features/internal/ArchivesTest.java | 112 ------------------ 9 files changed, 43 insertions(+), 413 deletions(-) delete mode 100644 apis/docker/src/main/java/org/jclouds/docker/binders/BindInputStreamToRequest.java delete mode 100644 apis/docker/src/main/java/org/jclouds/docker/features/internal/Archives.java delete mode 100644 apis/docker/src/test/java/org/jclouds/docker/binders/BindInputStreamToRequestTest.java delete mode 100644 apis/docker/src/test/java/org/jclouds/docker/features/internal/ArchivesTest.java diff --git a/apis/docker/pom.xml b/apis/docker/pom.xml index ae9c675c84..cc6716c05c 100644 --- a/apis/docker/pom.xml +++ b/apis/docker/pom.xml @@ -58,11 +58,6 @@ jclouds-compute ${project.version} - - org.apache.commons - commons-compress - 1.5 - com.google.auto.value auto-value @@ -112,6 +107,13 @@ + + org.jboss.shrinkwrap + shrinkwrap-depchain + 1.2.2 + pom + test + diff --git a/apis/docker/src/main/java/org/jclouds/docker/binders/BindInputStreamToRequest.java b/apis/docker/src/main/java/org/jclouds/docker/binders/BindInputStreamToRequest.java deleted file mode 100644 index 855a2e5d41..0000000000 --- a/apis/docker/src/main/java/org/jclouds/docker/binders/BindInputStreamToRequest.java +++ /dev/null @@ -1,68 +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.binders; - -import com.google.common.base.Throwables; -import com.google.common.io.Files; -import org.jclouds.compute.reference.ComputeServiceConstants; -import org.jclouds.docker.features.internal.Archives; -import org.jclouds.http.HttpRequest; -import org.jclouds.io.Payload; -import org.jclouds.io.Payloads; -import org.jclouds.logging.Logger; -import org.jclouds.rest.Binder; - -import javax.annotation.Resource; -import javax.inject.Named; -import javax.inject.Singleton; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; - -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; - -@Singleton -public class BindInputStreamToRequest implements Binder { - - @Resource - @Named(ComputeServiceConstants.COMPUTE_LOGGER) - protected Logger logger = Logger.NULL; - - @Override - public R bindToRequest(R request, Object input) { - checkArgument(checkNotNull(input, "input") instanceof File, "this binder is only valid for File!"); - checkNotNull(request, "request"); - - File dockerFile = (File) input; - File tmpDir = Files.createTempDir(); - final File targetFile = new File(tmpDir + File.separator + "Dockerfile"); - try { - Files.copy(dockerFile, targetFile); - File archive = Archives.tar(tmpDir, File.createTempFile("archive", ".tar")); - FileInputStream data = new FileInputStream(archive); - Payload payload = Payloads.newInputStreamPayload(data); - payload.getContentMetadata().setContentLength(data.getChannel().size()); - payload.getContentMetadata().setContentType("application/tar"); - request.setPayload(payload); - } catch (IOException e) { - logger.error(e, "Couldn't create a tarball for %s", targetFile); - throw Throwables.propagate(e); - } - return request; - } -} diff --git a/apis/docker/src/main/java/org/jclouds/docker/features/RemoteApi.java b/apis/docker/src/main/java/org/jclouds/docker/features/RemoteApi.java index 96b0228f94..785eb20b59 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/features/RemoteApi.java +++ b/apis/docker/src/main/java/org/jclouds/docker/features/RemoteApi.java @@ -17,7 +17,6 @@ package org.jclouds.docker.features; import java.io.Closeable; -import java.io.File; import java.io.InputStream; import java.util.Set; @@ -32,7 +31,6 @@ import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; import org.jclouds.Fallbacks; -import org.jclouds.docker.binders.BindInputStreamToRequest; import org.jclouds.docker.domain.Config; import org.jclouds.docker.domain.Container; import org.jclouds.docker.domain.HostConfig; @@ -255,18 +253,4 @@ public interface RemoteApi extends Closeable { @Path("/build") @Headers(keys = "Content-Type", values = "application/tar") InputStream build(Payload inputStream, BuildOptions options); - - /** - * Build an image from Dockerfile via stdin - * - * @param dockerFile The file to be compressed with one of the following algorithms: identity, gzip, bzip2, xz.* - * @param options the image build's options (@see BuildOptions) - * @return a stream of the build execution - */ - @Named("image:build") - @POST - @Path("/build") - @Headers(keys = "Content-Type", values = "application/tar") - InputStream build(@BinderParam(BindInputStreamToRequest.class) File dockerFile, BuildOptions options); - } diff --git a/apis/docker/src/main/java/org/jclouds/docker/features/internal/Archives.java b/apis/docker/src/main/java/org/jclouds/docker/features/internal/Archives.java deleted file mode 100644 index 43b69c3e30..0000000000 --- a/apis/docker/src/main/java/org/jclouds/docker/features/internal/Archives.java +++ /dev/null @@ -1,60 +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.internal; - -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.collect.Iterables.getLast; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; - -import org.apache.commons.compress.archivers.tar.TarArchiveEntry; -import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; - -import com.google.common.base.Splitter; -import com.google.common.io.Files; - -public class Archives { - - public static File tar(File baseDir, String archivePath) throws IOException { - return tar(baseDir, new File(archivePath)); - } - - public static File tar(File baseDir, File tarFile) throws IOException { - // Check that the directory is a directory, and get its contents - checkArgument(baseDir.isDirectory(), "%s is not a directory", baseDir); - File[] files = baseDir.listFiles(); - String token = getLast(Splitter.on("/").split(baseDir.getAbsolutePath())); - TarArchiveOutputStream tos = new TarArchiveOutputStream(new FileOutputStream(tarFile)); - tos.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU); - try { - for (File file : files) { - TarArchiveEntry tarEntry = new TarArchiveEntry(file); - tarEntry.setName("/" + getLast(Splitter.on(token).split(file.toString()))); - tos.putArchiveEntry(tarEntry); - if (!file.isDirectory()) { - Files.asByteSource(file).copyTo(tos); - } - tos.closeArchiveEntry(); - } - } finally { - tos.close(); - } - return tarFile; - } - -} diff --git a/apis/docker/src/test/java/org/jclouds/docker/binders/BindInputStreamToRequestTest.java b/apis/docker/src/test/java/org/jclouds/docker/binders/BindInputStreamToRequestTest.java deleted file mode 100644 index 1aa7611e5e..0000000000 --- a/apis/docker/src/test/java/org/jclouds/docker/binders/BindInputStreamToRequestTest.java +++ /dev/null @@ -1,67 +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.binders; - -import com.google.common.base.Charsets; -import com.google.common.io.CharStreams; -import org.jclouds.http.HttpRequest; -import org.testng.annotations.Test; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStreamReader; - -import static org.testng.Assert.assertEquals; -import static org.testng.AssertJUnit.assertTrue; - -@Test(groups = "unit", testName = "BindInputStreamToRequestTest") -public class BindInputStreamToRequestTest { - - @Test - public void testBindInputStreamToRequest() throws IOException { - BindInputStreamToRequest binder = new BindInputStreamToRequest(); - - HttpRequest request = HttpRequest.builder().method("GET").endpoint("http://test").build(); - request = binder.bindToRequest(request, File.createTempFile("dockerfile", "")); - String rawContent = CharStreams.toString(new InputStreamReader((FileInputStream) request.getPayload().getRawContent(), Charsets.UTF_8)); - assertTrue(rawContent.startsWith("Dockerfile")); - assertEquals(request.getPayload().getContentMetadata().getContentType(), "application/tar"); - } - - @Test(expectedExceptions = IllegalArgumentException.class) - public void testBindInputStreamToRequestWithObjectAsInput() throws IOException { - BindInputStreamToRequest binder = new BindInputStreamToRequest(); - - HttpRequest request = HttpRequest.builder().method("GET").endpoint("http://test").build(); - request = binder.bindToRequest(request, new Object()); - String rawContent = CharStreams.toString(new InputStreamReader((FileInputStream) request.getPayload().getRawContent(), Charsets.UTF_8)); - assertTrue(rawContent.startsWith("Dockerfile")); - assertEquals(request.getPayload().getContentMetadata().getContentType(), "application/tar"); - } - - @Test(expectedExceptions = NullPointerException.class) - public void testBindInputStreamToRequestWithNullInput() throws IOException { - BindInputStreamToRequest binder = new BindInputStreamToRequest(); - - HttpRequest request = HttpRequest.builder().method("GET").endpoint("http://test").build(); - request = binder.bindToRequest(request, null); - String rawContent = CharStreams.toString(new InputStreamReader((FileInputStream) request.getPayload().getRawContent(), Charsets.UTF_8)); - assertTrue(rawContent.startsWith("Dockerfile")); - assertEquals(request.getPayload().getContentMetadata().getContentType(), "application/tar"); - } -} diff --git a/apis/docker/src/test/java/org/jclouds/docker/compute/BaseDockerApiLiveTest.java b/apis/docker/src/test/java/org/jclouds/docker/compute/BaseDockerApiLiveTest.java index 77115f248e..c266060fa6 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/compute/BaseDockerApiLiveTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/compute/BaseDockerApiLiveTest.java @@ -16,30 +16,30 @@ */ package org.jclouds.docker.compute; -import com.google.common.base.Charsets; -import com.google.common.collect.ImmutableSet; -import com.google.common.io.CharStreams; -import com.google.common.io.Closeables; -import com.google.common.io.Files; -import com.google.common.io.Resources; -import com.google.inject.Module; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.Properties; + +import org.jboss.shrinkwrap.api.GenericArchive; +import org.jboss.shrinkwrap.api.ShrinkWrap; +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.docker.DockerApi; -import org.jclouds.docker.features.internal.Archives; import org.jclouds.io.Payload; import org.jclouds.io.Payloads; import org.jclouds.sshj.config.SshjSshClientModule; import org.testng.Assert; import org.testng.annotations.Test; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.net.URL; -import java.util.Properties; +import com.google.common.base.Charsets; +import com.google.common.collect.ImmutableSet; +import com.google.common.io.CharStreams; +import com.google.common.io.Closeables; +import com.google.inject.Module; @Test(groups = "live") public class BaseDockerApiLiveTest extends BaseApiLiveTest { @@ -72,20 +72,12 @@ public class BaseDockerApiLiveTest extends BaseApiLiveTest { return result; } - protected Payload createPayload() throws IOException { - String folderPath = System.getProperty("user.dir") + "/docker/src/test/resources"; - File parentDir = new File(folderPath + "/archive"); - parentDir.mkdirs(); - URL url = Resources.getResource("Dockerfile"); - String content = Resources.toString(url, Charsets.UTF_8); - final File dockerfile = new File(parentDir.getAbsolutePath() + File.separator + "Dockerfile"); - Files.write(content.getBytes(), dockerfile); - File archive = Archives.tar(parentDir, folderPath + "/archive/archive.tar"); - FileInputStream data = new FileInputStream(archive); - Payload payload = Payloads.newInputStreamPayload(data); - payload.getContentMetadata().setContentLength(data.getChannel().size()); - payload.getContentMetadata().setContentType("application/tar"); - return payload; - } + public static Payload tarredDockerfile() throws IOException { + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + ShrinkWrap.create(GenericArchive.class, "archive.tar") + .add(new ClassLoaderAsset("Dockerfile"), "Dockerfile") + .as(TarExporter.class).exportTo(bytes); + return Payloads.newByteArrayPayload(bytes.toByteArray()); + } } diff --git a/apis/docker/src/test/java/org/jclouds/docker/features/RemoteApiLiveTest.java b/apis/docker/src/test/java/org/jclouds/docker/features/RemoteApiLiveTest.java index 30fd171f2f..a489ac37fb 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/features/RemoteApiLiveTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/features/RemoteApiLiveTest.java @@ -16,10 +16,16 @@ */ package org.jclouds.docker.features; -import com.google.common.base.Splitter; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Iterables; -import com.google.common.io.Resources; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertNull; +import static org.testng.Assert.assertTrue; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URISyntaxException; + import org.jclouds.docker.compute.BaseDockerApiLiveTest; import org.jclouds.docker.domain.Config; import org.jclouds.docker.domain.Container; @@ -30,16 +36,9 @@ import org.jclouds.docker.options.DeleteImageOptions; import org.jclouds.rest.ResourceNotFoundException; import org.testng.annotations.Test; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.net.URISyntaxException; - -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertNull; -import static org.testng.Assert.assertTrue; +import com.google.common.base.Splitter; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; @Test(groups = "live", testName = "RemoteApiLiveTest", singleThreaded = true) public class RemoteApiLiveTest extends BaseDockerApiLiveTest { @@ -104,7 +103,7 @@ public class RemoteApiLiveTest extends BaseDockerApiLiveTest { public void testBuildImage() throws IOException, InterruptedException, URISyntaxException { BuildOptions options = BuildOptions.Builder.tag("testBuildImage").verbose(false).nocache(false); - InputStream buildImageStream = api().build(new File(Resources.getResource("Dockerfile").toURI()), options); + InputStream buildImageStream = api().build(tarredDockerfile(), options); String buildStream = consumeStream(buildImageStream, false); Iterable splitted = Splitter.on("\n").split(buildStream.replace("\r", "").trim()); String lastStreamedLine = Iterables.getLast(splitted).trim(); @@ -117,5 +116,4 @@ public class RemoteApiLiveTest extends BaseDockerApiLiveTest { private RemoteApi api() { return api.getRemoteApi(); } - } diff --git a/apis/docker/src/test/java/org/jclouds/docker/features/RemoteApiMockTest.java b/apis/docker/src/test/java/org/jclouds/docker/features/RemoteApiMockTest.java index 515d98ddb1..b164c7b834 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/features/RemoteApiMockTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/features/RemoteApiMockTest.java @@ -315,22 +315,6 @@ public class RemoteApiMockTest extends BaseDockerMockTest { } } - public void testBuildContainer() throws Exception { - MockWebServer server = mockWebServer(); - server.enqueue(new MockResponse().setResponseCode(200)); - DockerApi api = api(server.getUrl("/")); - RemoteApi remoteApi = api.getRemoteApi(); - File dockerFile = File.createTempFile("docker", "tmp"); - try { - remoteApi.build(dockerFile, BuildOptions.NONE); - assertRequestHasCommonFields(server.takeRequest(), "POST", "/build"); - } finally { - dockerFile.delete(); - api.close(); - server.shutdown(); - } - } - public void testBuildContainerUsingPayload() throws Exception { MockWebServer server = mockWebServer(); server.enqueue(new MockResponse().setResponseCode(200)); @@ -350,27 +334,4 @@ public class RemoteApiMockTest extends BaseDockerMockTest { server.shutdown(); } } - - public void testBuildNonexistentContainer() throws Exception { - MockWebServer server = mockWebServer(); - server.enqueue(new MockResponse().setResponseCode(404)); - - DockerApi api = api(server.getUrl("/")); - RemoteApi remoteApi = api.getRemoteApi(); - - File dockerFile = File.createTempFile("docker", "tmp"); - try { - try { - remoteApi.build(dockerFile, BuildOptions.NONE); - fail("Build container must fail on 404"); - } catch (ResourceNotFoundException ex) { - // Expected exception - } - } finally { - dockerFile.delete(); - api.close(); - server.shutdown(); - } - } - } diff --git a/apis/docker/src/test/java/org/jclouds/docker/features/internal/ArchivesTest.java b/apis/docker/src/test/java/org/jclouds/docker/features/internal/ArchivesTest.java deleted file mode 100644 index 15eb3ffe69..0000000000 --- a/apis/docker/src/test/java/org/jclouds/docker/features/internal/ArchivesTest.java +++ /dev/null @@ -1,112 +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.internal; - -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.collect.Iterables.getOnlyElement; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertTrue; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.List; - -import javax.annotation.Resource; -import javax.inject.Named; - -import org.apache.commons.compress.archivers.ArchiveStreamFactory; -import org.apache.commons.compress.archivers.tar.TarArchiveEntry; -import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; -import org.apache.commons.compress.archivers.tar.TarUtils; -import org.jclouds.compute.reference.ComputeServiceConstants; -import org.jclouds.logging.Logger; -import org.testng.Assert; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; - -import com.beust.jcommander.internal.Lists; -import com.google.common.io.ByteStreams; -import com.google.common.io.Files; - -@Test(groups = "unit", testName = "ArchivesTest") -public class ArchivesTest { - - @Resource - @Named(ComputeServiceConstants.COMPUTE_LOGGER) - protected Logger logger = Logger.NULL; - - private File tmpDir; - private File outputDir; - private long checkSum; - - @BeforeClass - private void init() throws IOException { - tmpDir = Files.createTempDir(); - outputDir = Files.createTempDir(); - File sampleFile = writeSampleFile("test", "this is a test to tar a hierarchy of folders and files\n"); - checkSum = TarUtils.computeCheckSum(Files.asByteSource(sampleFile).read()); - } - - public void testTarSingleFile() throws Exception { - File archive = Archives.tar(tmpDir, new File(outputDir + File.separator + "test.tar.gz")); - List untarredFiles = unTar(archive, outputDir); - File untarredSampleFile = getOnlyElement(untarredFiles, null); - assertNotNull(untarredSampleFile); - assertTrue(checkSum == TarUtils.computeCheckSum(Files.asByteSource(untarredSampleFile).read())); - } - - private List unTar(final File inputFile, final File outputDir) throws Exception { - final List untarredFiles = Lists.newArrayList(); - final InputStream is = new FileInputStream(inputFile); - final TarArchiveInputStream tarArchiveInputStream = (TarArchiveInputStream) - new ArchiveStreamFactory().createArchiveInputStream("tar", is); - TarArchiveEntry entry; - while ((entry = (TarArchiveEntry) tarArchiveInputStream.getNextEntry()) != null) { - final File outputFile = new File(outputDir, entry.getName()); - if (entry.isDirectory()) { - if (!outputFile.exists()) { - if (!outputFile.mkdirs()) { - throw new IllegalStateException(String.format("Couldn't create directory %s.", outputFile.getAbsolutePath())); - } - } - } else { - OutputStream outputFileStream = new FileOutputStream(outputFile); - ByteStreams.copy(tarArchiveInputStream, outputFileStream); - outputFileStream.close(); - } - untarredFiles.add(outputFile); - } - tarArchiveInputStream.close(); - return untarredFiles; - } - - private File writeSampleFile(String fileName, final String contents) { - checkNotNull(fileName, "Provided file name for writing must NOT be null."); - checkNotNull(contents, "Unable to write null contents."); - File sampleFile = new File(tmpDir + File.separator + fileName); - try { - Files.write(contents.getBytes(), sampleFile); - } catch (IOException e) { - logger.error("ERROR trying to write to file '" + fileName + "' - " + e.toString()); - Assert.fail(); - } - return sampleFile; - } -} From c77d59470b24a5046ca5dc1f3b142fda4fdcb93d Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Mon, 27 Oct 2014 09:04:50 -0700 Subject: [PATCH 19/81] JCLOUDS-534 Avoid runtime incompatibility introduced by Guava's closeQuietly. --- .../jclouds/docker/handlers/DockerErrorHandler.java | 10 +++------- .../jclouds/docker/compute/BaseDockerApiLiveTest.java | 11 +++++++++-- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/apis/docker/src/main/java/org/jclouds/docker/handlers/DockerErrorHandler.java b/apis/docker/src/main/java/org/jclouds/docker/handlers/DockerErrorHandler.java index 72d3069fba..a31901469e 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/handlers/DockerErrorHandler.java +++ b/apis/docker/src/main/java/org/jclouds/docker/handlers/DockerErrorHandler.java @@ -16,6 +16,8 @@ */ package org.jclouds.docker.handlers; +import static org.jclouds.util.Closeables2.closeQuietly; + import java.io.IOException; import javax.annotation.Resource; @@ -30,7 +32,6 @@ import org.jclouds.rest.ResourceNotFoundException; import org.jclouds.util.Strings2; import com.google.common.base.Throwables; -import com.google.common.io.Closeables; /** * This will parse and set an appropriate exception on the command object. @@ -80,12 +81,7 @@ public class DockerErrorHandler implements HttpErrorHandler { break; } } finally { - try { - Closeables.close(response.getPayload(), true); - } catch (IOException e) { - // This code will never be reached - throw Throwables.propagate(e); - } + closeQuietly(response.getPayload()); command.setException(exception); } } diff --git a/apis/docker/src/test/java/org/jclouds/docker/compute/BaseDockerApiLiveTest.java b/apis/docker/src/test/java/org/jclouds/docker/compute/BaseDockerApiLiveTest.java index c266060fa6..ce64e01380 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/compute/BaseDockerApiLiveTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/compute/BaseDockerApiLiveTest.java @@ -38,7 +38,6 @@ import org.testng.annotations.Test; import com.google.common.base.Charsets; import com.google.common.collect.ImmutableSet; import com.google.common.io.CharStreams; -import com.google.common.io.Closeables; import com.google.inject.Module; @Test(groups = "live") @@ -65,9 +64,17 @@ public class BaseDockerApiLiveTest extends BaseApiLiveTest { String result = null; try { result = CharStreams.toString(new InputStreamReader(stream, Charsets.UTF_8)); - Closeables.close(stream, swallowIOException); } catch (IOException e) { Assert.fail(); + } finally { + // TODO: remove swallowIOException over-optimization and just use Closeables2.closeQuietly + try { + stream.close(); + } catch (IOException e) { + if (!swallowIOException) { + throw new RuntimeException(e); + } + } } return result; } From 42d881f78a702e08ef9e8b13929e7424c6ea33bd Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Tue, 28 Oct 2014 08:55:04 -0700 Subject: [PATCH 20/81] Remove source of service loader typos with AutoService. --- apis/docker/pom.xml | 1 - .../src/main/java/org/jclouds/docker/DockerApiMetadata.java | 5 ++--- .../resources/META-INF/services/org.jclouds.apis.ApiMetadata | 1 - 3 files changed, 2 insertions(+), 5 deletions(-) delete mode 100644 apis/docker/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata diff --git a/apis/docker/pom.xml b/apis/docker/pom.xml index cc6716c05c..0469d60ef0 100644 --- a/apis/docker/pom.xml +++ b/apis/docker/pom.xml @@ -61,7 +61,6 @@ com.google.auto.value auto-value - 1.0-rc2 provided diff --git a/apis/docker/src/main/java/org/jclouds/docker/DockerApiMetadata.java b/apis/docker/src/main/java/org/jclouds/docker/DockerApiMetadata.java index b281eb7566..a533614a42 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/DockerApiMetadata.java +++ b/apis/docker/src/main/java/org/jclouds/docker/DockerApiMetadata.java @@ -16,6 +16,7 @@ */ package org.jclouds.docker; +import com.google.auto.service.AutoService; import com.google.common.collect.ImmutableSet; import com.google.inject.Module; import org.jclouds.Constants; @@ -33,9 +34,7 @@ import java.util.Properties; import static org.jclouds.compute.config.ComputeServiceProperties.TEMPLATE; import static org.jclouds.reflect.Reflection2.typeToken; -/** - * Implementation of {@link BaseHttpApiMetadata} for the Docker API - */ +@AutoService(ApiMetadata.class) public class DockerApiMetadata extends BaseHttpApiMetadata { @Override diff --git a/apis/docker/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata b/apis/docker/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata deleted file mode 100644 index ca1a6cb4dc..0000000000 --- a/apis/docker/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata +++ /dev/null @@ -1 +0,0 @@ -org.jclouds.docker.DockerApiMetadata \ No newline at end of file From 8b5fcf069b382080fbf81c3cee93e01b90ff7755 Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Wed, 29 Oct 2014 12:52:13 -0700 Subject: [PATCH 21/81] Decomplicate consuming the output of docker commands in tests. --- .../docker/compute/BaseDockerApiLiveTest.java | 22 ++++++------------- .../docker/features/RemoteApiLiveTest.java | 10 +++------ 2 files changed, 10 insertions(+), 22 deletions(-) diff --git a/apis/docker/src/test/java/org/jclouds/docker/compute/BaseDockerApiLiveTest.java b/apis/docker/src/test/java/org/jclouds/docker/compute/BaseDockerApiLiveTest.java index ce64e01380..54f61a151f 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/compute/BaseDockerApiLiveTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/compute/BaseDockerApiLiveTest.java @@ -16,6 +16,9 @@ */ package org.jclouds.docker.compute; +import static com.google.common.base.Charsets.UTF_8; +import static org.jclouds.util.Closeables2.closeQuietly; + import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; @@ -32,10 +35,8 @@ import org.jclouds.docker.DockerApi; import org.jclouds.io.Payload; import org.jclouds.io.Payloads; import org.jclouds.sshj.config.SshjSshClientModule; -import org.testng.Assert; import org.testng.annotations.Test; -import com.google.common.base.Charsets; import com.google.common.collect.ImmutableSet; import com.google.common.io.CharStreams; import com.google.inject.Module; @@ -60,23 +61,14 @@ public class BaseDockerApiLiveTest extends BaseApiLiveTest { return overrides; } - protected String consumeStream(InputStream stream, boolean swallowIOException) { - String result = null; + protected String consumeStream(InputStream stream) { try { - result = CharStreams.toString(new InputStreamReader(stream, Charsets.UTF_8)); + return CharStreams.toString(new InputStreamReader(stream, UTF_8)); } catch (IOException e) { - Assert.fail(); + throw new AssertionError(e); } finally { - // TODO: remove swallowIOException over-optimization and just use Closeables2.closeQuietly - try { - stream.close(); - } catch (IOException e) { - if (!swallowIOException) { - throw new RuntimeException(e); - } - } + closeQuietly(stream); } - return result; } public static Payload tarredDockerfile() throws IOException { diff --git a/apis/docker/src/test/java/org/jclouds/docker/features/RemoteApiLiveTest.java b/apis/docker/src/test/java/org/jclouds/docker/features/RemoteApiLiveTest.java index a489ac37fb..3f2321217f 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/features/RemoteApiLiveTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/features/RemoteApiLiveTest.java @@ -23,7 +23,6 @@ import static org.testng.Assert.assertNull; import static org.testng.Assert.assertTrue; import java.io.IOException; -import java.io.InputStream; import java.net.URISyntaxException; import org.jclouds.docker.compute.BaseDockerApiLiveTest; @@ -55,8 +54,7 @@ public class RemoteApiLiveTest extends BaseDockerApiLiveTest { @Test(dependsOnMethods = "testVersion") public void testCreateImage() throws IOException, InterruptedException { CreateImageOptions options = CreateImageOptions.Builder.fromImage(BUSYBOX_IMAGE); - InputStream createImageStream = api().createImage(options); - consumeStream(createImageStream, false); + consumeStream(api().createImage(options)); image = api().inspectImage(BUSYBOX_IMAGE); assertNotNull(image); } @@ -96,15 +94,13 @@ public class RemoteApiLiveTest extends BaseDockerApiLiveTest { @Test(dependsOnMethods = "testRemoveContainer", expectedExceptions = ResourceNotFoundException.class) public void testDeleteImage() { - InputStream deleteImageStream = api().deleteImage(image.id()); - consumeStream(deleteImageStream, false); + consumeStream(api().deleteImage(image.id())); assertNull(api().inspectImage(image.id())); } public void testBuildImage() throws IOException, InterruptedException, URISyntaxException { BuildOptions options = BuildOptions.Builder.tag("testBuildImage").verbose(false).nocache(false); - InputStream buildImageStream = api().build(tarredDockerfile(), options); - String buildStream = consumeStream(buildImageStream, false); + String buildStream = consumeStream(api().build(tarredDockerfile(), options)); Iterable splitted = Splitter.on("\n").split(buildStream.replace("\r", "").trim()); String lastStreamedLine = Iterables.getLast(splitted).trim(); String rawImageId = Iterables.getLast(Splitter.on("Successfully built ").split(lastStreamedLine)); From b04fd71998b4ad735b1da207bda924f1117e5d8e Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Sat, 1 Nov 2014 11:06:22 -0700 Subject: [PATCH 22/81] JCLOUDS-750 FieldNamingStrategy is no longer required. --- .../docker/config/DockerParserModule.java | 23 ------------------- 1 file changed, 23 deletions(-) diff --git a/apis/docker/src/main/java/org/jclouds/docker/config/DockerParserModule.java b/apis/docker/src/main/java/org/jclouds/docker/config/DockerParserModule.java index ea777a4192..70c76cbda0 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/config/DockerParserModule.java +++ b/apis/docker/src/main/java/org/jclouds/docker/config/DockerParserModule.java @@ -16,35 +16,12 @@ */ package org.jclouds.docker.config; -import java.lang.reflect.Field; - import org.jclouds.json.config.GsonModule; -import com.google.gson.FieldNamingPolicy; -import com.google.gson.FieldNamingStrategy; import com.google.inject.AbstractModule; public class DockerParserModule extends AbstractModule { @Override protected void configure() { - bind(FieldNamingStrategy.class).toInstance(FIELD_NAMING_STRATEGY); bind(GsonModule.DateAdapter.class).to(GsonModule.Iso8601DateAdapter.class); } - - /** When serializing, Most fields are UpperCamelCase, with some exceptions. */ - private static final FieldNamingStrategy FIELD_NAMING_STRATEGY = new FieldNamingStrategy() { - private final FieldNamingStrategy delegate = FieldNamingPolicy.UPPER_CAMEL_CASE; - - @Override public String translateName(Field f) { - String result = delegate.translateName(f); - // IP not Ip as code wins over docs https://github.com/docker/docker/blob/master/daemon/network_settings.go - if (result.equals("IpAddress")) { - return "IPAddress"; - } else if (result.equals("IpPrefixLen")) { - return "IPPrefixLen"; - } else if (result.equals("Ip")) { - return "IP"; - } - return result; - } - }; } From 6219b77dbcd7bbb2ffdc39c72477ea65d4079282 Mon Sep 17 00:00:00 2001 From: Andrea Turli Date: Thu, 2 Oct 2014 18:08:24 +0200 Subject: [PATCH 23/81] JCLOUDS-737 update docker to support v1.3 --- apis/docker/README.md | 10 +- apis/docker/pom.xml | 1 + .../java/org/jclouds/docker/DockerApi.java | 20 +- .../org/jclouds/docker/DockerApiMetadata.java | 12 +- .../functions/ContainerToNodeMetadata.java | 1 - .../compute/functions/ImageToImage.java | 2 + .../options/DockerTemplateOptions.java | 2 +- .../strategy/DockerComputeServiceAdapter.java | 44 ++-- .../docker/config/DockerHttpApiModule.java | 23 ++ .../org/jclouds/docker/domain/Config.java | 6 + .../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 +++++ .../org/jclouds/docker/domain/Version.java | 11 +- .../{RemoteApi.java => ContainerApi.java} | 113 +-------- .../org/jclouds/docker/features/ImageApi.java | 110 +++++++++ .../org/jclouds/docker/features/MiscApi.java | 72 ++++++ .../docker/suppliers/KeyStoreSupplier.java | 130 ++++++++++ .../suppliers/SSLContextWithKeysSupplier.java | 77 ++++++ .../docker/compute/BaseDockerApiLiveTest.java | 2 + .../DockerComputeServiceAdapterLiveTest.java | 38 ++- .../compute/DockerComputeServiceLiveTest.java | 226 +++++++++++------ .../ContainerToNodeMetadataTest.java | 6 + .../compute/functions/ImageToImageTest.java | 1 + ...iveTest.java => ContainerApiLiveTest.java} | 78 +++--- ...ockTest.java => ContainerApiMockTest.java} | 229 ++++++------------ .../docker/features/ImageApiLiveTest.java | 59 +++++ .../docker/features/ImageApiMockTest.java | 97 ++++++++ .../docker/features/MiscApiLiveTest.java | 86 +++++++ .../docker/features/MiscApiMockTest.java | 159 ++++++++++++ .../docker/internal/BaseDockerMockTest.java | 4 +- apis/docker/src/test/resources/Dockerfile | 17 +- .../src/test/resources/SimpleDockerfile | 18 ++ apis/docker/src/test/resources/container.json | 204 ++++++++++------ apis/docker/src/test/resources/info.json | 28 +++ apis/docker/src/test/resources/version.json | 9 + 37 files changed, 1496 insertions(+), 524 deletions(-) create mode 100644 apis/docker/src/main/java/org/jclouds/docker/domain/ContainerSummary.java create mode 100644 apis/docker/src/main/java/org/jclouds/docker/domain/Info.java rename apis/docker/src/main/java/org/jclouds/docker/features/{RemoteApi.java => ContainerApi.java} (59%) create mode 100644 apis/docker/src/main/java/org/jclouds/docker/features/ImageApi.java create mode 100644 apis/docker/src/main/java/org/jclouds/docker/features/MiscApi.java create mode 100644 apis/docker/src/main/java/org/jclouds/docker/suppliers/KeyStoreSupplier.java create mode 100644 apis/docker/src/main/java/org/jclouds/docker/suppliers/SSLContextWithKeysSupplier.java rename apis/docker/src/test/java/org/jclouds/docker/features/{RemoteApiLiveTest.java => ContainerApiLiveTest.java} (55%) rename apis/docker/src/test/java/org/jclouds/docker/features/{RemoteApiMockTest.java => ContainerApiMockTest.java} (54%) create mode 100644 apis/docker/src/test/java/org/jclouds/docker/features/ImageApiLiveTest.java create mode 100644 apis/docker/src/test/java/org/jclouds/docker/features/ImageApiMockTest.java create mode 100644 apis/docker/src/test/java/org/jclouds/docker/features/MiscApiLiveTest.java create mode 100644 apis/docker/src/test/java/org/jclouds/docker/features/MiscApiMockTest.java create mode 100644 apis/docker/src/test/resources/SimpleDockerfile create mode 100644 apis/docker/src/test/resources/info.json create mode 100644 apis/docker/src/test/resources/version.json diff --git a/apis/docker/README.md b/apis/docker/README.md index 1c4394ac10..9b55cbcea4 100644 --- a/apis/docker/README.md +++ b/apis/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` diff --git a/apis/docker/pom.xml b/apis/docker/pom.xml index 0469d60ef0..b268a83da5 100644 --- a/apis/docker/pom.xml +++ b/apis/docker/pom.xml @@ -131,6 +131,7 @@ test + 1 ${test.docker.endpoint} ${test.docker.api-version} diff --git a/apis/docker/src/main/java/org/jclouds/docker/DockerApi.java b/apis/docker/src/main/java/org/jclouds/docker/DockerApi.java index 54bf5c9a06..502f5774fa 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/DockerApi.java +++ b/apis/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 - */ public interface DockerApi extends Closeable { - /** - * Provides synchronous access to Docker Remote API features. - */ @Delegate - RemoteApi getRemoteApi(); + MiscApi getMiscApi(); + + @Delegate + ContainerApi getContainerApi(); + + @Delegate + ImageApi getImageApi(); } diff --git a/apis/docker/src/main/java/org/jclouds/docker/DockerApiMetadata.java b/apis/docker/src/main/java/org/jclouds/docker/DockerApiMetadata.java index a533614a42..d5c470f415 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/DockerApiMetadata.java +++ b/apis/docker/src/main/java/org/jclouds/docker/DockerApiMetadata.java @@ -56,7 +56,7 @@ public class DockerApiMetadata extends BaseHttpApiMetadata { 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 { 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.>of( - DockerHttpApiModule.class, DockerParserModule.class, + DockerHttpApiModule.class, DockerComputeServiceContextModule.class)); } diff --git a/apis/docker/src/main/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadata.java b/apis/docker/src/main/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadata.java index 7b070fbfbe..20df6a3499 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadata.java +++ b/apis/docker/src/main/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadata.java @@ -100,7 +100,6 @@ public class ContainerToNodeMetadata implements Function> 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.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 listImages() { Set 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 listNodes() { Set 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 listNodesByIds(final Iterable ids) { Set 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 diff --git a/apis/docker/src/main/java/org/jclouds/docker/config/DockerHttpApiModule.java b/apis/docker/src/main/java/org/jclouds/docker/config/DockerHttpApiModule.java index e6da554704..ced396c8a8 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/config/DockerHttpApiModule.java +++ b/apis/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 { 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>() { + }).to(new TypeLiteral() { + }); + bind(new TypeLiteral>() { + }).to(new TypeLiteral() { + }); + } } diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/Config.java b/apis/docker/src/main/java/org/jclouds/docker/domain/Config.java index 396ab2f3fa..bef89692f6 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/Config.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/Config.java @@ -123,6 +123,7 @@ public abstract class Config { private List entrypoint = ImmutableList.of(); private boolean networkDisabled; private List onBuild = ImmutableList.of(); + private Map 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 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, diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/ContainerSummary.java b/apis/docker/src/main/java/org/jclouds/docker/domain/ContainerSummary.java new file mode 100644 index 0000000000..17d006427d --- /dev/null +++ b/apis/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 names(); + + public abstract String created(); + + public abstract String image(); + + public abstract String command(); + + public abstract List ports(); + + public abstract String status(); + + @SerializedNames({"Id", "Names", "Created", "Image", "Command", "Ports", "Status"}) + public static ContainerSummary create(String id, List names, String created, String image, String command, List ports, String status) { + return new AutoValue_ContainerSummary(id, copyOf(names), created, image, command, copyOf(ports), status); + } + +} diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/HostConfig.java b/apis/docker/src/main/java/org/jclouds/docker/domain/HostConfig.java index 757157c457..630be7c4ab 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/HostConfig.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/HostConfig.java @@ -35,7 +35,7 @@ public abstract class HostConfig { public abstract List binds(); - public abstract Map lxcConf(); + public abstract List> 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 binds, Map lxcConf, + public static HostConfig create(String containerIDFile, List binds, List> lxcConf, boolean privileged, String dns, String dnsSearch, Map>> portBindings, List links, boolean publishAllPorts, List 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 binds = Lists.newArrayList(); - private Map lxcConf = Maps.newLinkedHashMap(); + private List> 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 lxcConf) { - this.lxcConf.putAll(checkNotNull(lxcConf, "lxcConf")); + public Builder lxcConf(List> lxcConf) { + this.lxcConf.addAll(checkNotNull(lxcConf, "lxcConf")); return this; } diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/Image.java b/apis/docker/src/main/java/org/jclouds/docker/domain/Image.java index 99b25943e9..c80e44860c 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/Image.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/Image.java @@ -48,10 +48,11 @@ public abstract class Image { public abstract List 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 repoTags) { return new AutoValue_Image(id, parent, created, container, dockerVersion, architecture, os, size, virtualSize, copyOf(repoTags)); } + } diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/Info.java b/apis/docker/src/main/java/org/jclouds/docker/domain/Info.java new file mode 100644 index 0000000000..596c0a51f2 --- /dev/null +++ b/apis/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); + } +} diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/Version.java b/apis/docker/src/main/java/org/jclouds/docker/domain/Version.java index ee0ba1972b..7cf7960067 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/Version.java +++ b/apis/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); } } diff --git a/apis/docker/src/main/java/org/jclouds/docker/features/RemoteApi.java b/apis/docker/src/main/java/org/jclouds/docker/features/ContainerApi.java similarity index 59% rename from apis/docker/src/main/java/org/jclouds/docker/features/RemoteApi.java rename to apis/docker/src/main/java/org/jclouds/docker/features/ContainerApi.java index 785eb20b59..60f7749564 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/features/RemoteApi.java +++ b/apis/docker/src/main/java/org/jclouds/docker/features/ContainerApi.java @@ -16,9 +16,7 @@ */ package org.jclouds.docker.features; -import java.io.Closeable; -import java.io.InputStream; -import java.util.Set; +import java.util.List; import javax.inject.Named; import javax.ws.rs.Consumes; @@ -33,34 +31,18 @@ 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.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(); +public interface ContainerApi { /** * List all running containers @@ -70,8 +52,8 @@ public interface RemoteApi extends Closeable { @Named("containers:list") @GET @Path("/containers/json") - @Fallback(Fallbacks.EmptySetOnNotFoundOr404.class) - Set listContainers(); + @Fallback(Fallbacks.EmptyListOnNotFoundOr404.class) + List listContainers(); /** * List all running containers @@ -82,8 +64,8 @@ public interface RemoteApi extends Closeable { @Named("containers:list") @GET @Path("/containers/json") - @Fallback(Fallbacks.EmptySetOnNotFoundOr404.class) - Set listContainers(ListContainerOptions options); + @Fallback(Fallbacks.EmptyListOnNotFoundOr404.class) + List listContainers(ListContainerOptions options); /** * Create a container @@ -172,85 +154,4 @@ public interface RemoteApi extends Closeable { @Path("/commit") Image commit(CommitOptions options); - /** - * List images - * - * @return the images available. - */ - @Named("images:list") - @GET - @Path("/images/json") - @Fallback(Fallbacks.EmptySetOnNotFoundOr404.class) - Set 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 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); } diff --git a/apis/docker/src/main/java/org/jclouds/docker/features/ImageApi.java b/apis/docker/src/main/java/org/jclouds/docker/features/ImageApi.java new file mode 100644 index 0000000000..95c963c88d --- /dev/null +++ b/apis/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 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 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); + +} diff --git a/apis/docker/src/main/java/org/jclouds/docker/features/MiscApi.java b/apis/docker/src/main/java/org/jclouds/docker/features/MiscApi.java new file mode 100644 index 0000000000..30cd1f31a9 --- /dev/null +++ b/apis/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); + +} diff --git a/apis/docker/src/main/java/org/jclouds/docker/suppliers/KeyStoreSupplier.java b/apis/docker/src/main/java/org/jclouds/docker/suppliers/KeyStoreSupplier.java new file mode 100644 index 0000000000..e8643cd9aa --- /dev/null +++ b/apis/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 { + private final Crypto crypto; + private final Supplier creds; + + @Inject + KeyStoreSupplier(Crypto crypto, @Provider Supplier 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 certs = (Collection) 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); + } + } +} diff --git a/apis/docker/src/main/java/org/jclouds/docker/suppliers/SSLContextWithKeysSupplier.java b/apis/docker/src/main/java/org/jclouds/docker/suppliers/SSLContextWithKeysSupplier.java new file mode 100644 index 0000000000..59695d34bd --- /dev/null +++ b/apis/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 { + private final Supplier keyStore; + private final TrustManager[] trustManager; + private final Supplier creds; + + @Inject + SSLContextWithKeysSupplier(Supplier keyStore, @Provider Supplier 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); + } + } +} diff --git a/apis/docker/src/test/java/org/jclouds/docker/compute/BaseDockerApiLiveTest.java b/apis/docker/src/test/java/org/jclouds/docker/compute/BaseDockerApiLiveTest.java index 54f61a151f..9aca266574 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/compute/BaseDockerApiLiveTest.java +++ b/apis/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 { 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; } diff --git a/apis/docker/src/test/java/org/jclouds/docker/compute/DockerComputeServiceAdapterLiveTest.java b/apis/docker/src/test/java/org/jclouds/docker/compute/DockerComputeServiceAdapterLiveTest.java index b64dace919..c3da551d91 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/compute/DockerComputeServiceAdapterLiveTest.java +++ b/apis/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 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 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(); } diff --git a/apis/docker/src/test/java/org/jclouds/docker/compute/DockerComputeServiceLiveTest.java b/apis/docker/src/test/java/org/jclouds/docker/compute/DockerComputeServiceLiveTest.java index cc460c3c4b..1e024e4cba 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/compute/DockerComputeServiceLiveTest.java +++ b/apis/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 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 + public void setupContext() { + super.setupContext(); + buildSocketTester(); + } + @Override protected void initializeContext() { super.initializeContext(); - Optional optionalImage = Iterables.tryFind(client.listImages(), new Predicate() { - @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); + client = view.getComputeService(); + + 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 + protected void tearDownContext() { + super.tearDownContext(); + if (defaultImage != null) { + imageApi().deleteImage(SSHABLE_IMAGE + ":" + SSHABLE_IMAGE_TAG, DeleteImageOptions.Builder.force(true)); } } - @Override + private ImageApi imageApi() { + return client.getContext().unwrapApi(DockerApi.class).getImageApi(); + } + protected Template buildTemplate(TemplateBuilder templateBuilder) { - return templateBuilder.imageId(defaultImage.getId()).build(); + + 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; } - @Override - public void testOptionToNotBlock() throws Exception { - // Docker ComputeService implementation has to block until the node - // is provisioned, to be able to return it. + protected void createAndRunAServiceInGroup(String group) throws RunNodesException { + // note that some cloud providers do not support mixed case tag names + ImmutableMap userMetadata = ImmutableMap.of("test", group); + ImmutableSet 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); } - @Override - protected void checkTagsInNodeEquals(NodeMetadata node, ImmutableSet tags) { - // Docker does not support tags + 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>> 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 - protected void checkUserMetadataContains(NodeMetadata node, ImmutableMap userMetadata) { - // Docker does not support user metadata + static class ServiceStats { + long backgroundProcessMilliseconds; + long socketOpenMilliseconds; + + @Override + public String toString() { + return format("[backgroundProcessMilliseconds=%s, socketOpenMilliseconds=%s]", + backgroundProcessMilliseconds, socketOpenMilliseconds); + } } - @Override - public void testCreateAndRunAService() throws Exception { - // Docker does not support blockOnPort + protected String exec(final String nodeId, String command) { + return exec(nodeId, Statements.exec(command)); } - @Override - @Test(enabled = true, dependsOnMethods = { "testCompareSizes" }) - public void testAScriptExecutionAfterBootWithBasicTemplate() throws Exception { - super.testAScriptExecutionAfterBootWithBasicTemplate(); + protected String exec(final String nodeId, Statement command) { + return client.runScriptOnNode(nodeId, command, runAsRoot(false).wrapInInitScript(false)).getOutput().trim(); } - @Override - @Test(enabled = true, dependsOnMethods = "testReboot", expectedExceptions = UnsupportedOperationException.class) - public void testSuspendResume() throws Exception { - super.testSuspendResume(); - } - - @Override - @Test(enabled = true, dependsOnMethods = "testSuspendResume") - public void testGetNodesWithDetails() throws Exception { - super.testGetNodesWithDetails(); - } - - @Override - @Test(enabled = true, dependsOnMethods = "testSuspendResume") - public void testListNodes() throws Exception { - super.testListNodes(); - } - - @Override - @Test(enabled = true, dependsOnMethods = "testSuspendResume") - public void testListNodesByIds() throws Exception { - super.testListNodesByIds(); - } - - @Override - @Test(enabled = true, dependsOnMethods = { "testListNodes", "testGetNodesWithDetails", "testListNodesByIds" }) - public void testDestroyNodes() { - super.testDestroyNodes(); - } - - @Test(enabled = true, expectedExceptions = NullPointerException.class) - public void testCorrectExceptionRunningNodesNotFound() throws Exception { - super.testCorrectExceptionRunningNodesNotFound(); - } - - @Test(enabled = true, expectedExceptions = NullPointerException.class) - public void testCorrectAuthException() throws Exception { - // Docker does not support authentication yet - super.testCorrectAuthException(); + 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); } } diff --git a/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadataTest.java b/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadataTest.java index 6e79685e99..94ee205fb1 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadataTest.java +++ b/apis/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); } diff --git a/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ImageToImageTest.java b/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ImageToImageTest.java index 0d357929e8..76d82ee753 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ImageToImageTest.java +++ b/apis/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; diff --git a/apis/docker/src/test/java/org/jclouds/docker/features/RemoteApiLiveTest.java b/apis/docker/src/test/java/org/jclouds/docker/features/ContainerApiLiveTest.java similarity index 55% rename from apis/docker/src/test/java/org/jclouds/docker/features/RemoteApiLiveTest.java rename to apis/docker/src/test/java/org/jclouds/docker/features/ContainerApiLiveTest.java index 3f2321217f..44edc573b9 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/features/RemoteApiLiveTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/features/ContainerApiLiveTest.java @@ -16,55 +16,52 @@ */ package org.jclouds.docker.features; -import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertNull; import static org.testng.Assert.assertTrue; import java.io.IOException; -import java.net.URISyntaxException; +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.BuildOptions; import org.jclouds.docker.options.CreateImageOptions; -import org.jclouds.docker.options.DeleteImageOptions; -import org.jclouds.rest.ResourceNotFoundException; +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.base.Splitter; import com.google.common.collect.ImmutableList; -import com.google.common.collect.Iterables; @Test(groups = "live", testName = "RemoteApiLiveTest", singleThreaded = true) -public class RemoteApiLiveTest extends BaseDockerApiLiveTest { +public class ContainerApiLiveTest extends BaseDockerApiLiveTest { - private static final String BUSYBOX_IMAGE = "busybox"; private Container container = null; - private Image image = null; + protected static final String BUSYBOX_IMAGE_TAG = "busybox:ubuntu-12.04"; + protected Image image = null; - @Test - public void testVersion() { - assertEquals(api().getVersion().version(), "1.0.0"); - } - - @Test(dependsOnMethods = "testVersion") - public void testCreateImage() throws IOException, InterruptedException { - CreateImageOptions options = CreateImageOptions.Builder.fromImage(BUSYBOX_IMAGE); - consumeStream(api().createImage(options)); - image = api().inspectImage(BUSYBOX_IMAGE); + @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); } - @Test(dependsOnMethods = "testCreateImage") - public void testListImages() { - assertNotNull(api().listImages()); + @AfterClass + protected void tearDown() { + if (image != null) { + api.getImageApi().deleteImage(BUSYBOX_IMAGE_TAG); + } } - @Test(dependsOnMethods = "testListImages") 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")) @@ -86,30 +83,23 @@ public class RemoteApiLiveTest extends BaseDockerApiLiveTest { assertFalse(api().inspectContainer(container.id()).state().running()); } + @Test + public void testListContainers() { + List 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()); } - @Test(dependsOnMethods = "testRemoveContainer", expectedExceptions = ResourceNotFoundException.class) - public void testDeleteImage() { - consumeStream(api().deleteImage(image.id())); - assertNull(api().inspectImage(image.id())); - } - - public void testBuildImage() throws IOException, InterruptedException, URISyntaxException { - BuildOptions options = BuildOptions.Builder.tag("testBuildImage").verbose(false).nocache(false); - String buildStream = consumeStream(api().build(tarredDockerfile(), options)); - Iterable splitted = Splitter.on("\n").split(buildStream.replace("\r", "").trim()); - String lastStreamedLine = Iterables.getLast(splitted).trim(); - String rawImageId = Iterables.getLast(Splitter.on("Successfully built ").split(lastStreamedLine)); - String imageId = rawImageId.substring(0, 11); - Image image = api().inspectImage(imageId); - api().deleteImage(image.id(), DeleteImageOptions.Builder.force(true)); - } - - private RemoteApi api() { - return api.getRemoteApi(); + private ContainerApi api() { + return api.getContainerApi(); } } diff --git a/apis/docker/src/test/java/org/jclouds/docker/features/RemoteApiMockTest.java b/apis/docker/src/test/java/org/jclouds/docker/features/ContainerApiMockTest.java similarity index 54% rename from apis/docker/src/test/java/org/jclouds/docker/features/RemoteApiMockTest.java rename to apis/docker/src/test/java/org/jclouds/docker/features/ContainerApiMockTest.java index b164c7b834..4bbff66fb6 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/features/RemoteApiMockTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/features/ContainerApiMockTest.java @@ -16,50 +16,45 @@ */ package org.jclouds.docker.features; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMultimap; -import com.squareup.okhttp.mockwebserver.MockResponse; -import com.squareup.okhttp.mockwebserver.MockWebServer; -import org.jclouds.docker.DockerApi; -import org.jclouds.docker.domain.Config; -import org.jclouds.docker.domain.Container; -import org.jclouds.docker.internal.BaseDockerMockTest; -import org.jclouds.docker.options.BuildOptions; -import org.jclouds.docker.options.CreateImageOptions; -import org.jclouds.docker.options.ListContainerOptions; -import org.jclouds.io.Payload; -import org.jclouds.io.Payloads; -import org.jclouds.rest.ResourceNotFoundException; -import org.testng.annotations.Test; - -import java.io.File; -import java.io.FileInputStream; -import java.util.Set; - import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; +import java.util.List; + +import org.jclouds.docker.DockerApi; +import org.jclouds.docker.domain.Config; +import org.jclouds.docker.domain.Container; +import org.jclouds.docker.domain.ContainerSummary; +import org.jclouds.docker.internal.BaseDockerMockTest; +import org.jclouds.docker.options.ListContainerOptions; +import org.jclouds.rest.ResourceNotFoundException; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMultimap; +import com.squareup.okhttp.mockwebserver.MockResponse; +import com.squareup.okhttp.mockwebserver.MockWebServer; /** - * Mock tests for the {@link org.jclouds.docker.DockerApi} class. + * Mock tests for the {@link org.jclouds.docker.features.ContainerApi} class. */ -@Test(groups = "unit", testName = "RemoteApiMockTest") -public class RemoteApiMockTest extends BaseDockerMockTest { +@Test(groups = "unit", testName = "ContainerApiMockTest") +public class ContainerApiMockTest extends BaseDockerMockTest { public void testListContainers() throws Exception { MockWebServer server = mockWebServer(); server.enqueue(new MockResponse().setBody(payloadFromResource("/containers.json"))); - DockerApi api = api(server.getUrl("/")); - RemoteApi remoteApi = api.getRemoteApi(); + DockerApi dockerApi = api(server.getUrl("/")); + ContainerApi api = dockerApi.getContainerApi(); try { - Set containers = remoteApi.listContainers(); + List containerSummaries = api.listContainers(); assertRequestHasCommonFields(server.takeRequest(), "/containers/json"); - assertEquals(containers.size(), 1); + assertEquals(containerSummaries.size(), 1); } finally { - api.close(); + dockerApi.close(); server.shutdown(); } } @@ -67,16 +62,14 @@ public class RemoteApiMockTest extends BaseDockerMockTest { public void testListNonexistentContainers() throws Exception { MockWebServer server = mockWebServer(); server.enqueue(new MockResponse().setResponseCode(404)); - - DockerApi api = api(server.getUrl("/")); - RemoteApi remoteApi = api.getRemoteApi(); - + DockerApi dockerApi = api(server.getUrl("/")); + ContainerApi api = dockerApi.getContainerApi(); try { - Set containers = remoteApi.listContainers(); + List containerSummaries = api.listContainers(); assertRequestHasCommonFields(server.takeRequest(), "/containers/json"); - assertTrue(containers.isEmpty()); + assertTrue(containerSummaries.isEmpty()); } finally { - api.close(); + dockerApi.close(); server.shutdown(); } } @@ -86,14 +79,14 @@ public class RemoteApiMockTest extends BaseDockerMockTest { public void testListAllContainers() throws Exception { MockWebServer server = mockWebServer(); server.enqueue(new MockResponse().setBody(payloadFromResource("/containers.json"))); - DockerApi api = api(server.getUrl("/")); - RemoteApi remoteApi = api.getRemoteApi(); + DockerApi dockerApi = api(server.getUrl("/")); + ContainerApi api = dockerApi.getContainerApi(); try { - Set containers = remoteApi.listContainers(ListContainerOptions.Builder.all(true)); + List containerSummaries = api.listContainers(ListContainerOptions.Builder.all(true)); assertRequestHasParameters(server.takeRequest(), "/containers/json", ImmutableMultimap.of("all", "true")); - assertEquals(containers.size(), 1); + assertEquals(containerSummaries.size(), 1); } finally { - api.close(); + dockerApi.close(); server.shutdown(); } } @@ -101,20 +94,20 @@ public class RemoteApiMockTest extends BaseDockerMockTest { public void testGetContainer() throws Exception { MockWebServer server = mockWebServer(); server.enqueue(new MockResponse().setBody(payloadFromResource("/container.json"))); - DockerApi api = api(server.getUrl("/")); - RemoteApi remoteApi = api.getRemoteApi(); + DockerApi dockerApi = api(server.getUrl("/")); + ContainerApi api = dockerApi.getContainerApi(); String containerId = "b03d4cd15b76f8876110615cdeed15eadf77c9beb408d62f1687dcc69192cd6d"; try { - Container container = remoteApi.inspectContainer(containerId); + Container container = api.inspectContainer(containerId); assertRequestHasCommonFields(server.takeRequest(), "/containers/" + containerId + "/json"); assertNotNull(container); assertNotNull(container.id(), containerId); assertNotNull(container.config()); assertNotNull(container.hostConfig()); - assertEquals(container.name(), "/tender_lumiere"); + assertTrue(container.name().contains("/weave")); assertEquals(container.state().running(), true); } finally { - api.close(); + dockerApi.close(); server.shutdown(); } } @@ -122,14 +115,14 @@ public class RemoteApiMockTest extends BaseDockerMockTest { public void testGetNonExistingContainer() throws Exception { MockWebServer server = mockWebServer(); server.enqueue(new MockResponse().setResponseCode(404)); - DockerApi api = api(server.getUrl("/")); - RemoteApi remoteApi = api.getRemoteApi(); + DockerApi dockerApi = api(server.getUrl("/")); + ContainerApi api = dockerApi.getContainerApi(); String containerId = "notExisting"; try { - remoteApi.inspectContainer(containerId); + Container container = api.inspectContainer(containerId); assertRequestHasCommonFields(server.takeRequest(), "/containers/" + containerId + "/json"); } finally { - api.close(); + dockerApi.close(); server.shutdown(); } } @@ -137,9 +130,8 @@ public class RemoteApiMockTest extends BaseDockerMockTest { public void testCreateContainer() throws Exception { MockWebServer server = mockWebServer(); server.enqueue(new MockResponse().setBody(payloadFromResource("/container-creation.json"))); - - DockerApi api = api(server.getUrl("/")); - RemoteApi remoteApi = api.getRemoteApi(); + DockerApi dockerApi = api(server.getUrl("/")); + ContainerApi api = dockerApi.getContainerApi(); Config containerConfig = Config.builder().cmd(ImmutableList.of("date")) .attachStdin(false) .attachStderr(true) @@ -148,12 +140,12 @@ public class RemoteApiMockTest extends BaseDockerMockTest { .image("base") .build(); try { - Container container = remoteApi.createContainer("test", containerConfig); + Container container = api.createContainer("test", containerConfig); assertRequestHasCommonFields(server.takeRequest(), "POST", "/containers/create?name=test"); assertNotNull(container); assertEquals(container.id(), "c6c74153ae4b1d1633d68890a68d89c40aa5e284a1ea016cbc6ef0e634ee37b2"); } finally { - api.close(); + dockerApi.close(); server.shutdown(); } } @@ -161,16 +153,15 @@ public class RemoteApiMockTest extends BaseDockerMockTest { public void testRemoveContainer() throws Exception { MockWebServer server = mockWebServer(); server.enqueue(new MockResponse().setResponseCode(204)); - - DockerApi api = api(server.getUrl("/")); - RemoteApi remoteApi = api.getRemoteApi(); + DockerApi dockerApi = api(server.getUrl("/")); + ContainerApi api = dockerApi.getContainerApi(); String containerId = "6d35806c1bd2b25cd92bba2d2c2c5169dc2156f53ab45c2b62d76e2d2fee14a9"; try { - remoteApi.removeContainer(containerId); + api.removeContainer(containerId); assertRequestHasCommonFields(server.takeRequest(), "DELETE", "/containers/" + containerId); } finally { - api.close(); + dockerApi.close(); server.shutdown(); } } @@ -178,16 +169,16 @@ public class RemoteApiMockTest extends BaseDockerMockTest { public void testRemoveNonExistingContainer() throws Exception { MockWebServer server = mockWebServer(); server.enqueue(new MockResponse().setResponseCode(404)); - DockerApi api = api(server.getUrl("/")); - RemoteApi remoteApi = api.getRemoteApi(); + DockerApi dockerApi = api(server.getUrl("/")); + ContainerApi api = dockerApi.getContainerApi(); String containerId = "nonExisting"; try { - remoteApi.removeContainer(containerId); + api.removeContainer(containerId); fail("Remove container must fail on 404"); } catch (ResourceNotFoundException ex) { // Expected exception } finally { - api.close(); + dockerApi.close(); server.shutdown(); } } @@ -195,13 +186,13 @@ public class RemoteApiMockTest extends BaseDockerMockTest { public void testStartContainer() throws Exception { MockWebServer server = mockWebServer(); server.enqueue(new MockResponse().setResponseCode(200)); - DockerApi api = api(server.getUrl("/")); - RemoteApi remoteApi = api.getRemoteApi(); + DockerApi dockerApi = api(server.getUrl("/")); + ContainerApi api = dockerApi.getContainerApi(); try { - remoteApi.startContainer("1"); + api.startContainer("1"); assertRequestHasCommonFields(server.takeRequest(), "POST", "/containers/1/start"); } finally { - api.close(); + dockerApi.close(); server.shutdown(); } } @@ -209,17 +200,17 @@ public class RemoteApiMockTest extends BaseDockerMockTest { public void testStartNonExistingContainer() throws Exception { MockWebServer server = mockWebServer(); server.enqueue(new MockResponse().setResponseCode(404)); - DockerApi api = api(server.getUrl("/")); - RemoteApi remoteApi = api.getRemoteApi(); + DockerApi dockerApi = api(server.getUrl("/")); + ContainerApi api = dockerApi.getContainerApi(); try { try { - remoteApi.startContainer("1"); + api.startContainer("1"); fail("Start container must fail on 404"); } catch (ResourceNotFoundException ex) { // Expected exception } } finally { - api.close(); + dockerApi.close(); server.shutdown(); } } @@ -227,13 +218,13 @@ public class RemoteApiMockTest extends BaseDockerMockTest { public void testStopContainer() throws Exception { MockWebServer server = mockWebServer(); server.enqueue(new MockResponse().setResponseCode(200)); - DockerApi api = api(server.getUrl("/")); - RemoteApi remoteApi = api.getRemoteApi(); + DockerApi dockerApi = api(server.getUrl("/")); + ContainerApi api = dockerApi.getContainerApi(); try { - remoteApi.stopContainer("1"); + api.stopContainer("1"); assertRequestHasCommonFields(server.takeRequest(), "POST", "/containers/1/stop"); } finally { - api.close(); + dockerApi.close(); server.shutdown(); } } @@ -241,97 +232,17 @@ public class RemoteApiMockTest extends BaseDockerMockTest { public void testStopNonExistingContainer() throws Exception { MockWebServer server = mockWebServer(); server.enqueue(new MockResponse().setResponseCode(404)); - DockerApi api = api(server.getUrl("/")); - RemoteApi remoteApi = api.getRemoteApi(); + DockerApi dockerApi = api(server.getUrl("/")); + ContainerApi api = dockerApi.getContainerApi(); try { - remoteApi.stopContainer("1"); + api.stopContainer("1"); fail("Stop container must fail on 404"); } catch (ResourceNotFoundException ex) { // Expected exception } finally { - api.close(); + dockerApi.close(); server.shutdown(); } } - public void testCreateImage() throws Exception { - MockWebServer server = mockWebServer(); - server.enqueue(new MockResponse().setResponseCode(200)); - DockerApi api = api(server.getUrl("/")); - RemoteApi remoteApi = api.getRemoteApi(); - try { - remoteApi.createImage(CreateImageOptions.Builder.fromImage("base")); - assertRequestHasParameters(server.takeRequest(), "POST", "/images/create", ImmutableMultimap.of("fromImage", - "base")); - } finally { - api.close(); - server.shutdown(); - } - } - - public void testCreateImageFailure() throws Exception { - MockWebServer server = mockWebServer(); - server.enqueue(new MockResponse().setResponseCode(404)); - DockerApi api = api(server.getUrl("/")); - RemoteApi remoteApi = api.getRemoteApi(); - try { - remoteApi.createImage(CreateImageOptions.Builder.fromImage("base")); - fail("Create image must fail on 404"); - } catch (ResourceNotFoundException ex) { - // Expected exception - } finally { - api.close(); - server.shutdown(); - } - } - - public void testDeleteImage() throws Exception { - MockWebServer server = mockWebServer(); - server.enqueue(new MockResponse().setResponseCode(204)); - DockerApi api = api(server.getUrl("/")); - RemoteApi remoteApi = api.getRemoteApi(); - try { - remoteApi.deleteImage("1"); - assertRequestHasCommonFields(server.takeRequest(), "DELETE", "/images/1"); - } finally { - api.close(); - server.shutdown(); - } - } - - public void testDeleteNotExistingImage() throws Exception { - MockWebServer server = mockWebServer(); - server.enqueue(new MockResponse().setResponseCode(404)); - DockerApi api = api(server.getUrl("/")); - RemoteApi remoteApi = api.getRemoteApi(); - try { - remoteApi.deleteImage("1"); - fail("Delete image must fail on 404"); - } catch (ResourceNotFoundException ex) { - // Expected exception - } finally { - api.close(); - server.shutdown(); - } - } - - public void testBuildContainerUsingPayload() throws Exception { - MockWebServer server = mockWebServer(); - server.enqueue(new MockResponse().setResponseCode(200)); - DockerApi api = api(server.getUrl("/")); - RemoteApi remoteApi = api.getRemoteApi(); - - File file = File.createTempFile("docker", "tmp"); - FileInputStream data = new FileInputStream(file); - Payload payload = Payloads.newInputStreamPayload(data); - payload.getContentMetadata().setContentLength(file.length()); - - try { - remoteApi.build(payload, BuildOptions.NONE); - assertRequestHasCommonFields(server.takeRequest(), "POST", "/build"); - } finally { - api.close(); - server.shutdown(); - } - } } diff --git a/apis/docker/src/test/java/org/jclouds/docker/features/ImageApiLiveTest.java b/apis/docker/src/test/java/org/jclouds/docker/features/ImageApiLiveTest.java new file mode 100644 index 0000000000..a145417082 --- /dev/null +++ b/apis/docker/src/test/java/org/jclouds/docker/features/ImageApiLiveTest.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.features; + +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertNull; +import java.io.InputStream; + +import org.jclouds.docker.compute.BaseDockerApiLiveTest; +import org.jclouds.docker.options.CreateImageOptions; +import org.testng.annotations.Test; + +@Test(groups = "live", testName = "RemoteApiLiveTest", singleThreaded = true) +public class ImageApiLiveTest extends BaseDockerApiLiveTest { + + private static final String DEFAULT_IMAGE = "busybox"; + private static final String DEFAULT_TAG = "ubuntu-14.04"; + + @Test + public void testCreateImage() { + InputStream createImageStream = api().createImage(CreateImageOptions.Builder.fromImage(DEFAULT_IMAGE).tag(DEFAULT_TAG)); + consumeStream(createImageStream); + } + + @Test(dependsOnMethods = "testCreateImage") + public void testInspectImage() { + assertNotNull(api.getImageApi().inspectImage(String.format("%s:%s", DEFAULT_IMAGE, DEFAULT_TAG))); + } + + @Test(dependsOnMethods = "testInspectImage") + public void testListImages() { + assertNotNull(api().listImages()); + } + + @Test(dependsOnMethods = "testListImages") + public void testDeleteImage() { + consumeStream(api().deleteImage(String.format("%s:%s", DEFAULT_IMAGE, DEFAULT_TAG))); + assertNull(api().inspectImage(String.format("%s:%s", DEFAULT_IMAGE, DEFAULT_TAG))); + } + + private ImageApi api() { + return api.getImageApi(); + } + +} diff --git a/apis/docker/src/test/java/org/jclouds/docker/features/ImageApiMockTest.java b/apis/docker/src/test/java/org/jclouds/docker/features/ImageApiMockTest.java new file mode 100644 index 0000000000..7ad6414f52 --- /dev/null +++ b/apis/docker/src/test/java/org/jclouds/docker/features/ImageApiMockTest.java @@ -0,0 +1,97 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.features; + +import static org.testng.Assert.fail; + +import org.jclouds.docker.DockerApi; +import org.jclouds.docker.internal.BaseDockerMockTest; +import org.jclouds.docker.options.CreateImageOptions; +import org.jclouds.rest.ResourceNotFoundException; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableMultimap; +import com.squareup.okhttp.mockwebserver.MockResponse; +import com.squareup.okhttp.mockwebserver.MockWebServer; + +/** + * Mock tests for the {@link org.jclouds.docker.features.ImageApi} class. + */ +@Test(groups = "unit", testName = "ImageApiMockTest") +public class ImageApiMockTest extends BaseDockerMockTest { + + public void testCreateImage() throws Exception { + MockWebServer server = mockWebServer(); + server.enqueue(new MockResponse().setResponseCode(200)); + DockerApi dockerApi = api(server.getUrl("/")); + ImageApi api = dockerApi.getImageApi(); + try { + api.createImage(CreateImageOptions.Builder.fromImage("base")); + assertRequestHasParameters(server.takeRequest(), "POST", "/images/create", ImmutableMultimap.of("fromImage", "base")); + } finally { + dockerApi.close(); + server.shutdown(); + } + } + + public void testCreateImageFailure() throws Exception { + MockWebServer server = mockWebServer(); + server.enqueue(new MockResponse().setResponseCode(404)); + DockerApi dockerApi = api(server.getUrl("/")); + ImageApi api = dockerApi.getImageApi(); + try { + api.createImage(CreateImageOptions.Builder.fromImage("base")); + fail("Create image must fail on 404"); + } catch (ResourceNotFoundException ex) { + // Expected exception + } finally { + dockerApi.close(); + server.shutdown(); + } + } + + public void testDeleteImage() throws Exception { + MockWebServer server = mockWebServer(); + server.enqueue(new MockResponse().setResponseCode(204)); + DockerApi dockerApi = api(server.getUrl("/")); + ImageApi api = dockerApi.getImageApi(); + try { + api.deleteImage("1"); + assertRequestHasCommonFields(server.takeRequest(), "DELETE", "/images/1"); + } finally { + dockerApi.close(); + server.shutdown(); + } + } + + public void testDeleteNotExistingImage() throws Exception { + MockWebServer server = mockWebServer(); + server.enqueue(new MockResponse().setResponseCode(404)); + DockerApi dockerApi = api(server.getUrl("/")); + ImageApi api = dockerApi.getImageApi(); + try { + api.deleteImage("1"); + fail("Delete image must fail on 404"); + } catch (ResourceNotFoundException ex) { + // Expected exception + } finally { + dockerApi.close(); + server.shutdown(); + } + } + +} diff --git a/apis/docker/src/test/java/org/jclouds/docker/features/MiscApiLiveTest.java b/apis/docker/src/test/java/org/jclouds/docker/features/MiscApiLiveTest.java new file mode 100644 index 0000000000..b14a1085e9 --- /dev/null +++ b/apis/docker/src/test/java/org/jclouds/docker/features/MiscApiLiveTest.java @@ -0,0 +1,86 @@ +/* + * 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 org.jclouds.docker.compute.BaseDockerApiLiveTest; +import org.jclouds.docker.options.BuildOptions; +import org.testng.annotations.AfterClass; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; +import java.io.IOException; +import java.io.InputStream; +import java.net.URISyntaxException; + +import com.google.common.base.Splitter; +import com.google.common.collect.Iterables; + +@Test(groups = "live", testName = "MiscApiLiveTest", singleThreaded = true) +public class MiscApiLiveTest extends BaseDockerApiLiveTest { + + private static final String API_VERSION = "1.15"; + private static final String VERSION = "1.3.0"; + private static final String GIT_COMMIT = "c78088f"; + private static final String GO_VERSION = "go1.3.3"; + private static final String KERNEL_VERSION = "3.16.4-tinycore64"; + private static final String ARCH = "amd64"; + private static final String OS = "linux"; + + private static String imageId; + + @Test + public void testVersion() { + assertEquals(api().getVersion().apiVersion(), API_VERSION); + assertEquals(api().getVersion().version(), VERSION); + assertEquals(api().getVersion().gitCommit(), GIT_COMMIT); + assertEquals(api().getVersion().goVersion(), GO_VERSION); + assertEquals(api().getVersion().kernelVersion(), KERNEL_VERSION); + assertEquals(api().getVersion().arch(), ARCH); + assertEquals(api().getVersion().os(), OS); + } + + @Test + public void testInfo() { + assertNotNull(api().getInfo()); + } + + @Test + public void testBuildImageFromDockerfile() throws IOException, InterruptedException, URISyntaxException { + BuildOptions options = BuildOptions.Builder.tag("testBuildImage").verbose(false).nocache(false); + InputStream buildImageStream = api().build(tarredDockerfile(), options); + String buildStream = consumeStream(buildImageStream); + Iterable splitted = Splitter.on("\n").split(buildStream.replace("\r", "").trim()); + String lastStreamedLine = Iterables.getLast(splitted).trim(); + String rawImageId = Iterables.getLast(Splitter.on("Successfully built ").split(lastStreamedLine)); + imageId = rawImageId.substring(0, 11); + assertNotNull(imageId); + } + + @AfterClass + protected void tearDown() { + if (imageId != null) { + consumeStream(api.getImageApi().deleteImage(imageId)); + } + } + + private MiscApi api() { + return api.getMiscApi(); + } + + +} diff --git a/apis/docker/src/test/java/org/jclouds/docker/features/MiscApiMockTest.java b/apis/docker/src/test/java/org/jclouds/docker/features/MiscApiMockTest.java new file mode 100644 index 0000000000..e7b1e5b1b4 --- /dev/null +++ b/apis/docker/src/test/java/org/jclouds/docker/features/MiscApiMockTest.java @@ -0,0 +1,159 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.features; + +import com.squareup.okhttp.mockwebserver.MockResponse; +import com.squareup.okhttp.mockwebserver.MockWebServer; +import org.jclouds.docker.DockerApi; +import org.jclouds.docker.domain.Info; +import org.jclouds.docker.domain.Version; +import org.jclouds.docker.internal.BaseDockerMockTest; +import org.jclouds.docker.options.BuildOptions; +import org.jclouds.io.Payload; +import org.jclouds.io.Payloads; +import org.jclouds.rest.ResourceNotFoundException; +import org.testng.annotations.Test; + +import static org.jclouds.docker.compute.BaseDockerApiLiveTest.tarredDockerfile; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.fail; +import java.io.File; +import java.io.FileInputStream; + +/** + * Mock tests for the {@link org.jclouds.docker.features.MiscApi} class. + */ +@Test(groups = "unit", testName = "MiscApiMockTest") +public class MiscApiMockTest extends BaseDockerMockTest { + + private static final String API_VERSION = "1.15"; + private static final String VERSION = "1.3.0"; + private static final String GIT_COMMIT = "c78088f"; + private static final String GO_VERSION = "go1.3.3"; + private static final String KERNEL_VERSION = "3.16.4-tinycore64"; + private static final String ARCH = "amd64"; + private static final String OS = "linux"; + + public void testGetVersion() throws Exception { + MockWebServer server = mockWebServer(); + server.enqueue(new MockResponse().setBody(payloadFromResource("/version.json"))); + DockerApi dockerApi = api(server.getUrl("/")); + MiscApi api = dockerApi.getMiscApi(); + try { + Version version = api.getVersion(); + assertRequestHasCommonFields(server.takeRequest(), "/version"); + assertNotNull(version); + assertEquals(version.version(), VERSION); + assertEquals(version.gitCommit(), GIT_COMMIT); + assertEquals(version.apiVersion(), API_VERSION); + assertEquals(version.goVersion(), GO_VERSION); + assertEquals(version.kernelVersion(), KERNEL_VERSION); + assertEquals(version.arch(), ARCH); + assertEquals(version.os(), OS); + } finally { + dockerApi.close(); + server.shutdown(); + } + } + + public void testGetInfo() throws Exception { + MockWebServer server = mockWebServer(); + server.enqueue(new MockResponse().setBody(payloadFromResource("/info.json"))); + DockerApi dockerApi = api(server.getUrl("/")); + MiscApi api = dockerApi.getMiscApi(); + + try { + Info info = api.getInfo(); + assertRequestHasCommonFields(server.takeRequest(), "/info"); + assertNotNull(info); + assertNotNull(info.containers()); + assertNotNull(info.debug()); + assertNotNull(info.driver()); + assertNotNull(info.executionDriver()); + assertNotNull(info.images()); + assertNotNull(info.indexServerAddress()); + assertNotNull(info.initPath()); + assertNotNull(info.iPv4Forwarding()); + assertNotNull(info.kernelVersion()); + assertNotNull(info.memoryLimit()); + assertNotNull(info.nEventsListener()); + assertNotNull(info.nFd()); + assertNotNull(info.nGoroutines()); + assertNotNull(info.swapLimit()); + } finally { + dockerApi.close(); + server.shutdown(); + } + } + + public void testBuildContainer() throws Exception { + MockWebServer server = mockWebServer(); + server.enqueue(new MockResponse().setResponseCode(200)); + DockerApi dockerApi = api(server.getUrl("/")); + MiscApi api = dockerApi.getMiscApi(); + File dockerFile = File.createTempFile("docker", "tmp"); + try { + api.build(tarredDockerfile(), BuildOptions.NONE); + assertRequestHasCommonFields(server.takeRequest(), "POST", "/build"); + } finally { + dockerFile.delete(); + dockerApi.close(); + server.shutdown(); + } + } + + public void testBuildContainerUsingPayload() throws Exception { + MockWebServer server = mockWebServer(); + server.enqueue(new MockResponse().setResponseCode(200)); + DockerApi dockerApi = api(server.getUrl("/")); + MiscApi api = dockerApi.getMiscApi(); + File file = File.createTempFile("docker", "tmp"); + FileInputStream data = new FileInputStream(file); + Payload payload = Payloads.newInputStreamPayload(data); + payload.getContentMetadata().setContentLength(file.length()); + + try { + api.build(payload, BuildOptions.NONE); + assertRequestHasCommonFields(server.takeRequest(), "POST", "/build"); + } finally { + dockerApi.close(); + server.shutdown(); + } + } + + public void testBuildNonexistentContainer() throws Exception { + MockWebServer server = mockWebServer(); + server.enqueue(new MockResponse().setResponseCode(404)); + DockerApi dockerApi = api(server.getUrl("/")); + MiscApi api = dockerApi.getMiscApi(); + File dockerFile = File.createTempFile("docker", "tmp"); + try { + try { + api.build(tarredDockerfile(), BuildOptions.NONE); + fail("Build container must fail on 404"); + } catch (ResourceNotFoundException ex) { + // Expected exception + } + } finally { + dockerFile.delete(); + dockerApi.close(); + server.shutdown(); + } + } + +} diff --git a/apis/docker/src/test/java/org/jclouds/docker/internal/BaseDockerMockTest.java b/apis/docker/src/test/java/org/jclouds/docker/internal/BaseDockerMockTest.java index 146b2a051d..efdc9b5b2c 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/internal/BaseDockerMockTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/internal/BaseDockerMockTest.java @@ -58,8 +58,8 @@ public class BaseDockerMockTest { return ContextBuilder.newBuilder(provider) .credentials("clientid", "apikey") .endpoint(url.toString()) - .modules(modules) - .overrides(setupProperties()) + .modules(modules) + .overrides(setupProperties()) .buildApi(DockerApi.class); } diff --git a/apis/docker/src/test/resources/Dockerfile b/apis/docker/src/test/resources/Dockerfile index 131871544c..3c3b30ea48 100644 --- a/apis/docker/src/test/resources/Dockerfile +++ b/apis/docker/src/test/resources/Dockerfile @@ -14,16 +14,19 @@ # See the License for the specific language governing permissions and # limitations under the License. # +FROM ubuntu:14.04 +MAINTAINER Sven Dowideit -FROM centos:6.4 -MAINTAINER Andrea Turli +RUN apt-get update && apt-get install -y openssh-server +RUN mkdir /var/run/sshd +RUN echo 'root:screencast' | chpasswd +RUN sed -i 's/PermitRootLogin without-password/PermitRootLogin yes/' /etc/ssh/sshd_config -# RUN yum -y groupinstall 'Development Tools' -RUN yum -y install openssh-server openssh-clients +# SSH login fix. Otherwise user is kicked off after login +RUN sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd -RUN chkconfig sshd on -RUN service sshd start -RUN echo 'root:password' | chpasswd +ENV NOTVISIBLE "in users profile" +RUN echo "export VISIBLE=now" >> /etc/profile EXPOSE 22 CMD ["/usr/sbin/sshd", "-D"] \ No newline at end of file diff --git a/apis/docker/src/test/resources/SimpleDockerfile b/apis/docker/src/test/resources/SimpleDockerfile new file mode 100644 index 0000000000..ecce563190 --- /dev/null +++ b/apis/docker/src/test/resources/SimpleDockerfile @@ -0,0 +1,18 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +FROM scratch \ No newline at end of file diff --git a/apis/docker/src/test/resources/container.json b/apis/docker/src/test/resources/container.json index 839abb6de2..1356e9fa91 100644 --- a/apis/docker/src/test/resources/container.json +++ b/apis/docker/src/test/resources/container.json @@ -1,81 +1,129 @@ { - "Args": [ - "-c", - "yum -y install openssh-server openssh-clients" - ], - "Config": { - "AttachStderr": false, - "AttachStdin": false, - "AttachStdout": false, - "Cmd": [ - "/bin/sh", - "-c", - "yum -y install openssh-server openssh-clients" + "Args": [ + "-iface", + "ethwe", + "-wait", + "5", + "-name", + "7a:63:a2:39:7b:0f" ], - "CpuShares": 0, - "Cpuset": "", - "Domainname": "", - "Entrypoint": [ "/usr/bin/sshd", "-d" ], - "Env": [ - "HOME=/", - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" - ], - "ExposedPorts": null, - "Hostname": "9088c45a9592", - "Image": "1e2d12a45cd57ae3fe3c31ede149d800aaf6a711c61646fad340080f531775c8", - "Memory": 0, - "MemorySwap": 0, - "NetworkDisabled": false, - "OnBuild": [], - "OpenStdin": false, - "PortSpecs": null, - "StdinOnce": false, - "Tty": false, - "User": "", - "Volumes": null, - "WorkingDir": "" - }, - "Created": "2014-06-18T08:49:25.36448943Z", - "Driver": "aufs", - "ExecDriver": "native-0.2", - "HostConfig": { - "Binds": null, - "ContainerIDFile": "", - "Dns": null, - "DnsSearch": null, - "Links": null, - "LxcConf": null, - "NetworkMode": "", - "PortBindings": null, - "Privileged": false, - "PublishAllPorts": false, - "VolumesFrom": null - }, - "HostnamePath": "/mnt/sda1/var/lib/docker/containers/be1d295c091720abc9a3105219ab75a0a7367d74156cc6048aa599fcc7d650e2/hostname", - "HostsPath": "/mnt/sda1/var/lib/docker/containers/be1d295c091720abc9a3105219ab75a0a7367d74156cc6048aa599fcc7d650e2/hosts", - "Id": "be1d295c091720abc9a3105219ab75a0a7367d74156cc6048aa599fcc7d650e2", - "Image": "1e2d12a45cd57ae3fe3c31ede149d800aaf6a711c61646fad340080f531775c8", - "MountLabel": "", - "Name": "/tender_lumiere", - "NetworkSettings": { - "Bridge": "docker0", - "Gateway": "172.17.42.1", - "IPAddress": "172.17.0.100", - "IPPrefixLen": 16, - "PortMapping": null, - "Ports": {} - }, - "Path": "/bin/sh", - "ProcessLabel": "", - "ResolvConfPath": "/mnt/sda1/var/lib/docker/containers/be1d295c091720abc9a3105219ab75a0a7367d74156cc6048aa599fcc7d650e2/resolv.conf", - "State": { - "ExitCode": 0, - "FinishedAt": "0001-01-01T00:00:00Z", - "Paused": false, - "Pid": 16422, - "Running": true, - "StartedAt": "2014-06-18T08:49:25.63685385Z" - }, - "Volumes": {}, - "VolumesRW": {} + "Config": { + "AttachStderr": false, + "AttachStdin": false, + "AttachStdout": false, + "Cmd": [ + "-name", + "7a:63:a2:39:7b:0f" + ], + "CpuShares": 0, + "Cpuset": "", + "Domainname": "", + "Entrypoint": [ + "/home/weave/weaver", + "-iface", + "ethwe", + "-wait", + "5" + ], + "Env": [ + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + ], + "ExposedPorts": { + "6783/tcp": {}, + "6783/udp": {} + }, + "Hostname": "6c9932f478bd", + "Image": "zettio/weave", + "Memory": 0, + "MemorySwap": 0, + "NetworkDisabled": false, + "OnBuild": null, + "OpenStdin": false, + "PortSpecs": null, + "SecurityOpt": null, + "StdinOnce": false, + "Tty": false, + "User": "", + "Volumes": null, + "WorkingDir": "/home/weave" + }, + "Created": "2014-10-31T17:00:21.544197943Z", + "Driver": "aufs", + "ExecDriver": "native-0.2", + "HostConfig": { + "Binds": null, + "CapAdd": null, + "CapDrop": null, + "ContainerIDFile": "", + "Devices": [], + "Dns": null, + "DnsSearch": null, + "ExtraHosts": null, + "Links": null, + "LxcConf": [], + "NetworkMode": "bridge", + "PortBindings": { + "6783/tcp": [ + { + "HostIp": "", + "HostPort": "6783" + } + ], + "6783/udp": [ + { + "HostIp": "", + "HostPort": "6783" + } + ] + }, + "Privileged": true, + "PublishAllPorts": false, + "RestartPolicy": { + "MaximumRetryCount": 0, + "Name": "" + }, + "VolumesFrom": null + }, + "HostnamePath": "/var/lib/docker/containers/6c9932f478bd761f32ddb54ed28ab42ab6fac6f2a279f561ea31503ee9d39524/hostname", + "HostsPath": "/var/lib/docker/containers/6c9932f478bd761f32ddb54ed28ab42ab6fac6f2a279f561ea31503ee9d39524/hosts", + "Id": "6c9932f478bd761f32ddb54ed28ab42ab6fac6f2a279f561ea31503ee9d39524", + "Image": "57e570db16baba1e8c0d6f3c15868ddb400f64ff76ec948e65c3ca3f15fb3587", + "MountLabel": "", + "Name": "/weave", + "NetworkSettings": { + "Bridge": "docker0", + "Gateway": "172.17.42.1", + "IPAddress": "172.17.0.7", + "IPPrefixLen": 16, + "MacAddress": "02:42:ac:11:00:07", + "PortMapping": null, + "Ports": { + "6783/tcp": [ + { + "HostIp": "0.0.0.0", + "HostPort": "6783" + } + ], + "6783/udp": [ + { + "HostIp": "0.0.0.0", + "HostPort": "6783" + } + ] + } + }, + "Path": "/home/weave/weaver", + "ProcessLabel": "", + "ResolvConfPath": "/var/lib/docker/containers/6c9932f478bd761f32ddb54ed28ab42ab6fac6f2a279f561ea31503ee9d39524/resolv.conf", + "State": { + "ExitCode": 0, + "FinishedAt": "0001-01-01T00:00:00Z", + "Paused": false, + "Pid": 3939, + "Restarting": false, + "Running": true, + "StartedAt": "2014-10-31T17:00:21.802008706Z" + }, + "Volumes": {}, + "VolumesRW": {} } \ No newline at end of file diff --git a/apis/docker/src/test/resources/info.json b/apis/docker/src/test/resources/info.json new file mode 100644 index 0000000000..6697993652 --- /dev/null +++ b/apis/docker/src/test/resources/info.json @@ -0,0 +1,28 @@ +{ + "Containers": 0, + "Debug": 1, + "Driver": "aufs", + "DriverStatus": [ + [ + "Root Dir", + "/mnt/sda1/var/lib/docker/aufs" + ], + [ + "Dirs", + "15" + ] + ], + "ExecutionDriver": "native-0.2", + "IPv4Forwarding": 1, + "Images": 15, + "IndexServerAddress": "https://index.docker.io/v1/", + "InitPath": "/usr/local/bin/docker", + "InitSha1": "", + "KernelVersion": "3.16.4-tinycore64", + "MemoryLimit": 1, + "NEventsListener": 0, + "NFd": 10, + "NGoroutines": 11, + "OperatingSystem": "Boot2Docker 1.3.0 (TCL 5.4); master : a083df4 - Thu Oct 16 17:05:03 UTC 2014", + "SwapLimit": 1 +} \ No newline at end of file diff --git a/apis/docker/src/test/resources/version.json b/apis/docker/src/test/resources/version.json new file mode 100644 index 0000000000..53706b76d7 --- /dev/null +++ b/apis/docker/src/test/resources/version.json @@ -0,0 +1,9 @@ +{ + "ApiVersion": "1.15", + "Arch": "amd64", + "GitCommit": "c78088f", + "GoVersion": "go1.3.3", + "KernelVersion": "3.16.4-tinycore64", + "Os": "linux", + "Version": "1.3.0" +} \ No newline at end of file From 14da3d680a0a7175668ddd708affedd0954ab8b7 Mon Sep 17 00:00:00 2001 From: Andrea Turli Date: Mon, 24 Nov 2014 17:34:22 +0100 Subject: [PATCH 24/81] support for docker 1.3.2 - update value objects (Container, Config) - add new value object (Resource, StatusCode) - add more options for ContainerApi - add support for pause/unpause, restart, kill, copy and attach APIs + Mock and Live tests - add *ParseTest - refactor MockTests - removed *Options.NONE - cleanup low-value javadoc - more coverage in LiveTests - removed useless DockerProperties - add comments for TLS issue - use Date where needed - fix listImages - add package-private constructor to value classes - remove KillOptions, RestartOptions, StopOptions in favour of @QueryParam - use PEM instead of PKCS12 format - update docker/pom.xml to use sshj dependencies - explicit usage of OkHttp driver - simplify DockerApiMetadata - add bouncycastle driver explicitly --- apis/docker/README.md | 19 +- apis/docker/pom.xml | 13 +- .../org/jclouds/docker/DockerApiMetadata.java | 6 +- .../strategy/DockerComputeServiceAdapter.java | 18 +- .../docker/config/DockerHttpApiModule.java | 22 +- .../config/DockerOkHttpClientSupplier.java | 50 ++++ .../docker/config/DockerParserModule.java | 2 + .../org/jclouds/docker/domain/Config.java | 257 +++++++++++++----- .../org/jclouds/docker/domain/Container.java | 128 ++++++--- .../docker/domain/ContainerSummary.java | 3 + .../jclouds/docker/domain/ExposedPorts.java | 3 + .../org/jclouds/docker/domain/HostConfig.java | 3 + .../java/org/jclouds/docker/domain/Image.java | 40 ++- .../jclouds/docker/domain/ImageSummary.java | 51 ++++ .../java/org/jclouds/docker/domain/Info.java | 51 ++-- .../docker/domain/NetworkSettings.java | 3 + .../java/org/jclouds/docker/domain/Port.java | 3 + .../Resource.java} | 21 +- .../java/org/jclouds/docker/domain/State.java | 13 +- .../org/jclouds/docker/domain/StatusCode.java | 35 +++ .../org/jclouds/docker/domain/Version.java | 3 + .../jclouds/docker/features/ContainerApi.java | 137 ++++++++-- .../org/jclouds/docker/features/ImageApi.java | 27 +- .../org/jclouds/docker/features/MiscApi.java | 13 + .../jclouds/docker/options/AttachOptions.java | 114 ++++++++ .../jclouds/docker/options/BuildOptions.java | 5 - .../jclouds/docker/options/CommitOptions.java | 5 - .../docker/options/CreateImageOptions.java | 5 - .../docker/options/DeleteImageOptions.java | 18 +- .../docker/options/ListContainerOptions.java | 5 - .../docker/options/ListImageOptions.java | 5 - .../options/RemoveContainerOptions.java | 5 - .../docker/suppliers/KeyStoreSupplier.java | 130 --------- .../suppliers/SSLContextWithKeysSupplier.java | 128 +++++++-- .../docker/compute/BaseDockerApiLiveTest.java | 4 +- .../DockerComputeServiceAdapterLiveTest.java | 6 - .../ContainerToNodeMetadataTest.java | 6 +- .../compute/functions/ImageToImageTest.java | 31 ++- .../docker/features/ContainerApiLiveTest.java | 62 ++++- .../docker/features/ContainerApiMockTest.java | 250 ++++++++--------- .../docker/features/ImageApiMockTest.java | 67 ++--- .../docker/features/MiscApiLiveTest.java | 14 +- .../docker/features/MiscApiMockTest.java | 107 ++------ .../docker/internal/BaseDockerMockTest.java | 83 ++---- .../docker/internal/BaseDockerParseTest.java | 33 +++ .../docker/parse/ContainerParseTest.java | 95 +++++++ .../docker/parse/ContainersParseTest.java | 53 ++++ .../jclouds/docker/parse/ImageParseTest.java | 83 ++++++ .../jclouds/docker/parse/ImagesParseTest.java | 69 +++++ .../jclouds/docker/parse/InfoParseTest.java | 62 +++++ .../docker/parse/VersionParseTest.java | 46 ++++ apis/docker/src/test/resources/cert.pem | 22 ++ apis/docker/src/test/resources/image.json | 83 ++++++ apis/docker/src/test/resources/images.json | 42 +++ apis/docker/src/test/resources/key.pem | 27 ++ 55 files changed, 1806 insertions(+), 780 deletions(-) create mode 100644 apis/docker/src/main/java/org/jclouds/docker/config/DockerOkHttpClientSupplier.java create mode 100644 apis/docker/src/main/java/org/jclouds/docker/domain/ImageSummary.java rename apis/docker/src/main/java/org/jclouds/docker/{config/DockerProperties.java => domain/Resource.java} (69%) create mode 100644 apis/docker/src/main/java/org/jclouds/docker/domain/StatusCode.java create mode 100644 apis/docker/src/main/java/org/jclouds/docker/options/AttachOptions.java delete mode 100644 apis/docker/src/main/java/org/jclouds/docker/suppliers/KeyStoreSupplier.java create mode 100644 apis/docker/src/test/java/org/jclouds/docker/internal/BaseDockerParseTest.java create mode 100644 apis/docker/src/test/java/org/jclouds/docker/parse/ContainerParseTest.java create mode 100644 apis/docker/src/test/java/org/jclouds/docker/parse/ContainersParseTest.java create mode 100644 apis/docker/src/test/java/org/jclouds/docker/parse/ImageParseTest.java create mode 100644 apis/docker/src/test/java/org/jclouds/docker/parse/ImagesParseTest.java create mode 100644 apis/docker/src/test/java/org/jclouds/docker/parse/InfoParseTest.java create mode 100644 apis/docker/src/test/java/org/jclouds/docker/parse/VersionParseTest.java create mode 100644 apis/docker/src/test/resources/cert.pem create mode 100644 apis/docker/src/test/resources/image.json create mode 100644 apis/docker/src/test/resources/images.json create mode 100644 apis/docker/src/test/resources/key.pem diff --git a/apis/docker/README.md b/apis/docker/README.md index 9b55cbcea4..e6e0d9c84f 100644 --- a/apis/docker/README.md +++ b/apis/docker/README.md @@ -7,10 +7,16 @@ 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: + +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 import CA certificate into Trusted Certs: - `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` + ` keytool -import -trustcacerts -file /Users/andrea/.boot2docker/certs/boot2docker-vm/ca.pem -alias BOOT2DOCKER -keystore $JAVA_HOME/jre/lib/security/cacerts` + +by default the passoword is `changeit` + +N.B.: From `Docker 1.3.2+` the server doesn't accept sslv3 protocol (https://github.com/docker/docker/pull/8588/files) #How it works @@ -45,8 +51,9 @@ then you need to create a p12 certificate using the following command: 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 -aq | xargs docker rm -f` + + $ docker rm `docker ps -a` + 2. remove all the images - `$ docker images -q | xargs docker rmi -f` + $ docker rmi -f `docker images -q` diff --git a/apis/docker/pom.xml b/apis/docker/pom.xml index b268a83da5..620cae139a 100644 --- a/apis/docker/pom.xml +++ b/apis/docker/pom.xml @@ -35,7 +35,7 @@ https://localhost:4243 - 1.10 + 1.15 FIXME FIXME org.jclouds.docker*;version="${project.version}" @@ -67,7 +67,16 @@ org.apache.jclouds.driver jclouds-sshj ${project.version} - test + + + org.apache.jclouds.driver + jclouds-bouncycastle + ${project.version} + + + org.apache.jclouds.driver + jclouds-okhttp + ${project.version} org.apache.jclouds diff --git a/apis/docker/src/main/java/org/jclouds/docker/DockerApiMetadata.java b/apis/docker/src/main/java/org/jclouds/docker/DockerApiMetadata.java index d5c470f415..25d75babaf 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/DockerApiMetadata.java +++ b/apis/docker/src/main/java/org/jclouds/docker/DockerApiMetadata.java @@ -52,8 +52,6 @@ public class DockerApiMetadata extends BaseHttpApiMetadata { public static Properties defaultProperties() { Properties properties = BaseHttpApiMetadata.defaultProperties(); - properties.setProperty(Constants.PROPERTY_MAX_RETRIES, "15"); - properties.setProperty("jclouds.ssh.retry-auth", "true"); properties.setProperty(Constants.PROPERTY_CONNECTION_TIMEOUT, "1200000"); // 15 minutes properties.setProperty(ComputeServiceProperties.IMAGE_LOGIN_USER, "root:password"); properties.setProperty(TEMPLATE, "osFamily=UBUNTU,os64Bit=true"); @@ -66,8 +64,8 @@ public class DockerApiMetadata extends BaseHttpApiMetadata { super(DockerApi.class); id("docker") .name("Docker API") - .identityName("Path to Certificate .p12 file") - .credentialName("Password to Certificate") + .identityName("Path to certificate .pem file") + .credentialName("Password to key .pem file") .documentation(URI.create("https://docs.docker.com/reference/api/docker_remote_api/")) .version("1.15") .defaultEndpoint("https://127.0.0.1:2376") diff --git a/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java b/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java index 4cbb3b0165..920071b178 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java +++ b/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java @@ -40,6 +40,7 @@ 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.domain.ImageSummary; import org.jclouds.docker.options.ListContainerOptions; import org.jclouds.docker.options.RemoveContainerOptions; import org.jclouds.domain.Location; @@ -170,14 +171,13 @@ public class DockerComputeServiceAdapter implements @Override public Set listImages() { Set images = Sets.newHashSet(); - for (Image image : api.getImageApi().listImages()) { + for (ImageSummary imageSummary : api.getImageApi().listImages()) { // less efficient than just listImages but returns richer json that needs repoTags coming from listImages - Image inspected = api.getImageApi().inspectImage(image.id()); - if (inspected.repoTags().isEmpty()) { - inspected = Image.create(inspected.id(), inspected.parent(), inspected.container(), inspected.created(), - inspected.dockerVersion(), inspected.architecture(), inspected.os(), inspected.size(), - inspected.virtualSize(), image.repoTags()); - } + Image inspected = api.getImageApi().inspectImage(imageSummary.id()); + inspected = Image.create(inspected.id(), inspected.author(), inspected.comment(), inspected.config(), + inspected.containerConfig(), inspected.parent(), inspected.created(), inspected.container(), + inspected.dockerVersion(), inspected.architecture(), inspected.os(), inspected.size(), + inspected.virtualSize(), imageSummary.repoTags()); images.add(inspected); } return images; @@ -237,12 +237,12 @@ public class DockerComputeServiceAdapter implements @Override public void resumeNode(String id) { - throw new UnsupportedOperationException("resume not supported"); + api.getContainerApi().unpause(id); } @Override public void suspendNode(String id) { - throw new UnsupportedOperationException("suspend not supported"); + api.getContainerApi().pause(id); } } diff --git a/apis/docker/src/main/java/org/jclouds/docker/config/DockerHttpApiModule.java b/apis/docker/src/main/java/org/jclouds/docker/config/DockerHttpApiModule.java index ced396c8a8..ed23d795ff 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/config/DockerHttpApiModule.java +++ b/apis/docker/src/main/java/org/jclouds/docker/config/DockerHttpApiModule.java @@ -16,28 +16,23 @@ */ 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; import org.jclouds.http.annotation.ServerError; +import org.jclouds.http.config.ConfiguresHttpCommandExecutorService; +import org.jclouds.http.okhttp.OkHttpClientSupplier; +import org.jclouds.http.okhttp.config.OkHttpCommandExecutorServiceModule; 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. */ @ConfiguresHttpApi +@ConfiguresHttpCommandExecutorService public class DockerHttpApiModule extends HttpApiModule { @Override @@ -53,11 +48,8 @@ public class DockerHttpApiModule extends HttpApiModule { @Override protected void configure() { super.configure(); - bind(new TypeLiteral>() { - }).to(new TypeLiteral() { - }); - bind(new TypeLiteral>() { - }).to(new TypeLiteral() { - }); + install(new OkHttpCommandExecutorServiceModule()); + bind(OkHttpClientSupplier.class).to(DockerOkHttpClientSupplier.class); } + } diff --git a/apis/docker/src/main/java/org/jclouds/docker/config/DockerOkHttpClientSupplier.java b/apis/docker/src/main/java/org/jclouds/docker/config/DockerOkHttpClientSupplier.java new file mode 100644 index 0000000000..f8a29b1057 --- /dev/null +++ b/apis/docker/src/main/java/org/jclouds/docker/config/DockerOkHttpClientSupplier.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.config; + +import org.jclouds.docker.suppliers.SSLContextWithKeysSupplier; +import org.jclouds.http.okhttp.OkHttpClientSupplier; + +import com.google.common.collect.ImmutableList; +import com.squareup.okhttp.ConnectionSpec; +import com.squareup.okhttp.OkHttpClient; +import com.squareup.okhttp.TlsVersion; +import javax.inject.Inject; +import javax.inject.Singleton; + +@Singleton +public class DockerOkHttpClientSupplier implements OkHttpClientSupplier { + + private final SSLContextWithKeysSupplier sslContextWithKeysSupplier; + + @Inject + DockerOkHttpClientSupplier(SSLContextWithKeysSupplier sslContextWithKeysSupplier) { + this.sslContextWithKeysSupplier = sslContextWithKeysSupplier; + } + + @Override + public OkHttpClient get() { + OkHttpClient client = new OkHttpClient(); + ConnectionSpec connectionSpecs = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) + .tlsVersions(TlsVersion.TLS_1_0, TlsVersion.TLS_1_1, TlsVersion.TLS_1_2) + .build(); + client.setConnectionSpecs(ImmutableList.of(connectionSpecs)); + client.setSslSocketFactory(sslContextWithKeysSupplier.get().getSocketFactory()); + return client; + } + +} diff --git a/apis/docker/src/main/java/org/jclouds/docker/config/DockerParserModule.java b/apis/docker/src/main/java/org/jclouds/docker/config/DockerParserModule.java index 70c76cbda0..383f99ec72 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/config/DockerParserModule.java +++ b/apis/docker/src/main/java/org/jclouds/docker/config/DockerParserModule.java @@ -21,7 +21,9 @@ import org.jclouds.json.config.GsonModule; import com.google.inject.AbstractModule; public class DockerParserModule extends AbstractModule { + @Override protected void configure() { bind(GsonModule.DateAdapter.class).to(GsonModule.Iso8601DateAdapter.class); } + } diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/Config.java b/apis/docker/src/main/java/org/jclouds/docker/domain/Config.java index bef89692f6..05dcc4863b 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/Config.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/Config.java @@ -26,8 +26,8 @@ import org.jclouds.javax.annotation.Nullable; import org.jclouds.json.SerializedNames; import com.google.auto.value.AutoValue; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; @AutoValue public abstract class Config { @@ -49,8 +49,6 @@ public abstract class Config { public abstract boolean attachStderr(); - public abstract Map exposedPorts(); - public abstract boolean tty(); public abstract boolean openStdin(); @@ -61,34 +59,75 @@ public abstract class Config { public abstract List cmd(); - public abstract List dns(); + public abstract List entrypoint(); public abstract String image(); public abstract Map volumes(); - @Nullable public abstract String volumesFrom(); - @Nullable public abstract String workingDir(); - public abstract List entrypoint(); - public abstract boolean networkDisabled(); - public abstract List onBuild(); + public abstract Map exposedPorts(); + + public abstract List securityOpts(); + + @Nullable public abstract HostConfig hostConfig(); + + public abstract List binds(); + + public abstract List links(); + + public abstract List> lxcConf(); + + public abstract Map>> portBindings(); + + public abstract boolean publishAllPorts(); + + public abstract boolean privileged(); + + @Nullable public abstract List dns(); + + @Nullable public abstract String dnsSearch(); + + @Nullable public abstract String volumesFrom(); + + public abstract List capAdd(); + + public abstract List capDrop(); + + public abstract Map restartPolicy(); + + @Nullable public abstract String networkMode(); + + public abstract Map devices(); + + Config() { + } @SerializedNames( - { "Hostname", "Domainname", "User", "Memory", "MemorySwap", "CpuShares", "AttachStdin", "AttachStdout", - "AttachStderr", "ExposedPorts", "Tty", "OpenStdin", "StdinOnce", "Env", "Cmd", "Dns", "Image", "Volumes", - "VolumesFrom", "WorkingDir", "Entrypoint", "NetworkDisabled", "OnBuild" }) + { + "Hostname", "Domainname", "User", "Memory", "MemorySwap", "CpuShares", "AttachStdin", "AttachStdout", + "AttachStderr", "Tty", "OpenStdin", "StdinOnce", "Env", "Cmd", "Entrypoint", "Image", "Volumes", + "WorkingDir", "NetworkDisabled", "ExposedPorts", "SecurityOpts", "HostConfig", "Binds", "Links", + "LxcConf", "PortBindings", "PublishAllPorts", "Privileged", "Dns", "DnsSearch", "VolumesFrom", + "CapAdd", "CapDrop", "RestartPolicy", "NetworkMode", "Devices" + }) public static Config create(String hostname, String domainname, String user, int memory, int memorySwap, - int cpuShares, boolean attachStdin, boolean attachStdout, boolean attachStderr, Map exposedPorts, - boolean tty, boolean openStdin, boolean stdinOnce, List env, List cmd, List dns, - String image, Map volumes, String volumesFrom, String workingDir, List entrypoint, - boolean networkDisabled, List onBuild) { - return new AutoValue_Config(hostname, domainname, user, memory, memorySwap, cpuShares, attachStdin, attachStdout, - attachStderr, copyOf(exposedPorts), tty, openStdin, stdinOnce, copyOf(env), copyOf(cmd), copyOf(dns), image, - copyOf(volumes), volumesFrom, workingDir, copyOf(entrypoint), networkDisabled, copyOf(onBuild)); + int cpuShares, boolean attachStdin, boolean attachStdout, boolean attachStderr, boolean tty, + boolean openStdin, boolean stdinOnce, List env, List cmd, List entrypoint, + String image, Map volumes, String workingDir, boolean networkDisabled, + Map exposedPorts, List securityOpts, HostConfig hostConfig, List binds, + List links, List> lxcConf, Map>> portBindings, + boolean publishAllPorts, boolean privileged, List dns, String dnsSearch, String volumesFrom, + List capAdd, List capDrop, Map restartPolicy, String networkMode, Map devices) { + return new AutoValue_Config(hostname, domainname, user, memory, memorySwap, cpuShares, attachStdin, + attachStdout, attachStderr, tty, openStdin, stdinOnce, copyOf(env), copyOf(cmd), copyOf(entrypoint), + image, copyOf(volumes), workingDir, networkDisabled, copyOf(exposedPorts), copyOf(securityOpts), hostConfig, + copyOf(binds), copyOf(links), copyOf(lxcConf), copyOf(portBindings), publishAllPorts, privileged, + copyOf(dns), dnsSearch, volumesFrom, copyOf(capAdd), copyOf(capDrop), copyOf(restartPolicy), networkMode, + copyOf(devices)); } public static Builder builder() { @@ -109,21 +148,33 @@ public abstract class Config { private boolean attachStdin; private boolean attachStdout; private boolean attachStderr; - private Map exposedPorts = ImmutableMap.of(); - private List env = ImmutableList.of(); private boolean tty; private boolean openStdin; private boolean stdinOnce; - private List cmd = ImmutableList.of(); - private List dns = ImmutableList.of(); + private List env = Lists.newArrayList(); + private List cmd = Lists.newArrayList(); + private List entrypoint = Lists.newArrayList(); private String image; - private Map volumes = ImmutableMap.of(); - private String volumesFrom; + private Map volumes = Maps.newHashMap(); private String workingDir; - private List entrypoint = ImmutableList.of(); private boolean networkDisabled; - private List onBuild = ImmutableList.of(); - private Map restartPolicy = ImmutableMap.of(); + private Map exposedPorts = Maps.newHashMap(); + private List securityOpts = Lists.newArrayList(); + private HostConfig hostConfig; + private List binds = Lists.newArrayList(); + private List links = Lists.newArrayList(); + private List> lxcConf = Lists.newArrayList(); + private Map>> portBindings = Maps.newHashMap(); + private boolean publishAllPorts; + private boolean privileged; + private List dns; + private String dnsSearch; + private String volumesFrom; + private List capAdd = Lists.newArrayList(); + private List capDrop = Lists.newArrayList(); + private Map restartPolicy = Maps.newHashMap(); + private String networkMode; + private Map devices = Maps.newHashMap(); public Builder hostname(String hostname) { this.hostname = hostname; @@ -170,11 +221,6 @@ public abstract class Config { return this; } - public Builder exposedPorts(Map exposedPorts) { - this.exposedPorts = ImmutableMap.copyOf(checkNotNull(exposedPorts, "exposedPorts")); - return this; - } - public Builder tty(boolean tty) { this.tty = tty; return this; @@ -196,32 +242,7 @@ public abstract class Config { } public Builder cmd(List cmd) { - this.cmd = ImmutableList.copyOf(checkNotNull(cmd, "cmd")); - return this; - } - - public Builder dns(List dns) { - this.dns = ImmutableList.copyOf(checkNotNull(dns, "dns")); - return this; - } - - public Builder image(String image) { - this.image = image; - return this; - } - - public Builder volumes(Map volumes) { - this.volumes = ImmutableMap.copyOf(checkNotNull(volumes, "volumes")); - return this; - } - - public Builder volumesFrom(String volumesFrom) { - this.volumesFrom = volumesFrom; - return this; - } - - public Builder workingDir(String workingDir) { - this.workingDir = workingDir; + this.cmd = cmd; return this; } @@ -230,35 +251,129 @@ public abstract class Config { return this; } + public Builder image(String image) { + this.image = checkNotNull(image, "image"); + return this; + } + + public Builder volumes(Map volumes) { + this.volumes = volumes; + return this; + } + + public Builder workingDir(String workingDir) { + this.workingDir = workingDir; + return this; + } + public Builder networkDisabled(boolean networkDisabled) { this.networkDisabled = networkDisabled; return this; } - public Builder onBuild(List onBuild) { - this.onBuild = ImmutableList.copyOf(checkNotNull(onBuild, "onBuild")); + public Builder exposedPorts(Map exposedPorts) { + this.exposedPorts = exposedPorts; + return this; + } + + public Builder securityOpts(List securityOpts) { + this.securityOpts = securityOpts; + return this; + } + + public Builder hostConfig(HostConfig hostConfig) { + this.hostConfig = checkNotNull(hostConfig, "hostConfig"); + return this; + } + + public Builder binds(List binds) { + this.binds = binds; + return this; + } + + public Builder links(List links) { + this.links = links; + return this; + } + + public Builder lxcConf(List> lxcConf) { + this.lxcConf = lxcConf; + return this; + } + + public Builder portBindings(Map>> portBindings) { + this.portBindings = portBindings; + return this; + } + + public Builder publishAllPorts(boolean publishAllPorts) { + this.publishAllPorts = publishAllPorts; + return this; + } + + public Builder privileged(boolean privileged) { + this.privileged = privileged; + return this; + } + + public Builder dns(List dns) { + this.dns = dns; + return this; + } + + public Builder dnsSearch(String dnsSearch) { + this.dnsSearch = dnsSearch; + return this; + } + + public Builder volumesFrom(String volumesFrom) { + this.volumesFrom = volumesFrom; + return this; + } + + public Builder capAdd(List capAdd) { + this.capAdd = capAdd; + return this; + } + + public Builder capDrop(List capDrop) { + this.capDrop = capDrop; return this; } public Builder restartPolicy(Map restartPolicy) { - this.restartPolicy = ImmutableMap.copyOf(restartPolicy); + this.restartPolicy = restartPolicy; + return this; + } + + public Builder networkMode(String networkMode) { + this.networkMode = networkMode; + return this; + } + + public Builder devices(Map devices) { + this.devices = devices; 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, - workingDir, entrypoint, networkDisabled, onBuild); + attachStderr, tty, openStdin, stdinOnce, env, cmd, entrypoint, image, volumes, workingDir, + networkDisabled, exposedPorts, securityOpts, hostConfig, binds, links, lxcConf, portBindings, + publishAllPorts, privileged, dns, dnsSearch, volumesFrom, capAdd, capDrop, restartPolicy, + networkMode, devices); } public Builder fromConfig(Config in) { return hostname(in.hostname()).domainname(in.domainname()).user(in.user()).memory(in.memory()) - .memorySwap(in.memorySwap()).cpuShares(in.cpuShares()).attachStdin(in.attachStdin()) - .attachStdout(in.attachStdout()).attachStderr(in.attachStderr()).exposedPorts(in.exposedPorts()) - .tty(in.tty()).openStdin(in.openStdin()).stdinOnce(in.stdinOnce()).env(in.env()).cmd(in.cmd()) - .dns(in.dns()).image(in.image()).volumes(in.volumes()).volumesFrom(in.volumesFrom()) - .workingDir(in.workingDir()).entrypoint(in.entrypoint()).networkDisabled(in.networkDisabled()) - .onBuild(in.onBuild()); + .memorySwap(in.memorySwap()).cpuShares(in.cpuShares()).attachStdin(in.attachStdin()) + .attachStdout(in.attachStdout()).attachStderr(in.attachStderr()).tty(in.tty()) + .image(in.image()).volumes(in.volumes()).workingDir(in.workingDir()) + .networkDisabled(in.networkDisabled()).exposedPorts(in.exposedPorts()).securityOpts(in.securityOpts()) + .hostConfig(in.hostConfig()).binds(in.binds()).links(in.links()).lxcConf(in.lxcConf()) + .portBindings(in.portBindings()).publishAllPorts(in.publishAllPorts()).privileged(in.privileged()) + .dns(in.dns()).dnsSearch(in.dnsSearch()).volumesFrom(in.volumesFrom()).capAdd(in.capAdd()) + .capDrop(in.capDrop()).restartPolicy(in.restartPolicy()).networkMode(in.networkMode()).devices(in.devices()); } } diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/Container.java b/apis/docker/src/main/java/org/jclouds/docker/domain/Container.java index b06c3a56f9..e784351b40 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/Container.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/Container.java @@ -18,6 +18,7 @@ package org.jclouds.docker.domain; import static org.jclouds.docker.internal.NullSafeCopies.copyOf; +import java.util.Date; import java.util.List; import java.util.Map; @@ -32,12 +33,12 @@ import com.google.common.collect.ImmutableMap; public abstract class Container { public abstract String id(); - @Nullable public abstract String name(); - - @Nullable public abstract String created(); + @Nullable public abstract Date created(); @Nullable public abstract String path(); + @Nullable public abstract String name(); + public abstract List args(); @Nullable public abstract Config config(); @@ -48,37 +49,52 @@ public abstract class Container { @Nullable public abstract NetworkSettings networkSettings(); + @Nullable public abstract String sysInitPath(); + @Nullable public abstract String resolvConfPath(); + public abstract Map volumes(); + + @Nullable public abstract HostConfig hostConfig(); + @Nullable public abstract String driver(); @Nullable public abstract String execDriver(); - public abstract Map volumes(); - public abstract Map volumesRW(); @Nullable public abstract String command(); @Nullable public abstract String status(); - @Nullable public abstract HostConfig hostConfig(); - public abstract List ports(); @Nullable public abstract String hostnamePath(); + @Nullable public abstract String hostsPath(); + + @Nullable public abstract String mountLabel(); + + @Nullable public abstract String processLabel(); + + Container() { + } + @SerializedNames( - { "Id", "Name", "Created", "Path", "Args", "Config", "State", "Image", "NetworkSettings", "ResolvConfPath", - "Driver", "ExecDriver", "Volumes", "VolumesRW", "Command", "Status", "HostConfig", "Ports", - "HostnamePath" }) - public static Container create(String id, String name, String created, String path, List args, Config config, - State state, String image, NetworkSettings networkSettings, String resolvConfPath, String driver, - String execDriver, Map volumes, Map volumesRW, String command, String status, - HostConfig hostConfig, List ports, String hostnamePath) { - return new AutoValue_Container(id, name, created, path, copyOf(args), config, state, image, networkSettings, - resolvConfPath, driver, execDriver, copyOf(volumes), copyOf(volumesRW), command, status, hostConfig, - copyOf(ports), hostnamePath); + { + "Id", "Created", "Path", "Name", "Args", "Config", "State", "Image", "NetworkSettings", "SysInitPath", + "ResolvConfPath", "Volumes", "HostConfig", "Driver", "ExecDriver", "VolumesRW", "Command", "Status", + "Ports", "HostnamePath", "HostsPath", "MountLabel", "ProcessLabel" + }) + public static Container create(String id, Date created, String path, String name, List args, Config config, + State state, String image, NetworkSettings networkSettings, String sysInitPath, + String resolvConfPath, Map volumes, HostConfig hostConfig, + String driver, String execDriver, Map volumesRW, String command, + String status, List ports, String hostnamePath, String hostsPath, + String mountLabel, String processLabel) { + return new AutoValue_Container(id, created, path, name, copyOf(args), config, state, image, networkSettings, + sysInitPath, resolvConfPath, copyOf(volumes), hostConfig, driver, execDriver, copyOf(volumesRW), command, + status, copyOf(ports), hostnamePath, hostsPath, mountLabel, processLabel); } public static Builder builder() { @@ -92,36 +108,35 @@ public abstract class Container { public static final class Builder { private String id; - private String name; - private String created; + private Date created; private String path; + private String name; private List args; private Config config; private State state; private String image; private NetworkSettings networkSettings; + private String sysInitPath; private String resolvConfPath; + private Map volumes = ImmutableMap.of(); + private HostConfig hostConfig; private String driver; private String execDriver; - private Map volumes = ImmutableMap.of(); private Map volumesRW = ImmutableMap.of(); private String command; private String status; - private HostConfig hostConfig; private List ports = ImmutableList.of(); private String hostnamePath; + private String hostsPath; + private String mountLabel; + private String processLabel; public Builder id(String id) { this.id = id; return this; } - public Builder name(String name) { - this.name = name; - return this; - } - - public Builder created(String created) { + public Builder created(Date created) { this.created = created; return this; } @@ -131,6 +146,11 @@ public abstract class Container { return this; } + public Builder name(String name) { + this.name = name; + return this; + } + public Builder args(List args) { this.args = args; return this; @@ -156,11 +176,26 @@ public abstract class Container { return this; } + public Builder sysInitPath(String sysInitPath) { + this.sysInitPath = sysInitPath; + return this; + } + public Builder resolvConfPath(String resolvConfPath) { this.resolvConfPath = resolvConfPath; return this; } + public Builder volumes(Map volumes) { + this.volumes = volumes; + return this; + } + + public Builder hostConfig(HostConfig hostConfig) { + this.hostConfig = hostConfig; + return this; + } + public Builder driver(String driver) { this.driver = driver; return this; @@ -171,11 +206,6 @@ public abstract class Container { return this; } - public Builder volumes(Map volumes) { - this.volumes = volumes; - return this; - } - public Builder volumesRW(Map volumesRW) { this.volumesRW = volumesRW; return this; @@ -191,11 +221,6 @@ public abstract class Container { return this; } - public Builder hostConfig(HostConfig hostConfig) { - this.hostConfig = hostConfig; - return this; - } - public Builder ports(List ports) { this.ports = ports; return this; @@ -206,17 +231,34 @@ public abstract class Container { return this; } + public Builder hostsPath(String hostsPath) { + this.hostsPath = hostsPath; + return this; + } + + public Builder mountLabel(String mountLabel) { + this.mountLabel = mountLabel; + return this; + } + + public Builder processLabel(String processLabel) { + this.processLabel = processLabel; + return this; + } + public Container build() { - return Container.create(id, name, created, path, args, config, state, image, networkSettings, resolvConfPath, - driver, execDriver, volumes, volumesRW, command, status, hostConfig, ports, hostnamePath); + return Container.create(id, created, path, name, args, config, state, image, networkSettings, + sysInitPath, resolvConfPath, volumes, hostConfig, driver, execDriver, volumesRW, command, status, + ports, hostnamePath, hostsPath, mountLabel, processLabel); } public Builder fromContainer(Container in) { return this.id(in.id()).name(in.name()).created(in.created()).path(in.path()).args(in.args()) - .config(in.config()).state(in.state()).image(in.image()).networkSettings(in.networkSettings()) - .resolvConfPath(in.resolvConfPath()).driver(in.driver()).execDriver(in.execDriver()) - .volumes(in.volumes()).volumesRW(in.volumesRW()).command(in.command()).status(in.status()) - .hostConfig(in.hostConfig()).ports(in.ports()).hostnamePath(in.hostnamePath()); + .config(in.config()).state(in.state()).image(in.image()).networkSettings(in.networkSettings()) + .sysInitPath(in.sysInitPath()).resolvConfPath(in.resolvConfPath()).driver(in.driver()) + .execDriver(in.execDriver()).volumes(in.volumes()).hostConfig(in.hostConfig()).volumesRW(in.volumesRW()) + .command(in.command()).status(in.status()).ports(in.ports()).hostnamePath(in.hostnamePath()) + .hostsPath(in.hostsPath()).mountLabel(in.mountLabel()).processLabel(in.processLabel()); } } } diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/ContainerSummary.java b/apis/docker/src/main/java/org/jclouds/docker/domain/ContainerSummary.java index 17d006427d..25bd5952cf 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/ContainerSummary.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/ContainerSummary.java @@ -41,6 +41,9 @@ public abstract class ContainerSummary { public abstract String status(); + ContainerSummary() { + } + @SerializedNames({"Id", "Names", "Created", "Image", "Command", "Ports", "Status"}) public static ContainerSummary create(String id, List names, String created, String image, String command, List ports, String status) { return new AutoValue_ContainerSummary(id, copyOf(names), created, image, command, copyOf(ports), status); diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/ExposedPorts.java b/apis/docker/src/main/java/org/jclouds/docker/domain/ExposedPorts.java index 413c84f498..ac57a988f9 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/ExposedPorts.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/ExposedPorts.java @@ -30,6 +30,9 @@ public abstract class ExposedPorts { public abstract List hostPorts(); + ExposedPorts() { + } + @SerializedNames({ "PortAndProtocol", "HostPorts" }) public static ExposedPorts create(String portAndProtocol, List hostPorts) { return new AutoValue_ExposedPorts(portAndProtocol, copyOf(hostPorts)); diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/HostConfig.java b/apis/docker/src/main/java/org/jclouds/docker/domain/HostConfig.java index 630be7c4ab..30efd9cd97 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/HostConfig.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/HostConfig.java @@ -51,6 +51,9 @@ public abstract class HostConfig { public abstract List volumesFrom(); + HostConfig() { + } + @SerializedNames({ "ContainerIDFile", "Binds", "LxcConf", "Privileged", "Dns", "DnsSearch", "PortBindings", "Links", "PublishAllPorts", "VolumesFrom" }) public static HostConfig create(String containerIDFile, List binds, List> lxcConf, diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/Image.java b/apis/docker/src/main/java/org/jclouds/docker/domain/Image.java index c80e44860c..f49a7b01f6 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/Image.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/Image.java @@ -18,6 +18,7 @@ package org.jclouds.docker.domain; import static org.jclouds.docker.internal.NullSafeCopies.copyOf; +import java.util.Date; import java.util.List; import org.jclouds.javax.annotation.Nullable; @@ -27,32 +28,43 @@ import com.google.auto.value.AutoValue; @AutoValue public abstract class Image { + public abstract String id(); - @Nullable public abstract String parent(); + @Nullable public abstract String author(); - @Nullable public abstract String created(); + @Nullable public abstract String comment(); - @Nullable public abstract String container(); + @Nullable public abstract Config config(); - @Nullable public abstract String dockerVersion(); + @Nullable public abstract Config containerConfig(); - @Nullable public abstract String architecture(); + public abstract String parent(); - @Nullable public abstract String os(); + public abstract Date created(); + + public abstract String container(); + + public abstract String dockerVersion(); + + public abstract String architecture(); + + public abstract String os(); public abstract long size(); - @Nullable public abstract long virtualSize(); + public abstract long virtualSize(); - public abstract List repoTags(); + @Nullable public abstract List repoTags(); - @SerializedNames({ "Id", "Parent", "Created", "Container", "DockerVersion", "Architecture", "Os", "Size", - "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 repoTags) { - return new AutoValue_Image(id, parent, created, container, dockerVersion, architecture, os, size, virtualSize, - copyOf(repoTags)); + Image() { + } + + @SerializedNames({ "Id", "Author", "Comment", "Config", "ContainerConfig", "Parent", "Created", + "Container", "DockerVersion", "Architecture", "Os", "Size", "VirtualSize", "RepoTags" }) + public static Image create(String id, String author, String comment, Config config, Config containerConfig, String parent, Date created, String container, String dockerVersion, String architecture, String os, long size, long virtualSize, List repoTags) { + return new AutoValue_Image(id, author, comment, config, containerConfig, parent, created, container, + dockerVersion, architecture, os, size, virtualSize, copyOf(repoTags)); } } diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/ImageSummary.java b/apis/docker/src/main/java/org/jclouds/docker/domain/ImageSummary.java new file mode 100644 index 0000000000..f4ea9b9204 --- /dev/null +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/ImageSummary.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.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 Image value class) +@AutoValue +public abstract class ImageSummary { + + public abstract String id(); + + public abstract long created(); + + public abstract String parentId(); + + public abstract int size(); + + public abstract int virtualSize(); + + public abstract List repoTags(); + + ImageSummary() { + } + + @SerializedNames({"Id", "Created", "ParentId", "Size", "VirtualSize", "RepoTags"}) + public static ImageSummary create(String id, long created, String parentId, int size, int virtualSize, + List repoTags) { + return new AutoValue_ImageSummary(id, created, parentId, size, virtualSize, copyOf(repoTags)); + } + +} diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/Info.java b/apis/docker/src/main/java/org/jclouds/docker/domain/Info.java index 596c0a51f2..8a4c504130 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/Info.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/Info.java @@ -16,6 +16,8 @@ */ package org.jclouds.docker.domain; +import java.util.List; + import org.jclouds.json.SerializedNames; import com.google.auto.value.AutoValue; @@ -25,39 +27,52 @@ public abstract class Info { public abstract int containers(); - public abstract int images(); + public abstract int debug(); public abstract String driver(); + public abstract List> driverStatus(); + public abstract String executionDriver(); + public abstract int iPv4Forwarding(); + + public abstract int images(); + + public abstract String indexServerAddress(); + + public abstract String initPath(); + + public abstract String initSha1(); + public abstract String kernelVersion(); - public abstract int debug(); + public abstract int memoryLimit(); + + public abstract int nEventsListener(); 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 String operatingSystem(); public abstract int swapLimit(); - public abstract int iPv4Forwarding(); + Info() { + } - @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); + @SerializedNames({ + "Containers", "Debug", "Driver", "DriverStatus", "ExecutionDriver", "IPv4Forwarding", "Images", + "IndexServerAddress", "InitPath", "InitSha1", "KernelVersion", "MemoryLimit", "NEventsListener", + "NFd", "NGoroutines", "OperatingSystem", "SwapLimit" + }) + public static Info create(int containers, int debug, String driver, List> driverStatus, + String executionDriver, int iPv4Forwarding, int images, String indexServerAddress, + String initPath, String initSha1, String kernelVersion, int memoryLimit, + int nEventsListener, int nFd, int nGoroutines, String operatingSystem, int swapLimit) { + return new AutoValue_Info(containers, debug, driver, driverStatus, executionDriver, iPv4Forwarding, images, + indexServerAddress, initPath, initSha1, kernelVersion, memoryLimit, nEventsListener, nFd, nGoroutines, + operatingSystem, swapLimit); } } diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/NetworkSettings.java b/apis/docker/src/main/java/org/jclouds/docker/domain/NetworkSettings.java index f99bd7f3dd..fee82f2224 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/NetworkSettings.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/NetworkSettings.java @@ -42,6 +42,9 @@ public abstract class NetworkSettings { public abstract Map>> ports(); + NetworkSettings() { + } + @SerializedNames({ "IPAddress", "IPPrefixLen", "Gateway", "Bridge", "PortMapping", "Ports" }) public static NetworkSettings create(String ipAddress, int ipPrefixLen, String gateway, String bridge, String portMapping, Map>> ports) { diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/Port.java b/apis/docker/src/main/java/org/jclouds/docker/domain/Port.java index 0522be185d..b4bc93e1e1 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/Port.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/Port.java @@ -30,6 +30,9 @@ public abstract class Port { public abstract String type(); + Port() { + } + @SerializedNames({ "IP", "PrivatePort", "PublicPort", "Type" }) public static Port create(String ip, int privatePort, int publicPort, String type) { return new AutoValue_Port(ip, privatePort, publicPort, type); diff --git a/apis/docker/src/main/java/org/jclouds/docker/config/DockerProperties.java b/apis/docker/src/main/java/org/jclouds/docker/domain/Resource.java similarity index 69% rename from apis/docker/src/main/java/org/jclouds/docker/config/DockerProperties.java rename to apis/docker/src/main/java/org/jclouds/docker/domain/Resource.java index b870feac0f..701318ce08 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/config/DockerProperties.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/Resource.java @@ -14,13 +14,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jclouds.docker.config; +package org.jclouds.docker.domain; -public class DockerProperties { +import org.jclouds.json.SerializedNames; - /** - * default Docker host password - */ - public static final String HOST_PASSWORD = "jclouds.docker.host.password"; +import com.google.auto.value.AutoValue; +@AutoValue +public abstract class Resource { + + public abstract String resource(); + + Resource() { + } + + @SerializedNames({ "Resource" }) + public static Resource create(String resource) { + return new AutoValue_Resource(resource); + } } diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/State.java b/apis/docker/src/main/java/org/jclouds/docker/domain/State.java index c0a8586029..1b3c809af3 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/State.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/State.java @@ -32,11 +32,16 @@ public abstract class State { public abstract String finishedAt(); - public abstract boolean ghost(); + public abstract boolean paused(); - @SerializedNames({ "Pid", "Running", "ExitCode", "StartedAt", "FinishedAt", "Ghost" }) + public abstract boolean restarting(); + + State() { + } + + @SerializedNames({ "Pid", "Running", "ExitCode", "StartedAt", "FinishedAt", "Paused", "Restarting" }) public static State create(int pid, boolean running, int exitCode, String startedAt, String finishedAt, - boolean ghost) { - return new AutoValue_State(pid, running, exitCode, startedAt, finishedAt, ghost); + boolean paused, boolean restarting) { + return new AutoValue_State(pid, running, exitCode, startedAt, finishedAt, paused, restarting); } } diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/StatusCode.java b/apis/docker/src/main/java/org/jclouds/docker/domain/StatusCode.java new file mode 100644 index 0000000000..0bdfdc735c --- /dev/null +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/StatusCode.java @@ -0,0 +1,35 @@ +/* + * 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 StatusCode { + + public abstract int statusCode(); + + StatusCode() { + } + + @SerializedNames({ "StatusCode" }) + public static StatusCode create(int statusCode) { + return new AutoValue_StatusCode(statusCode); + } +} diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/Version.java b/apis/docker/src/main/java/org/jclouds/docker/domain/Version.java index 7cf7960067..9002d41103 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/Version.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/Version.java @@ -37,6 +37,9 @@ public abstract class Version { public abstract String version(); + 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) { diff --git a/apis/docker/src/main/java/org/jclouds/docker/features/ContainerApi.java b/apis/docker/src/main/java/org/jclouds/docker/features/ContainerApi.java index 60f7749564..fb592c8597 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/features/ContainerApi.java +++ b/apis/docker/src/main/java/org/jclouds/docker/features/ContainerApi.java @@ -16,6 +16,7 @@ */ package org.jclouds.docker.features; +import java.io.InputStream; import java.util.List; import javax.inject.Named; @@ -28,12 +29,16 @@ import javax.ws.rs.PathParam; import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; -import org.jclouds.Fallbacks; +import org.jclouds.Fallbacks.EmptyListOnNotFoundOr404; +import org.jclouds.Fallbacks.NullOnNotFoundOr404; 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.domain.Resource; +import org.jclouds.docker.domain.StatusCode; +import org.jclouds.docker.options.AttachOptions; import org.jclouds.docker.options.CommitOptions; import org.jclouds.docker.options.ListContainerOptions; import org.jclouds.docker.options.RemoveContainerOptions; @@ -42,34 +47,29 @@ import org.jclouds.rest.annotations.Fallback; import org.jclouds.rest.binders.BindToJsonPayload; @Consumes(MediaType.APPLICATION_JSON) +@Path("/v{jclouds.api-version}") public interface ContainerApi { /** - * List all running containers - * * @return a set of containers */ @Named("containers:list") @GET @Path("/containers/json") - @Fallback(Fallbacks.EmptyListOnNotFoundOr404.class) + @Fallback(EmptyListOnNotFoundOr404.class) List 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) + @Fallback(EmptyListOnNotFoundOr404.class) List 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 @@ -87,12 +87,10 @@ public interface ContainerApi { @Named("container:inspect") @GET @Path("/containers/{id}/json") - @Fallback(Fallbacks.NullOnNotFoundOr404.class) + @Fallback(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") @@ -101,8 +99,6 @@ public interface ContainerApi { 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) */ @@ -112,8 +108,6 @@ public interface ContainerApi { 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") @@ -122,8 +116,6 @@ public interface ContainerApi { 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 */ @@ -133,16 +125,28 @@ public interface ContainerApi { 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); + @Named("container:stop") + @POST + @Path("/containers/{id}/stop") + void stopContainer(@PathParam("id") String containerId, @QueryParam("t") int secondsToWait); + + /** + * Create a new image from a container’s changes + * + * @return a new image created from the current container's status. + */ + @Named("container:commit") + @POST + @Path("/commit") + Image commit(); + /** * Create a new image from a container’s changes * @@ -154,4 +158,95 @@ public interface ContainerApi { @Path("/commit") Image commit(CommitOptions options); + /** + * @param containerId The id of the container to be paused. + */ + @Named("container:pause") + @POST + @Path("/containers/{id}/pause") + void pause(@PathParam("id") String containerId); + + /** + * @param containerId The id of the container to be unpaused. + */ + @Named("container:unpause") + @POST + @Path("/containers/{id}/unpause") + void unpause(@PathParam("id") String containerId); + + /** + * @param containerId The id of the container to be attached. + */ + @Named("container:attach") + @POST + @Path("/containers/{id}/attach") + InputStream attach(@PathParam("id") String containerId); + + /** + * @param containerId The id of the container to be attached. + * @param options the attach options @see org.jclouds.docker.options.AttachOptions + * + */ + @Named("container:attach") + @POST + @Path("/containers/{id}/attach") + InputStream attach(@PathParam("id") String containerId, AttachOptions options); + + /** + * Block until container @param containerId stops, then returns the exit code + */ + @Named("container:wait") + @POST + @Path("/containers/{id}/wait") + StatusCode wait(@PathParam("id") String containerId); + + /** + * @param containerId restarts + */ + @Named("container:restart") + @POST + @Path("/containers/{id}/restart") + void restart(@PathParam("id") String containerId); + + @Named("container:restart") + @POST + @Path("/containers/{id}/restart") + void restart(@PathParam("id") String containerId, @QueryParam("t") int secondsToWait); + + + /** + * @param containerId to be killed + */ + @Named("container:kill") + @POST + @Path("/containers/{id}/kill") + void kill(@PathParam("id") String containerId); + + /** + * @param containerId to be killed + * @param signal Signal to send to the container. When not set, SIGKILL is assumed and the call will waits for the + * container to exit. + */ + @Named("container:kill") + @POST + @Path("/containers/{id}/kill") + void kill(@PathParam("id") String containerId, @QueryParam("signal") int signal); + + /** + * @param containerId to be killed + * @param signal Signal string like "SIGINT" to send to the container. When not set, SIGKILL is assumed and the call will waits for + * the container to exit. + */ + @Named("container:kill") + @POST + @Path("/containers/{id}/kill") + void kill(@PathParam("id") String containerId, @QueryParam("signal") String signal); + + /** + * @param containerId id of the container to copy files from + */ + @Named("container:copy") + @POST + @Path("/containers/{id}/copy") + InputStream copy(@PathParam("id") String containerId, @BinderParam(BindToJsonPayload.class) Resource resource); } diff --git a/apis/docker/src/main/java/org/jclouds/docker/features/ImageApi.java b/apis/docker/src/main/java/org/jclouds/docker/features/ImageApi.java index 95c963c88d..9f7cebc0fd 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/features/ImageApi.java +++ b/apis/docker/src/main/java/org/jclouds/docker/features/ImageApi.java @@ -28,49 +28,48 @@ import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.core.MediaType; -import org.jclouds.Fallbacks; +import org.jclouds.Fallbacks.EmptyListOnNotFoundOr404; +import org.jclouds.Fallbacks.NullOnNotFoundOr404; import org.jclouds.docker.domain.Image; +import org.jclouds.docker.domain.ImageSummary; import org.jclouds.docker.options.CreateImageOptions; import org.jclouds.docker.options.DeleteImageOptions; import org.jclouds.docker.options.ListImageOptions; +import org.jclouds.javax.annotation.Nullable; import org.jclouds.rest.annotations.Fallback; @Consumes(MediaType.APPLICATION_JSON) +@Path("/v{jclouds.api-version}") public interface ImageApi { /** - * List images - * * @return the images available. */ @Named("images:list") @GET @Path("/images/json") - @Fallback(Fallbacks.EmptyListOnNotFoundOr404.class) - List listImages(); + @Fallback(EmptyListOnNotFoundOr404.class) + List 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 listImages(ListImageOptions options); + @Fallback(EmptyListOnNotFoundOr404.class) + List 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) + @Fallback(NullOnNotFoundOr404.class) + @Nullable Image inspectImage(@PathParam("name") String imageName); /** @@ -85,8 +84,6 @@ public interface ImageApi { InputStream createImage(CreateImageOptions options); /** - * Delete an image. - * * @param name the image name to be deleted * @return the stream of the deletion execution. */ @@ -96,8 +93,6 @@ public interface ImageApi { 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. diff --git a/apis/docker/src/main/java/org/jclouds/docker/features/MiscApi.java b/apis/docker/src/main/java/org/jclouds/docker/features/MiscApi.java index 30cd1f31a9..6c49436632 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/features/MiscApi.java +++ b/apis/docker/src/main/java/org/jclouds/docker/features/MiscApi.java @@ -32,6 +32,7 @@ import org.jclouds.io.Payload; import org.jclouds.rest.annotations.Headers; @Consumes(MediaType.APPLICATION_JSON) +@Path("/v{jclouds.api-version}") public interface MiscApi { /** @@ -54,6 +55,18 @@ public interface MiscApi { @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. + * @return a stream of the build execution + */ + @Named("image:build") + @POST + @Path("/build") + @Headers(keys = "Content-Type", values = "application/tar") + InputStream build(Payload inputStream); /** * Build an image from Dockerfile via stdin diff --git a/apis/docker/src/main/java/org/jclouds/docker/options/AttachOptions.java b/apis/docker/src/main/java/org/jclouds/docker/options/AttachOptions.java new file mode 100644 index 0000000000..95fb8b593c --- /dev/null +++ b/apis/docker/src/main/java/org/jclouds/docker/options/AttachOptions.java @@ -0,0 +1,114 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.options; + +import org.jclouds.http.options.BaseHttpRequestOptions; + +public class AttachOptions extends BaseHttpRequestOptions { + + /** + * @param stream When TTY is enabled, the stream is the raw data from the process PTY and client's stdin. + * When TTY is disabled, the stream is multiplexed to separate stdout and stderr. + * @return AttachOptions + */ + public AttachOptions stream(Boolean stream) { + this.queryParameters.put("stream", stream.toString()); + return this; + } + + /** + * @param logs require logs to be attached. Default false. + * @return AttachOptions + */ + public AttachOptions logs(Boolean logs) { + this.queryParameters.put("logs", logs.toString()); + return this; + } + + /** + * @param stdin if stream=true, attach to stdin. Default false + * @return AttachOptions + */ + public AttachOptions stdin(Boolean stdin) { + this.queryParameters.put("stdin", stdin.toString()); + return this; + } + + /** + * @param stdout if logs=true, return stdout log, if stream=true, attach to stdout. Default false + * @return + */ + public AttachOptions stdout(Boolean stdout) { + this.queryParameters.put("stdout", stdout.toString()); + return this; + } + + /** + * + * @param stderr if logs=true, return stderr log, if stream=true, attach to stderr. Default false + * @return + */ + public AttachOptions stderr(Boolean stderr) { + this.queryParameters.put("stderr", stderr.toString()); + return this; + } + + public static class Builder { + + /** + * @see org.jclouds.docker.options.AttachOptions#stream + */ + public static AttachOptions stream(Boolean stream) { + AttachOptions options = new AttachOptions(); + return options.stream(stream); + } + + /** + * @see org.jclouds.docker.options.AttachOptions#logs(Boolean) + */ + public static AttachOptions logs(Boolean logs) { + AttachOptions options = new AttachOptions(); + return options.logs(logs); + } + + /** + * @see org.jclouds.docker.options.AttachOptions#stdin(Boolean) + */ + public static AttachOptions stdin(Boolean stdin) { + AttachOptions options = new AttachOptions(); + return options.stdin(stdin); + } + + /** + * @see org.jclouds.docker.options.AttachOptions#stdout(Boolean) + */ + public static AttachOptions stdout(Boolean stdout) { + AttachOptions options = new AttachOptions(); + return options.stdout(stdout); + } + + /** + * @see org.jclouds.docker.options.AttachOptions#stderr(Boolean) + */ + public static AttachOptions stderr(Boolean stderr) { + AttachOptions options = new AttachOptions(); + return options.stderr(stderr); + } + + } + +} diff --git a/apis/docker/src/main/java/org/jclouds/docker/options/BuildOptions.java b/apis/docker/src/main/java/org/jclouds/docker/options/BuildOptions.java index 4d7196ceee..14bc4a4829 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/options/BuildOptions.java +++ b/apis/docker/src/main/java/org/jclouds/docker/options/BuildOptions.java @@ -18,13 +18,8 @@ package org.jclouds.docker.options; import org.jclouds.http.options.BaseHttpRequestOptions; -/** - * Options to customize image builder. - */ public class BuildOptions extends BaseHttpRequestOptions { - public static final BuildOptions NONE = new BuildOptions(); - public BuildOptions tag(String tag) { this.queryParameters.put("tag", tag); return this; diff --git a/apis/docker/src/main/java/org/jclouds/docker/options/CommitOptions.java b/apis/docker/src/main/java/org/jclouds/docker/options/CommitOptions.java index 5653fba57e..fbf35b6c3d 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/options/CommitOptions.java +++ b/apis/docker/src/main/java/org/jclouds/docker/options/CommitOptions.java @@ -18,13 +18,8 @@ package org.jclouds.docker.options; import org.jclouds.http.options.BaseHttpRequestOptions; -/** - * Options to customize image commit. - */ public class CommitOptions extends BaseHttpRequestOptions { - public static final CommitOptions NONE = new CommitOptions(); - public CommitOptions containerId(String containerId) { this.queryParameters.put("containerId", containerId); return this; diff --git a/apis/docker/src/main/java/org/jclouds/docker/options/CreateImageOptions.java b/apis/docker/src/main/java/org/jclouds/docker/options/CreateImageOptions.java index 51dc399d4d..91b8413c27 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/options/CreateImageOptions.java +++ b/apis/docker/src/main/java/org/jclouds/docker/options/CreateImageOptions.java @@ -18,13 +18,8 @@ package org.jclouds.docker.options; import org.jclouds.http.options.BaseHttpRequestOptions; -/** - * Options to customize container creation. - */ public class CreateImageOptions extends BaseHttpRequestOptions { - public static final CreateImageOptions NONE = new CreateImageOptions(); - public CreateImageOptions fromImage(String fromImage) { this.queryParameters.put("fromImage", fromImage); return this; diff --git a/apis/docker/src/main/java/org/jclouds/docker/options/DeleteImageOptions.java b/apis/docker/src/main/java/org/jclouds/docker/options/DeleteImageOptions.java index 9438616b56..1ac20d98e8 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/options/DeleteImageOptions.java +++ b/apis/docker/src/main/java/org/jclouds/docker/options/DeleteImageOptions.java @@ -18,18 +18,18 @@ package org.jclouds.docker.options; import org.jclouds.http.options.BaseHttpRequestOptions; -/** - * Options to customize image deletion. - */ public class DeleteImageOptions extends BaseHttpRequestOptions { - public static final DeleteImageOptions NONE = new DeleteImageOptions(); - public DeleteImageOptions force(Boolean force) { this.queryParameters.put("force", force.toString()); return this; } + public DeleteImageOptions noPrune(Boolean noPrune) { + this.queryParameters.put("noPrune", noPrune.toString()); + return this; + } + public static class Builder { /** @@ -39,6 +39,14 @@ public class DeleteImageOptions extends BaseHttpRequestOptions { DeleteImageOptions options = new DeleteImageOptions(); return options.force(force); } + + /** + * @see DeleteImageOptions#noPrune + */ + public static DeleteImageOptions noPrune(Boolean noPrune) { + DeleteImageOptions options = new DeleteImageOptions(); + return options.noPrune(noPrune); + } } } diff --git a/apis/docker/src/main/java/org/jclouds/docker/options/ListContainerOptions.java b/apis/docker/src/main/java/org/jclouds/docker/options/ListContainerOptions.java index af16664010..7f73804c65 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/options/ListContainerOptions.java +++ b/apis/docker/src/main/java/org/jclouds/docker/options/ListContainerOptions.java @@ -18,13 +18,8 @@ package org.jclouds.docker.options; import org.jclouds.http.options.BaseHttpRequestOptions; -/** - * Options to customize container's listing. - */ public class ListContainerOptions extends BaseHttpRequestOptions { - public static final ListContainerOptions NONE = new ListContainerOptions(); - public ListContainerOptions all(Boolean all) { this.queryParameters.put("all", all.toString()); return this; diff --git a/apis/docker/src/main/java/org/jclouds/docker/options/ListImageOptions.java b/apis/docker/src/main/java/org/jclouds/docker/options/ListImageOptions.java index fab75d4233..613eff63ec 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/options/ListImageOptions.java +++ b/apis/docker/src/main/java/org/jclouds/docker/options/ListImageOptions.java @@ -18,13 +18,8 @@ package org.jclouds.docker.options; import org.jclouds.http.options.BaseHttpRequestOptions; -/** - * Options to customize image's listing. - */ public class ListImageOptions extends BaseHttpRequestOptions { - public static final ListImageOptions NONE = new ListImageOptions(); - public ListImageOptions all(Boolean all) { this.queryParameters.put("all", all.toString()); return this; diff --git a/apis/docker/src/main/java/org/jclouds/docker/options/RemoveContainerOptions.java b/apis/docker/src/main/java/org/jclouds/docker/options/RemoveContainerOptions.java index 5c3abba3a3..9334e69a4c 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/options/RemoveContainerOptions.java +++ b/apis/docker/src/main/java/org/jclouds/docker/options/RemoveContainerOptions.java @@ -18,13 +18,8 @@ package org.jclouds.docker.options; import org.jclouds.http.options.BaseHttpRequestOptions; -/** - * Options to customize container removal. - */ public class RemoveContainerOptions extends BaseHttpRequestOptions { - public static final RemoveContainerOptions NONE = new RemoveContainerOptions(); - public RemoveContainerOptions verbose(Boolean verbose) { this.queryParameters.put("verbose", verbose.toString()); return this; diff --git a/apis/docker/src/main/java/org/jclouds/docker/suppliers/KeyStoreSupplier.java b/apis/docker/src/main/java/org/jclouds/docker/suppliers/KeyStoreSupplier.java deleted file mode 100644 index e8643cd9aa..0000000000 --- a/apis/docker/src/main/java/org/jclouds/docker/suppliers/KeyStoreSupplier.java +++ /dev/null @@ -1,130 +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.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 { - private final Crypto crypto; - private final Supplier creds; - - @Inject - KeyStoreSupplier(Crypto crypto, @Provider Supplier 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 certs = (Collection) 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); - } - } -} diff --git a/apis/docker/src/main/java/org/jclouds/docker/suppliers/SSLContextWithKeysSupplier.java b/apis/docker/src/main/java/org/jclouds/docker/suppliers/SSLContextWithKeysSupplier.java index 59695d34bd..7aeee187e3 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/suppliers/SSLContextWithKeysSupplier.java +++ b/apis/docker/src/main/java/org/jclouds/docker/suppliers/SSLContextWithKeysSupplier.java @@ -18,60 +18,146 @@ 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.IOException; +import java.io.StringReader; +import java.net.Socket; import java.security.KeyManagementException; -import java.security.KeyStore; -import java.security.KeyStoreException; +import java.security.KeyPair; import java.security.NoSuchAlgorithmException; +import java.security.Principal; +import java.security.PrivateKey; import java.security.SecureRandom; -import java.security.UnrecoverableKeyException; +import java.security.Security; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; import javax.inject.Inject; import javax.inject.Singleton; -import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.KeyManager; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; +import javax.net.ssl.X509ExtendedKeyManager; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.openssl.PEMKeyPair; +import org.bouncycastle.openssl.PEMParser; +import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; 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.Charsets; import com.google.common.base.Supplier; +import com.google.common.io.Files; @Singleton public class SSLContextWithKeysSupplier implements Supplier { - private final Supplier keyStore; private final TrustManager[] trustManager; private final Supplier creds; @Inject - SSLContextWithKeysSupplier(Supplier keyStore, @Provider Supplier creds, HttpUtils utils, - TrustAllCerts trustAllCerts) { - this.keyStore = keyStore; - this.trustManager = utils.trustAllCerts() ? new TrustManager[] { trustAllCerts } : null; + SSLContextWithKeysSupplier(@Provider Supplier creds, HttpUtils utils, TrustAllCerts trustAllCerts) { + 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; + SSLContext sslContext = SSLContext.getInstance("TLS"); + X509Certificate certificate = getCertificate(loadFile(currentCreds.identity)); + PrivateKey privateKey = getKey(loadFile(currentCreds.credential)); + sslContext.init(new KeyManager[]{new InMemoryKeyManager(certificate, privateKey)}, trustManager, new SecureRandom()); + return sslContext; } catch (NoSuchAlgorithmException e) { throw propagate(e); - } catch (UnrecoverableKeyException e) { - throw propagate(e); - } catch (KeyStoreException e) { - throw propagate(e); } catch (KeyManagementException e) { throw propagate(e); + } catch (CertificateException e) { + throw propagate(e); + } catch (IOException e) { + throw propagate(e); } } + + private static X509Certificate getCertificate(String certificate) { + try { + return (X509Certificate) CertificateFactory.getInstance("X.509").generateCertificate( + new ByteArrayInputStream(certificate.getBytes(Charsets.UTF_8))); + } catch (CertificateException ex) { + throw new RuntimeException("Invalid certificate", ex); + } + } + + private static PrivateKey getKey(String privateKey) { + + try { + PEMParser pemParser = new PEMParser(new StringReader(privateKey)); + Object object = pemParser.readObject(); + if (Security.getProvider("BC") == null) { + Security.addProvider(new BouncyCastleProvider()); + } + JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC"); + KeyPair keyPair = converter.getKeyPair((PEMKeyPair) object); + return keyPair.getPrivate(); + } catch (IOException ex) { + throw new RuntimeException("Invalid private key", ex); + } + } + + private static String loadFile(final String filePath) throws IOException { + return Files.toString(new File(filePath), Charsets.UTF_8); + } + + private static class InMemoryKeyManager extends X509ExtendedKeyManager { + private static final String DEFAULT_ALIAS = "docker"; + + private final X509Certificate certificate; + + private final PrivateKey privateKey; + + public InMemoryKeyManager(final X509Certificate certificate, final PrivateKey privateKey) + throws IOException, CertificateException { + this.certificate = certificate; + this.privateKey = privateKey; + } + + @Override + public String chooseClientAlias(final String[] keyType, final Principal[] issuers, + final Socket socket) { + return DEFAULT_ALIAS; + } + + @Override + public String chooseServerAlias(final String keyType, final Principal[] issuers, + final Socket socket) { + return DEFAULT_ALIAS; + } + + @Override + public X509Certificate[] getCertificateChain(final String alias) { + return new X509Certificate[]{certificate}; + } + + @Override + public String[] getClientAliases(final String keyType, final Principal[] issuers) { + return new String[]{DEFAULT_ALIAS}; + } + + @Override + public PrivateKey getPrivateKey(final String alias) { + return privateKey; + } + + @Override + public String[] getServerAliases(final String keyType, final Principal[] issuers) { + return new String[]{DEFAULT_ALIAS}; + } + } + } diff --git a/apis/docker/src/test/java/org/jclouds/docker/compute/BaseDockerApiLiveTest.java b/apis/docker/src/test/java/org/jclouds/docker/compute/BaseDockerApiLiveTest.java index 9aca266574..447c9e4d68 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/compute/BaseDockerApiLiveTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/compute/BaseDockerApiLiveTest.java @@ -29,7 +29,6 @@ import org.jboss.shrinkwrap.api.GenericArchive; import org.jboss.shrinkwrap.api.ShrinkWrap; 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; @@ -57,8 +56,7 @@ public class BaseDockerApiLiveTest extends BaseApiLiveTest { @Override protected Properties setupProperties() { Properties overrides = super.setupProperties(); - overrides.setProperty(Constants.PROPERTY_MAX_RETRIES, "15"); - overrides.setProperty("jclouds.ssh.retry-auth", "true"); + overrides.setProperty("jclouds.trust-all-certs", "false"); overrides.setProperty(ComputeServiceProperties.IMAGE_LOGIN_USER, "root:password"); return overrides; } diff --git a/apis/docker/src/test/java/org/jclouds/docker/compute/DockerComputeServiceAdapterLiveTest.java b/apis/docker/src/test/java/org/jclouds/docker/compute/DockerComputeServiceAdapterLiveTest.java index c3da551d91..9aae9bffa4 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/compute/DockerComputeServiceAdapterLiveTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/compute/DockerComputeServiceAdapterLiveTest.java @@ -116,10 +116,4 @@ public class DockerComputeServiceAdapterLiveTest extends BaseDockerApiLiveTest { return ImmutableSet.of(getLoggingModule(), new SshjSshClientModule()); } - @Override - protected Properties setupProperties() { - Properties properties = super.setupProperties(); - properties.setProperty("jclouds.ssh.max-retries", "10"); - return properties; - } } diff --git a/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadataTest.java b/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadataTest.java index 94ee205fb1..850863c1af 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadataTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadataTest.java @@ -34,6 +34,7 @@ import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.compute.domain.OperatingSystem; import org.jclouds.compute.domain.OsFamily; import org.jclouds.compute.functions.GroupNamingConvention; +import org.jclouds.date.internal.SimpleDateFormatDateService; import org.jclouds.docker.domain.Config; import org.jclouds.docker.domain.Container; import org.jclouds.docker.domain.HostConfig; @@ -97,12 +98,13 @@ public class ContainerToNodeMetadataTest { 0, // exitCode "2014-03-24T20:28:37.537659054Z", // startedAt "0001-01-01T00:00:00Z", // finishedAt - false // ghost + false, // paused + false // restarting ); container = Container.builder() .id("6d35806c1bd2b25cd92bba2d2c2c5169dc2156f53ab45c2b62d76e2d2fee14a9") .name("/hopeful_mclean") - .created("2014-03-22T07:16:45.784120972Z") + .created(new SimpleDateFormatDateService().iso8601DateParse("2014-03-22T07:16:45.784120972Z")) .path("/usr/sbin/sshd") .args(Arrays.asList("-D")) .config(containerConfig) diff --git a/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ImageToImageTest.java b/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ImageToImageTest.java index 76d82ee753..db02e3dfd5 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ImageToImageTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ImageToImageTest.java @@ -21,8 +21,11 @@ import static org.easymock.EasyMock.replay; import static org.easymock.EasyMock.verify; import static org.testng.Assert.assertEquals; +import java.util.Date; + import org.easymock.EasyMock; import org.jclouds.compute.domain.Image; +import org.jclouds.docker.domain.Config; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; @@ -41,16 +44,24 @@ public class ImageToImageTest { @BeforeMethod public void setup() { image = org.jclouds.docker.domain.Image.create( - "id", // id - "parent", // parent - "created", // created - null, // container - null, // dockerVersion - "x86_64", // architecture - null, // os - 0l, // size - 0l, // virtualSize - ImmutableList.of("repoTag1:version") // repoTags + "id", // id + "author", + "comment", + Config.builder() + .image("imageId") + .build(), + Config.builder() + .image("imageId") + .build(), + "parent", // parent + new Date(), // created + "containerId", // container + "1.3.1", // dockerVersion + "x86_64", // architecture + "os", // os + 0l, // size + 0l, // virtualSize + ImmutableList.of("repoTag1:version") // repoTags ); function = new ImageToImage(); } diff --git a/apis/docker/src/test/java/org/jclouds/docker/features/ContainerApiLiveTest.java b/apis/docker/src/test/java/org/jclouds/docker/features/ContainerApiLiveTest.java index 44edc573b9..684f5debb9 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/features/ContainerApiLiveTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/features/ContainerApiLiveTest.java @@ -16,8 +16,11 @@ */ package org.jclouds.docker.features; +import static org.assertj.core.api.Assertions.assertThat; +import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertNull; import static org.testng.Assert.assertTrue; import java.io.IOException; @@ -29,8 +32,11 @@ 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.domain.Resource; +import org.jclouds.docker.options.AttachOptions; import org.jclouds.docker.options.CreateImageOptions; import org.jclouds.docker.options.ListContainerOptions; +import org.jclouds.docker.options.RemoveContainerOptions; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; @@ -57,6 +63,11 @@ public class ContainerApiLiveTest extends BaseDockerApiLiveTest { @AfterClass protected void tearDown() { + if (container != null) { + if (api.getContainerApi().inspectContainer(container.id()) != null) { + api.getContainerApi().removeContainer(container.id(), RemoveContainerOptions.Builder.force(true)); + } + } if (image != null) { api.getImageApi().deleteImage(BUSYBOX_IMAGE_TAG); } @@ -64,7 +75,7 @@ public class ContainerApiLiveTest extends BaseDockerApiLiveTest { 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")) + .cmd(ImmutableList.of("/bin/sh", "-c", "touch hello; while true; do echo hello world; sleep 1; done")) .build(); container = api().createContainer("testCreateContainer", containerConfig); assertNotNull(container); @@ -78,11 +89,54 @@ public class ContainerApiLiveTest extends BaseDockerApiLiveTest { } @Test(dependsOnMethods = "testStartContainer") + public void testAttachContainer() { + InputStream attachStream = api().attach(container.id(), AttachOptions.Builder.logs(true).stream(false).stdout(true)); + String stream = consumeStream(attachStream); + assertThat(stream.trim()).contains("hello world"); + } + + @Test(dependsOnMethods = "testAttachContainer") + public void testCopyFileFromContainer() { + InputStream tarredStream = api().copy(container.id(), Resource.create("hello")); + assertNotNull(consumeStream(tarredStream)); + } + + @Test(dependsOnMethods = "testCopyFileFromContainer") + public void testPauseContainer() { + api().pause(container.id()); + assertTrue(api().inspectContainer(container.id()).state().paused()); + } + + @Test(dependsOnMethods = "testPauseContainer") + public void testUnpauseContainer() { + api().unpause(container.id()); + assertFalse(api().inspectContainer(container.id()).state().paused()); + } + + @Test(dependsOnMethods = "testUnpauseContainer") public void testStopContainer() { api().stopContainer(container.id()); assertFalse(api().inspectContainer(container.id()).state().running()); } + @Test(dependsOnMethods = "testStopContainer") + public void testRestartContainer() { + api().restart(container.id()); + assertTrue(api().inspectContainer(container.id()).state().running()); + } + + @Test(dependsOnMethods = "testRestartContainer") + public void testWaitContainer() { + api().stopContainer(container.id(), 1); + assertEquals(api().wait(container.id()).statusCode(), -1); + } + + @Test(dependsOnMethods = "testWaitContainer") + public void testRemoveContainer() { + api().removeContainer(container.id()); + assertNull(api().inspectContainer(container.id())); + } + @Test public void testListContainers() { List containerSummaries = api().listContainers(ListContainerOptions.Builder.all(true)); @@ -93,12 +147,6 @@ public class ContainerApiLiveTest extends BaseDockerApiLiveTest { } } - @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(); } diff --git a/apis/docker/src/test/java/org/jclouds/docker/features/ContainerApiMockTest.java b/apis/docker/src/test/java/org/jclouds/docker/features/ContainerApiMockTest.java index 4bbff66fb6..ab3657b107 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/features/ContainerApiMockTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/features/ContainerApiMockTest.java @@ -16,23 +16,22 @@ */ package org.jclouds.docker.features; +import static org.assertj.core.api.Assertions.assertThat; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.fail; -import java.util.List; import org.jclouds.docker.DockerApi; +import org.jclouds.docker.config.DockerParserModule; import org.jclouds.docker.domain.Config; import org.jclouds.docker.domain.Container; -import org.jclouds.docker.domain.ContainerSummary; +import org.jclouds.docker.domain.Resource; import org.jclouds.docker.internal.BaseDockerMockTest; import org.jclouds.docker.options.ListContainerOptions; -import org.jclouds.rest.ResourceNotFoundException; +import org.jclouds.docker.parse.ContainerParseTest; +import org.jclouds.docker.parse.ContainersParseTest; import org.testng.annotations.Test; import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMultimap; import com.squareup.okhttp.mockwebserver.MockResponse; import com.squareup.okhttp.mockwebserver.MockWebServer; @@ -43,96 +42,56 @@ import com.squareup.okhttp.mockwebserver.MockWebServer; public class ContainerApiMockTest extends BaseDockerMockTest { public void testListContainers() throws Exception { - MockWebServer server = mockWebServer(); - server.enqueue(new MockResponse().setBody(payloadFromResource("/containers.json"))); - - DockerApi dockerApi = api(server.getUrl("/")); - ContainerApi api = dockerApi.getContainerApi(); - + MockWebServer server = mockWebServer(new MockResponse().setBody(payloadFromResource("/containers.json"))); + ContainerApi api = api(DockerApi.class, server.getUrl("/").toString()).getContainerApi(); try { - List containerSummaries = api.listContainers(); - assertRequestHasCommonFields(server.takeRequest(), "/containers/json"); - assertEquals(containerSummaries.size(), 1); + assertEquals(api.listContainers(), new ContainersParseTest().expected()); + assertSent(server, "GET", "/containers/json"); } finally { - dockerApi.close(); server.shutdown(); } } public void testListNonexistentContainers() throws Exception { - MockWebServer server = mockWebServer(); - server.enqueue(new MockResponse().setResponseCode(404)); - DockerApi dockerApi = api(server.getUrl("/")); - ContainerApi api = dockerApi.getContainerApi(); + MockWebServer server = mockWebServer(new MockResponse().setResponseCode(404)); + ContainerApi api = api(DockerApi.class, server.getUrl("/").toString()).getContainerApi(); try { - List containerSummaries = api.listContainers(); - assertRequestHasCommonFields(server.takeRequest(), "/containers/json"); - assertTrue(containerSummaries.isEmpty()); + assertEquals(api.listContainers(), ImmutableList.of()); + assertSent(server, "GET", "/containers/json"); } finally { - dockerApi.close(); server.shutdown(); } } - @Test(timeOut = 10000l) public void testListAllContainers() throws Exception { - MockWebServer server = mockWebServer(); - server.enqueue(new MockResponse().setBody(payloadFromResource("/containers.json"))); - DockerApi dockerApi = api(server.getUrl("/")); - ContainerApi api = dockerApi.getContainerApi(); + MockWebServer server = mockWebServer(new MockResponse().setBody(payloadFromResource("/containers.json"))); + ContainerApi api = api(DockerApi.class, server.getUrl("/").toString()).getContainerApi(); try { - List containerSummaries = api.listContainers(ListContainerOptions.Builder.all(true)); - assertRequestHasParameters(server.takeRequest(), "/containers/json", ImmutableMultimap.of("all", "true")); - assertEquals(containerSummaries.size(), 1); + assertEquals(api.listContainers(ListContainerOptions.Builder.all(true)), new ContainersParseTest().expected()); + assertSent(server, "GET", "/containers/json?all=true"); } finally { - dockerApi.close(); server.shutdown(); } } public void testGetContainer() throws Exception { - MockWebServer server = mockWebServer(); - server.enqueue(new MockResponse().setBody(payloadFromResource("/container.json"))); - DockerApi dockerApi = api(server.getUrl("/")); - ContainerApi api = dockerApi.getContainerApi(); + MockWebServer server = mockWebServer(new MockResponse().setBody(payloadFromResource("/container.json"))); + ContainerApi api = api(DockerApi.class, server.getUrl("/").toString(), new DockerParserModule()).getContainerApi(); String containerId = "b03d4cd15b76f8876110615cdeed15eadf77c9beb408d62f1687dcc69192cd6d"; try { - Container container = api.inspectContainer(containerId); - assertRequestHasCommonFields(server.takeRequest(), "/containers/" + containerId + "/json"); - assertNotNull(container); - assertNotNull(container.id(), containerId); - assertNotNull(container.config()); - assertNotNull(container.hostConfig()); - assertTrue(container.name().contains("/weave")); - assertEquals(container.state().running(), true); + assertEquals(api.inspectContainer(containerId), new ContainerParseTest().expected()); + assertSent(server, "GET", "/containers/" + containerId + "/json"); } finally { - dockerApi.close(); - server.shutdown(); - } - } - - public void testGetNonExistingContainer() throws Exception { - MockWebServer server = mockWebServer(); - server.enqueue(new MockResponse().setResponseCode(404)); - DockerApi dockerApi = api(server.getUrl("/")); - ContainerApi api = dockerApi.getContainerApi(); - String containerId = "notExisting"; - try { - Container container = api.inspectContainer(containerId); - assertRequestHasCommonFields(server.takeRequest(), "/containers/" + containerId + "/json"); - } finally { - dockerApi.close(); server.shutdown(); } } public void testCreateContainer() throws Exception { - MockWebServer server = mockWebServer(); - server.enqueue(new MockResponse().setBody(payloadFromResource("/container-creation.json"))); - DockerApi dockerApi = api(server.getUrl("/")); - ContainerApi api = dockerApi.getContainerApi(); - Config containerConfig = Config.builder().cmd(ImmutableList.of("date")) + MockWebServer server = mockWebServer(new MockResponse().setBody(payloadFromResource("/container-creation.json"))); + ContainerApi api = api(DockerApi.class, server.getUrl("/").toString()).getContainerApi(); + Config containerConfig = Config.builder() + .cmd(ImmutableList.of("date")) .attachStdin(false) .attachStderr(true) .attachStdout(true) @@ -141,106 +100,135 @@ public class ContainerApiMockTest extends BaseDockerMockTest { .build(); try { Container container = api.createContainer("test", containerConfig); - assertRequestHasCommonFields(server.takeRequest(), "POST", "/containers/create?name=test"); + assertSent(server, "POST", "/containers/create?name=test"); assertNotNull(container); - assertEquals(container.id(), "c6c74153ae4b1d1633d68890a68d89c40aa5e284a1ea016cbc6ef0e634ee37b2"); + assertThat(container.id()).isEqualTo("c6c74153ae4b1d1633d68890a68d89c40aa5e284a1ea016cbc6ef0e634ee37b2"); } finally { - dockerApi.close(); server.shutdown(); } } public void testRemoveContainer() throws Exception { - MockWebServer server = mockWebServer(); - server.enqueue(new MockResponse().setResponseCode(204)); - DockerApi dockerApi = api(server.getUrl("/")); - ContainerApi api = dockerApi.getContainerApi(); + MockWebServer server = mockWebServer(new MockResponse().setResponseCode(204)); + ContainerApi api = api(DockerApi.class, server.getUrl("/").toString()).getContainerApi(); String containerId = "6d35806c1bd2b25cd92bba2d2c2c5169dc2156f53ab45c2b62d76e2d2fee14a9"; try { api.removeContainer(containerId); - assertRequestHasCommonFields(server.takeRequest(), "DELETE", "/containers/" + containerId); - } finally { - dockerApi.close(); - server.shutdown(); - } - } + assertSent(server, "DELETE", "/containers/" + containerId); - public void testRemoveNonExistingContainer() throws Exception { - MockWebServer server = mockWebServer(); - server.enqueue(new MockResponse().setResponseCode(404)); - DockerApi dockerApi = api(server.getUrl("/")); - ContainerApi api = dockerApi.getContainerApi(); - String containerId = "nonExisting"; - try { - api.removeContainer(containerId); - fail("Remove container must fail on 404"); - } catch (ResourceNotFoundException ex) { - // Expected exception } finally { - dockerApi.close(); server.shutdown(); } } public void testStartContainer() throws Exception { - MockWebServer server = mockWebServer(); - server.enqueue(new MockResponse().setResponseCode(200)); - DockerApi dockerApi = api(server.getUrl("/")); - ContainerApi api = dockerApi.getContainerApi(); + MockWebServer server = mockWebServer(new MockResponse().setResponseCode(200)); + ContainerApi api = api(DockerApi.class, server.getUrl("/").toString()).getContainerApi(); try { api.startContainer("1"); - assertRequestHasCommonFields(server.takeRequest(), "POST", "/containers/1/start"); + assertSent(server, "POST", "/containers/1/start"); } finally { - dockerApi.close(); - server.shutdown(); - } - } - - public void testStartNonExistingContainer() throws Exception { - MockWebServer server = mockWebServer(); - server.enqueue(new MockResponse().setResponseCode(404)); - DockerApi dockerApi = api(server.getUrl("/")); - ContainerApi api = dockerApi.getContainerApi(); - try { - try { - api.startContainer("1"); - fail("Start container must fail on 404"); - } catch (ResourceNotFoundException ex) { - // Expected exception - } - } finally { - dockerApi.close(); server.shutdown(); } } public void testStopContainer() throws Exception { - MockWebServer server = mockWebServer(); - server.enqueue(new MockResponse().setResponseCode(200)); - DockerApi dockerApi = api(server.getUrl("/")); - ContainerApi api = dockerApi.getContainerApi(); + MockWebServer server = mockWebServer(new MockResponse().setResponseCode(200)); + ContainerApi api = api(DockerApi.class, server.getUrl("/").toString()).getContainerApi(); try { api.stopContainer("1"); - assertRequestHasCommonFields(server.takeRequest(), "POST", "/containers/1/stop"); + assertSent(server, "POST", "/containers/1/stop"); } finally { - dockerApi.close(); server.shutdown(); } } - public void testStopNonExistingContainer() throws Exception { - MockWebServer server = mockWebServer(); - server.enqueue(new MockResponse().setResponseCode(404)); - DockerApi dockerApi = api(server.getUrl("/")); - ContainerApi api = dockerApi.getContainerApi(); + + public void testCommitContainer() throws Exception { + MockWebServer server = mockWebServer(new MockResponse().setResponseCode(201)); + ContainerApi api = api(DockerApi.class, server.getUrl("/").toString()).getContainerApi(); try { - api.stopContainer("1"); - fail("Stop container must fail on 404"); - } catch (ResourceNotFoundException ex) { - // Expected exception + api.commit(); + assertSent(server, "POST", "/commit"); + } finally { + server.shutdown(); + } + } + + public void testPauseContainer() throws Exception { + MockWebServer server = mockWebServer(new MockResponse().setResponseCode(204)); + ContainerApi api = api(DockerApi.class, server.getUrl("/").toString()).getContainerApi(); + try { + api.pause("1"); + assertSent(server, "POST", "/containers/1/pause"); + } finally { + server.shutdown(); + } + } + + public void testUnpauseContainer() throws Exception { + MockWebServer server = mockWebServer(new MockResponse().setResponseCode(204)); + ContainerApi api = api(DockerApi.class, server.getUrl("/").toString()).getContainerApi(); + try { + api.unpause("1"); + assertSent(server, "POST", "/containers/1/unpause"); + } finally { + server.shutdown(); + } + } + + public void testAttachContainer() throws Exception { + MockWebServer server = mockWebServer(new MockResponse().setResponseCode(200)); + ContainerApi api = api(DockerApi.class, server.getUrl("/").toString()).getContainerApi(); + try { + api.attach("1"); + assertSent(server, "POST", "/containers/1/attach"); + } finally { + server.shutdown(); + } + } + + public void testWaitContainer() throws Exception { + MockWebServer server = mockWebServer(new MockResponse().setResponseCode(200)); + ContainerApi api = api(DockerApi.class, server.getUrl("/").toString()).getContainerApi(); + try { + api.wait("1"); + assertSent(server, "POST", "/containers/1/wait"); + } finally { + server.shutdown(); + } + } + + public void testRestartContainer() throws Exception { + MockWebServer server = mockWebServer(new MockResponse().setResponseCode(204)); + ContainerApi api = api(DockerApi.class, server.getUrl("/").toString()).getContainerApi(); + try { + api.restart("1"); + assertSent(server, "POST", "/containers/1/restart"); + } finally { + server.shutdown(); + } + } + + public void testKillContainer() throws Exception { + MockWebServer server = mockWebServer(new MockResponse().setResponseCode(204)); + ContainerApi api = api(DockerApi.class, server.getUrl("/").toString()).getContainerApi(); + try { + api.kill("1"); + assertSent(server, "POST", "/containers/1/kill"); + } finally { + server.shutdown(); + } + } + + public void testCopyFileFromContainer() throws Exception { + MockWebServer server = mockWebServer(new MockResponse().setResponseCode(204)); + ContainerApi api = api(DockerApi.class, server.getUrl("/").toString()).getContainerApi(); + try { + api.copy("1", Resource.create("test")); + assertSent(server, "POST", "/containers/1/copy"); } finally { - dockerApi.close(); server.shutdown(); } } diff --git a/apis/docker/src/test/java/org/jclouds/docker/features/ImageApiMockTest.java b/apis/docker/src/test/java/org/jclouds/docker/features/ImageApiMockTest.java index 7ad6414f52..5349c39c70 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/features/ImageApiMockTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/features/ImageApiMockTest.java @@ -16,15 +16,16 @@ */ package org.jclouds.docker.features; -import static org.testng.Assert.fail; +import static org.testng.Assert.assertEquals; import org.jclouds.docker.DockerApi; +import org.jclouds.docker.config.DockerParserModule; import org.jclouds.docker.internal.BaseDockerMockTest; import org.jclouds.docker.options.CreateImageOptions; -import org.jclouds.rest.ResourceNotFoundException; +import org.jclouds.docker.parse.ImageParseTest; +import org.jclouds.docker.parse.ImagesParseTest; import org.testng.annotations.Test; -import com.google.common.collect.ImmutableMultimap; import com.squareup.okhttp.mockwebserver.MockResponse; import com.squareup.okhttp.mockwebserver.MockWebServer; @@ -35,61 +36,47 @@ import com.squareup.okhttp.mockwebserver.MockWebServer; public class ImageApiMockTest extends BaseDockerMockTest { public void testCreateImage() throws Exception { - MockWebServer server = mockWebServer(); - server.enqueue(new MockResponse().setResponseCode(200)); - DockerApi dockerApi = api(server.getUrl("/")); - ImageApi api = dockerApi.getImageApi(); + MockWebServer server = mockWebServer(new MockResponse().setResponseCode(200)); + ImageApi api = api(DockerApi.class, server.getUrl("/").toString()).getImageApi(); try { api.createImage(CreateImageOptions.Builder.fromImage("base")); - assertRequestHasParameters(server.takeRequest(), "POST", "/images/create", ImmutableMultimap.of("fromImage", "base")); + assertSent(server, "POST", "/images/create?fromImage=base"); } finally { - dockerApi.close(); server.shutdown(); } } - public void testCreateImageFailure() throws Exception { - MockWebServer server = mockWebServer(); - server.enqueue(new MockResponse().setResponseCode(404)); - DockerApi dockerApi = api(server.getUrl("/")); - ImageApi api = dockerApi.getImageApi(); + public void testGetImage() throws Exception { + MockWebServer server = mockWebServer(new MockResponse().setBody(payloadFromResource("/image.json"))); + ImageApi api = api(DockerApi.class, server.getUrl("/").toString(), new DockerParserModule()).getImageApi(); try { - api.createImage(CreateImageOptions.Builder.fromImage("base")); - fail("Create image must fail on 404"); - } catch (ResourceNotFoundException ex) { - // Expected exception + String imageId = "cbba6639a342646deed70d7ea6162fa2a0acea9300f911f4e014555fe37d3456"; + assertEquals(api.inspectImage(imageId), new ImageParseTest().expected()); + assertSent(server, "GET", "/images/" + imageId + "/json"); + } finally { + server.shutdown(); + } + } + + public void testListImages() throws Exception { + MockWebServer server = mockWebServer(new MockResponse().setBody(payloadFromResource("/images.json"))); + ImageApi api = api(DockerApi.class, server.getUrl("/").toString()).getImageApi(); + try { + assertEquals(api.listImages(), new ImagesParseTest().expected()); + assertSent(server, "GET", "/images/json"); } finally { - dockerApi.close(); server.shutdown(); } } public void testDeleteImage() throws Exception { - MockWebServer server = mockWebServer(); - server.enqueue(new MockResponse().setResponseCode(204)); - DockerApi dockerApi = api(server.getUrl("/")); - ImageApi api = dockerApi.getImageApi(); + MockWebServer server = mockWebServer(new MockResponse().setResponseCode(204)); + ImageApi api = api(DockerApi.class, server.getUrl("/").toString()).getImageApi(); try { api.deleteImage("1"); - assertRequestHasCommonFields(server.takeRequest(), "DELETE", "/images/1"); - } finally { - dockerApi.close(); - server.shutdown(); - } - } + assertSent(server, "DELETE", "/images/1"); - public void testDeleteNotExistingImage() throws Exception { - MockWebServer server = mockWebServer(); - server.enqueue(new MockResponse().setResponseCode(404)); - DockerApi dockerApi = api(server.getUrl("/")); - ImageApi api = dockerApi.getImageApi(); - try { - api.deleteImage("1"); - fail("Delete image must fail on 404"); - } catch (ResourceNotFoundException ex) { - // Expected exception } finally { - dockerApi.close(); server.shutdown(); } } diff --git a/apis/docker/src/test/java/org/jclouds/docker/features/MiscApiLiveTest.java b/apis/docker/src/test/java/org/jclouds/docker/features/MiscApiLiveTest.java index b14a1085e9..bfe1b40232 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/features/MiscApiLiveTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/features/MiscApiLiveTest.java @@ -18,7 +18,6 @@ package org.jclouds.docker.features; import org.jclouds.docker.compute.BaseDockerApiLiveTest; import org.jclouds.docker.options.BuildOptions; -import org.testng.annotations.AfterClass; import org.testng.annotations.Test; import static org.testng.Assert.assertEquals; @@ -34,10 +33,10 @@ import com.google.common.collect.Iterables; public class MiscApiLiveTest extends BaseDockerApiLiveTest { private static final String API_VERSION = "1.15"; - private static final String VERSION = "1.3.0"; - private static final String GIT_COMMIT = "c78088f"; + private static final String VERSION = "1.3.2"; + private static final String GIT_COMMIT = "39fa2fa"; private static final String GO_VERSION = "go1.3.3"; - private static final String KERNEL_VERSION = "3.16.4-tinycore64"; + private static final String KERNEL_VERSION = "3.16.7-tinycore64"; private static final String ARCH = "amd64"; private static final String OS = "linux"; @@ -71,13 +70,6 @@ public class MiscApiLiveTest extends BaseDockerApiLiveTest { assertNotNull(imageId); } - @AfterClass - protected void tearDown() { - if (imageId != null) { - consumeStream(api.getImageApi().deleteImage(imageId)); - } - } - private MiscApi api() { return api.getMiscApi(); } diff --git a/apis/docker/src/test/java/org/jclouds/docker/features/MiscApiMockTest.java b/apis/docker/src/test/java/org/jclouds/docker/features/MiscApiMockTest.java index e7b1e5b1b4..630d7af3b1 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/features/MiscApiMockTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/features/MiscApiMockTest.java @@ -19,19 +19,15 @@ package org.jclouds.docker.features; import com.squareup.okhttp.mockwebserver.MockResponse; import com.squareup.okhttp.mockwebserver.MockWebServer; import org.jclouds.docker.DockerApi; -import org.jclouds.docker.domain.Info; -import org.jclouds.docker.domain.Version; import org.jclouds.docker.internal.BaseDockerMockTest; -import org.jclouds.docker.options.BuildOptions; +import org.jclouds.docker.parse.InfoParseTest; +import org.jclouds.docker.parse.VersionParseTest; import org.jclouds.io.Payload; import org.jclouds.io.Payloads; -import org.jclouds.rest.ResourceNotFoundException; import org.testng.annotations.Test; import static org.jclouds.docker.compute.BaseDockerApiLiveTest.tarredDockerfile; import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.fail; import java.io.File; import java.io.FileInputStream; @@ -41,117 +37,50 @@ import java.io.FileInputStream; @Test(groups = "unit", testName = "MiscApiMockTest") public class MiscApiMockTest extends BaseDockerMockTest { - private static final String API_VERSION = "1.15"; - private static final String VERSION = "1.3.0"; - private static final String GIT_COMMIT = "c78088f"; - private static final String GO_VERSION = "go1.3.3"; - private static final String KERNEL_VERSION = "3.16.4-tinycore64"; - private static final String ARCH = "amd64"; - private static final String OS = "linux"; - public void testGetVersion() throws Exception { - MockWebServer server = mockWebServer(); - server.enqueue(new MockResponse().setBody(payloadFromResource("/version.json"))); - DockerApi dockerApi = api(server.getUrl("/")); - MiscApi api = dockerApi.getMiscApi(); + MockWebServer server = mockWebServer(new MockResponse().setBody(payloadFromResource("/version.json"))); + MiscApi api = api(DockerApi.class, server.getUrl("/").toString()).getMiscApi(); try { - Version version = api.getVersion(); - assertRequestHasCommonFields(server.takeRequest(), "/version"); - assertNotNull(version); - assertEquals(version.version(), VERSION); - assertEquals(version.gitCommit(), GIT_COMMIT); - assertEquals(version.apiVersion(), API_VERSION); - assertEquals(version.goVersion(), GO_VERSION); - assertEquals(version.kernelVersion(), KERNEL_VERSION); - assertEquals(version.arch(), ARCH); - assertEquals(version.os(), OS); + assertEquals(api.getVersion(), new VersionParseTest().expected()); + assertSent(server, "GET", "/version"); } finally { - dockerApi.close(); server.shutdown(); } } public void testGetInfo() throws Exception { - MockWebServer server = mockWebServer(); - server.enqueue(new MockResponse().setBody(payloadFromResource("/info.json"))); - DockerApi dockerApi = api(server.getUrl("/")); - MiscApi api = dockerApi.getMiscApi(); - + MockWebServer server = mockWebServer(new MockResponse().setBody(payloadFromResource("/info.json"))); + MiscApi api = api(DockerApi.class, server.getUrl("/").toString()).getMiscApi(); try { - Info info = api.getInfo(); - assertRequestHasCommonFields(server.takeRequest(), "/info"); - assertNotNull(info); - assertNotNull(info.containers()); - assertNotNull(info.debug()); - assertNotNull(info.driver()); - assertNotNull(info.executionDriver()); - assertNotNull(info.images()); - assertNotNull(info.indexServerAddress()); - assertNotNull(info.initPath()); - assertNotNull(info.iPv4Forwarding()); - assertNotNull(info.kernelVersion()); - assertNotNull(info.memoryLimit()); - assertNotNull(info.nEventsListener()); - assertNotNull(info.nFd()); - assertNotNull(info.nGoroutines()); - assertNotNull(info.swapLimit()); + assertEquals(api.getInfo(), new InfoParseTest().expected()); + assertSent(server, "GET", "/info"); } finally { - dockerApi.close(); server.shutdown(); } } public void testBuildContainer() throws Exception { - MockWebServer server = mockWebServer(); - server.enqueue(new MockResponse().setResponseCode(200)); - DockerApi dockerApi = api(server.getUrl("/")); - MiscApi api = dockerApi.getMiscApi(); - File dockerFile = File.createTempFile("docker", "tmp"); + MockWebServer server = mockWebServer(new MockResponse().setResponseCode(200)); + MiscApi api = api(DockerApi.class, server.getUrl("/").toString()).getMiscApi(); try { - api.build(tarredDockerfile(), BuildOptions.NONE); - assertRequestHasCommonFields(server.takeRequest(), "POST", "/build"); + api.build(tarredDockerfile()); + assertSent(server, "POST", "/build"); } finally { - dockerFile.delete(); - dockerApi.close(); server.shutdown(); } } public void testBuildContainerUsingPayload() throws Exception { - MockWebServer server = mockWebServer(); - server.enqueue(new MockResponse().setResponseCode(200)); - DockerApi dockerApi = api(server.getUrl("/")); - MiscApi api = dockerApi.getMiscApi(); + MockWebServer server = mockWebServer(new MockResponse().setResponseCode(200)); + MiscApi api = api(DockerApi.class, server.getUrl("/").toString()).getMiscApi(); File file = File.createTempFile("docker", "tmp"); FileInputStream data = new FileInputStream(file); Payload payload = Payloads.newInputStreamPayload(data); payload.getContentMetadata().setContentLength(file.length()); - try { - api.build(payload, BuildOptions.NONE); - assertRequestHasCommonFields(server.takeRequest(), "POST", "/build"); + api.build(payload); + assertSent(server, "POST", "/build"); } finally { - dockerApi.close(); - server.shutdown(); - } - } - - public void testBuildNonexistentContainer() throws Exception { - MockWebServer server = mockWebServer(); - server.enqueue(new MockResponse().setResponseCode(404)); - DockerApi dockerApi = api(server.getUrl("/")); - MiscApi api = dockerApi.getMiscApi(); - File dockerFile = File.createTempFile("docker", "tmp"); - try { - try { - api.build(tarredDockerfile(), BuildOptions.NONE); - fail("Build container must fail on 404"); - } catch (ResourceNotFoundException ex) { - // Expected exception - } - } finally { - dockerFile.delete(); - dockerApi.close(); server.shutdown(); } } diff --git a/apis/docker/src/test/java/org/jclouds/docker/internal/BaseDockerMockTest.java b/apis/docker/src/test/java/org/jclouds/docker/internal/BaseDockerMockTest.java index efdc9b5b2c..994519ece3 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/internal/BaseDockerMockTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/internal/BaseDockerMockTest.java @@ -16,27 +16,19 @@ */ package org.jclouds.docker.internal; -import static com.google.common.util.concurrent.MoreExecutors.sameThreadExecutor; -import static org.jclouds.http.utils.Queries.encodeQueryLine; +import static org.assertj.core.api.Assertions.assertThat; import static org.jclouds.util.Strings2.toStringAndClose; -import static org.testng.Assert.assertEquals; import java.io.IOException; -import java.net.URL; import java.util.Properties; -import java.util.Set; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MediaType; -import org.jclouds.ContextBuilder; -import org.jclouds.concurrent.config.ExecutorServiceModule; -import org.jclouds.docker.DockerApi; +import org.jclouds.http.BaseMockWebServerTest; +import org.jclouds.http.okhttp.config.OkHttpCommandExecutorServiceModule; import com.google.common.base.Charsets; import com.google.common.base.Throwables; -import com.google.common.collect.ImmutableMultimap; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Multimap; import com.google.inject.Module; import com.squareup.okhttp.mockwebserver.MockWebServer; import com.squareup.okhttp.mockwebserver.RecordedRequest; @@ -44,33 +36,18 @@ import com.squareup.okhttp.mockwebserver.RecordedRequest; /** * Base class for all Docker mock tests. */ -public class BaseDockerMockTest { - private final Set modules = ImmutableSet. of(new ExecutorServiceModule(sameThreadExecutor(), - sameThreadExecutor())); +public class BaseDockerMockTest extends BaseMockWebServerTest { - protected String provider; + protected static final String API_VERSION = "1.15"; - public BaseDockerMockTest() { - provider = "docker"; + @Override + protected void addOverrideProperties(Properties properties) { + properties.setProperty("jclouds.api-version", API_VERSION); } - public DockerApi api(URL url) { - return ContextBuilder.newBuilder(provider) - .credentials("clientid", "apikey") - .endpoint(url.toString()) - .modules(modules) - .overrides(setupProperties()) - .buildApi(DockerApi.class); - } - - protected Properties setupProperties() { - return new Properties(); - } - - public static MockWebServer mockWebServer() throws IOException { - MockWebServer server = new MockWebServer(); - server.play(); - return server; + @Override + protected Module createConnectionModule() { + return new OkHttpCommandExecutorServiceModule(); } public byte[] payloadFromResource(String resource) { @@ -81,38 +58,12 @@ public class BaseDockerMockTest { } } - protected static void assertRequestHasCommonFields(final RecordedRequest request, final String path) - throws InterruptedException { - assertRequestHasParameters(request, "GET", path, ImmutableMultimap. of()); - } - - protected static void assertRequestHasCommonFields(final RecordedRequest request, - final String verb, final String path) - throws InterruptedException { - assertRequestHasParameters(request, verb, path, ImmutableMultimap. of()); - } - - protected static void assertRequestHasParameters(final RecordedRequest request, final String path, - Multimap parameters) throws InterruptedException { - assertRequestHasParameters(request, "GET", path, parameters); - } - - protected static void assertRequestHasParameters(final RecordedRequest request, String verb, final String path, - Multimap parameters) throws InterruptedException { - String queryParameters = ""; - if (!parameters.isEmpty()) { - Multimap allparams = ImmutableMultimap.builder() - .putAll(parameters) - .build(); - - assertRequestHasAcceptHeader(request); - queryParameters = "?" + encodeQueryLine(allparams); - } - assertEquals(request.getRequestLine(), verb + " " + path + queryParameters + " HTTP/1.1"); - } - - protected static void assertRequestHasAcceptHeader(final RecordedRequest request) throws InterruptedException { - assertEquals(request.getHeader(HttpHeaders.ACCEPT), MediaType.APPLICATION_JSON); + protected RecordedRequest assertSent(MockWebServer server, String method, String path) throws InterruptedException { + RecordedRequest request = server.takeRequest(); + assertThat(request.getMethod()).isEqualTo(method); + assertThat(request.getPath()).isEqualTo("/v" + API_VERSION + path); + assertThat(request.getHeader(HttpHeaders.ACCEPT)).isEqualTo(MediaType.APPLICATION_JSON); + return request; } } diff --git a/apis/docker/src/test/java/org/jclouds/docker/internal/BaseDockerParseTest.java b/apis/docker/src/test/java/org/jclouds/docker/internal/BaseDockerParseTest.java new file mode 100644 index 0000000000..5227ff8224 --- /dev/null +++ b/apis/docker/src/test/java/org/jclouds/docker/internal/BaseDockerParseTest.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.internal; + +import org.jclouds.docker.config.DockerParserModule; +import org.jclouds.json.BaseItemParserTest; +import org.jclouds.json.config.GsonModule; + +import com.google.inject.Guice; +import com.google.inject.Injector; + +public abstract class BaseDockerParseTest extends BaseItemParserTest { + + @Override + protected Injector injector() { + return Guice.createInjector(new GsonModule(), new DockerParserModule()); + } + +} diff --git a/apis/docker/src/test/java/org/jclouds/docker/parse/ContainerParseTest.java b/apis/docker/src/test/java/org/jclouds/docker/parse/ContainerParseTest.java new file mode 100644 index 0000000000..dc5c791b03 --- /dev/null +++ b/apis/docker/src/test/java/org/jclouds/docker/parse/ContainerParseTest.java @@ -0,0 +1,95 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.parse; + +import java.util.List; +import java.util.Map; + +import javax.ws.rs.Consumes; +import javax.ws.rs.core.MediaType; + +import org.jclouds.date.internal.SimpleDateFormatDateService; +import org.jclouds.docker.domain.Config; +import org.jclouds.docker.domain.Container; +import org.jclouds.docker.domain.HostConfig; +import org.jclouds.docker.domain.NetworkSettings; +import org.jclouds.docker.domain.State; +import org.jclouds.docker.internal.BaseDockerParseTest; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; + +@Test(groups = "unit") +public class ContainerParseTest extends BaseDockerParseTest { + + @Override + public String resource() { + return "/container.json"; + } + + @Override + @Consumes(MediaType.APPLICATION_JSON) + public Container expected() { + return Container.builder() + .id("6c9932f478bd761f32ddb54ed28ab42ab6fac6f2a279f561ea31503ee9d39524") + .created(new SimpleDateFormatDateService().iso8601DateParse("2014-10-31T17:00:21.544197943Z")) + .path("/home/weave/weaver") + .name("/weave") + .args(ImmutableList.of("-iface", "ethwe", "-wait", "5", "-name", "7a:63:a2:39:7b:0f")) + .config(Config.builder() + .hostname("6c9932f478bd") + .env(ImmutableList.of("PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin")) + .image("57e570db16baba1e8c0d6f3c15868ddb400f64ff76ec948e65c3ca3f15fb3587") + .domainname("") + .user("") + .cmd(ImmutableList.of("-name", "7a:63:a2:39:7b:0f")) + .entrypoint(ImmutableList.of("/home/weave/weaver", "-iface", "ethwe", "-wait", "5")) + .image("zettio/weave") + .workingDir("/home/weave") + .exposedPorts(ImmutableMap.of("6783/tcp", ImmutableMap.of(), "6783/udp", ImmutableMap.of())) + .build()) + .state(State.create(3939, true, 0, "2014-10-31T17:00:21.802008706Z", "0001-01-01T00:00:00Z", false, false)) + .image("57e570db16baba1e8c0d6f3c15868ddb400f64ff76ec948e65c3ca3f15fb3587") + .networkSettings(NetworkSettings.builder() + .ipAddress("172.17.0.7") + .ipPrefixLen(16) + .gateway("172.17.42.1") + .bridge("docker0") + .ports(ImmutableMap.>>of( + "6783/tcp", ImmutableList.>of(ImmutableMap.of("HostIp", "0.0.0.0", "HostPort", "6783")), + "6783/udp", ImmutableList.>of(ImmutableMap.of("HostIp", "0.0.0.0", "HostPort", "6783"))) + ) + .build()) + .resolvConfPath("/var/lib/docker/containers/6c9932f478bd761f32ddb54ed28ab42ab6fac6f2a279f561ea31503ee9d39524/resolv.conf") + .hostConfig(HostConfig.builder() + .containerIDFile("") + .portBindings(ImmutableMap.>>of( + "6783/tcp", ImmutableList.>of(ImmutableMap.of("HostIp", "", "HostPort", "6783")), + "6783/udp", ImmutableList.>of(ImmutableMap.of("HostIp", "", "HostPort", "6783"))) + ) + .privileged(true) + .build()) + .driver("aufs") + .execDriver("native-0.2") + .hostnamePath("/var/lib/docker/containers/6c9932f478bd761f32ddb54ed28ab42ab6fac6f2a279f561ea31503ee9d39524/hostname") + .hostsPath("/var/lib/docker/containers/6c9932f478bd761f32ddb54ed28ab42ab6fac6f2a279f561ea31503ee9d39524/hosts") + .mountLabel("") + .processLabel("") + .build(); + } +} diff --git a/apis/docker/src/test/java/org/jclouds/docker/parse/ContainersParseTest.java b/apis/docker/src/test/java/org/jclouds/docker/parse/ContainersParseTest.java new file mode 100644 index 0000000000..bb3bd623be --- /dev/null +++ b/apis/docker/src/test/java/org/jclouds/docker/parse/ContainersParseTest.java @@ -0,0 +1,53 @@ +/* + * 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.parse; + +import java.util.List; + +import javax.ws.rs.Consumes; +import javax.ws.rs.core.MediaType; + +import org.jclouds.docker.domain.ContainerSummary; +import org.jclouds.docker.domain.Port; +import org.jclouds.docker.internal.BaseDockerParseTest; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableList; + +@Test(groups = "unit") +public class ContainersParseTest extends BaseDockerParseTest> { + + @Override + public String resource() { + return "/containers.json"; + } + + @Override + @Consumes(MediaType.APPLICATION_JSON) + public List expected() { + return ImmutableList.of( + ContainerSummary.create("6d35806c1bd2b25cd92bba2d2c2c5169dc2156f53ab45c2b62d76e2d2fee14a9", + ImmutableList.of("/hopeful_mclean"), + "1395472605", + "jclouds/ubuntu:latest", + "/usr/sbin/sshd -D", + ImmutableList.of(Port.create("0.0.0.0", 22, 49231, "tcp")), + "Up 55 seconds") + ); + } + +} diff --git a/apis/docker/src/test/java/org/jclouds/docker/parse/ImageParseTest.java b/apis/docker/src/test/java/org/jclouds/docker/parse/ImageParseTest.java new file mode 100644 index 0000000000..038be863a0 --- /dev/null +++ b/apis/docker/src/test/java/org/jclouds/docker/parse/ImageParseTest.java @@ -0,0 +1,83 @@ +/* + * 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.parse; + +import javax.ws.rs.Consumes; +import javax.ws.rs.core.MediaType; + +import org.jclouds.date.internal.SimpleDateFormatDateService; +import org.jclouds.docker.domain.Config; +import org.jclouds.docker.domain.Image; +import org.jclouds.docker.internal.BaseDockerParseTest; +import org.testng.annotations.Test; + +import com.beust.jcommander.internal.Maps; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; + +@Test(groups = "unit") +public class ImageParseTest extends BaseDockerParseTest { + + @Override + public String resource() { + return "/image.json"; + } + + @Override + @Consumes(MediaType.APPLICATION_JSON) + public Image expected() { + return Image.create("cbba6639a342646deed70d7ea6162fa2a0acea9300f911f4e014555fe37d3456", + "author", + "comment", + Config.builder().cmd(ImmutableList.of("/bin/sh", "-c", "echo hello world")) + .env(ImmutableList.of( + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "HOME=/root", + "JAVA_HOME=/usr/lib/jvm/java-7-openjdk-amd64" + ) + ) + .exposedPorts(ImmutableMap.of("8081/tcp", Maps.newHashMap())) + .hostname("f22711318734") + .domainname("") + .user("user") + .image("05794515afd5724df1cdf0e674ae932455fce7dea3c70a94d77119ad1fa954ba") + .workingDir("/home/user") + .build(), + Config.builder().cmd(ImmutableList.of("/bin/sh", "-c", "echo hello world")) + .env(ImmutableList.of( + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "HOME=/root", + "JAVA_HOME=/usr/lib/jvm/java-7-openjdk-amd64") + ) + .exposedPorts(ImmutableMap.of("8081/tcp", Maps.newHashMap())) + .hostname("f22711318734") + .domainname("") + .user("user") + .image("05794515afd5724df1cdf0e674ae932455fce7dea3c70a94d77119ad1fa954ba") + .workingDir("/home/user") + .build(), + "05794515afd5724df1cdf0e674ae932455fce7dea3c70a94d77119ad1fa954ba", + new SimpleDateFormatDateService().iso8601DateParse("2014-11-24T11:09:20.310023104Z"), + "0d14967353dbbd2ee78abe209f026f71654da49692fa2b044296ec3c810027b3", + "1.3.1", + "amd64", + "linux", + 0, + 808709069, + null); + } +} diff --git a/apis/docker/src/test/java/org/jclouds/docker/parse/ImagesParseTest.java b/apis/docker/src/test/java/org/jclouds/docker/parse/ImagesParseTest.java new file mode 100644 index 0000000000..d2ef5b6074 --- /dev/null +++ b/apis/docker/src/test/java/org/jclouds/docker/parse/ImagesParseTest.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.parse; + +import java.util.List; + +import javax.ws.rs.Consumes; +import javax.ws.rs.core.MediaType; + +import org.jclouds.docker.domain.ImageSummary; +import org.jclouds.docker.internal.BaseDockerParseTest; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableList; + +@Test(groups = "unit") +public class ImagesParseTest extends BaseDockerParseTest> { + + @Override + public String resource() { + return "/images.json"; + } + + @Override + @Consumes(MediaType.APPLICATION_JSON) + public List expected() { + return ImmutableList.of( + ImageSummary.create("cbba6639a342646deed70d7ea6162fa2a0acea9300f911f4e014555fe37d3456", + 1416827360, + "05794515afd5724df1cdf0e674ae932455fce7dea3c70a94d77119ad1fa954ba", + 0, + 808709069, + ImmutableList.of("test:latest")), + ImageSummary.create("e1e548b03259ae30ba12232b6c16ef5205cf71b0363848e78b0394e1ecba4f57", + 1416826851, + "6f36bec79c7f184ceebf7000cfb7244c4bc9b397b6659ac7f420a53d114250d9", + 0, + 5609404, + ImmutableList.of(":")), + ImageSummary.create("8201388d2b288539aab6aabf5d3b15ec269eba95c6baa9d6771f16540abf3a3f", + 1414247273, + "4671e2c549c5b60063e349f520c801dc73b53d2226a5a8e5501845ebe94761ca", + 0, + 755313702, + ImmutableList.of("dockerfile/java:openjdk-7-jdk")), + ImageSummary.create("5506de2b643be1e6febbf3b8a240760c6843244c41e12aa2f60ccbb7153d17f5", + 1414108439, + "22093c35d77bb609b9257ffb2640845ec05018e3d96cb939f68d0e19127f1723", + 0, + 199257566, + ImmutableList.of("ubuntu:14.04")) + ); + } + +} diff --git a/apis/docker/src/test/java/org/jclouds/docker/parse/InfoParseTest.java b/apis/docker/src/test/java/org/jclouds/docker/parse/InfoParseTest.java new file mode 100644 index 0000000000..5527e28845 --- /dev/null +++ b/apis/docker/src/test/java/org/jclouds/docker/parse/InfoParseTest.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.parse; + +import java.util.List; + +import javax.ws.rs.Consumes; +import javax.ws.rs.core.MediaType; + +import org.jclouds.docker.domain.Info; +import org.jclouds.docker.internal.BaseDockerParseTest; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableList; + +@Test(groups = "unit") +public class InfoParseTest extends BaseDockerParseTest { + + @Override + public String resource() { + return "/info.json"; + } + + @Override + @Consumes(MediaType.APPLICATION_JSON) + public Info expected() { + return Info.create(0, + 1, + "aufs", + ImmutableList.>of( + ImmutableList.of("Root Dir", "/mnt/sda1/var/lib/docker/aufs"), + ImmutableList.of("Dirs", "15") + ), + "native-0.2", + 1, + 15, + "https://index.docker.io/v1/", + "/usr/local/bin/docker", + "", + "3.16.4-tinycore64", + 1, + 0, + 10, + 11, + "Boot2Docker 1.3.0 (TCL 5.4); master : a083df4 - Thu Oct 16 17:05:03 UTC 2014", + 1); + } +} diff --git a/apis/docker/src/test/java/org/jclouds/docker/parse/VersionParseTest.java b/apis/docker/src/test/java/org/jclouds/docker/parse/VersionParseTest.java new file mode 100644 index 0000000000..2d9781bc47 --- /dev/null +++ b/apis/docker/src/test/java/org/jclouds/docker/parse/VersionParseTest.java @@ -0,0 +1,46 @@ +/* + * 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.parse; + +import javax.ws.rs.Consumes; +import javax.ws.rs.core.MediaType; + +import org.jclouds.docker.domain.Version; +import org.jclouds.docker.internal.BaseDockerParseTest; +import org.testng.annotations.Test; + +@Test(groups = "unit") +public class VersionParseTest extends BaseDockerParseTest { + + @Override + public String resource() { + return "/version.json"; + } + + @Override + @Consumes(MediaType.APPLICATION_JSON) + public Version expected() { + return Version.create( + "1.15", + "amd64", + "c78088f", + "go1.3.3", + "3.16.4-tinycore64", + "linux", + "1.3.0"); + } +} diff --git a/apis/docker/src/test/resources/cert.pem b/apis/docker/src/test/resources/cert.pem new file mode 100644 index 0000000000..ab2c41a74e --- /dev/null +++ b/apis/docker/src/test/resources/cert.pem @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDtTCCAp2gAwIBAgIJAL/TuOknjSR5MA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV +BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX +aWRnaXRzIFB0eSBMdGQwHhcNMTQxMjEyMTYwMDEyWhcNMjQxMjA5MTYwMDEyWjBF +MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50 +ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEAuUQcWlhrMXmzLSLiHUADdF98IHKRde7I1eDT91NndOtx4pKfvVc3WPei +REIMksrU8F1r4A086h5++xxDf27LxR4EC9ry0Q6GgJ9Un9RB9clCWRhLw8awHAS7 +HgAEN8YOSCeF3qP+78muxyMkIKQbYn3TqqOzRZcK576hX+a6URNJDhbHHAzq2fxm +rOSRVdPXzKLl48ABfmqJ6+KiXc6e7mQSgmwBLfh51zxmJNNwZ5e+6sfZ8oz4yM4y +Kzek53GRSFj+VFNp5nS/x2072fUak2i6DGut5LibFfh1kqskIm+Iq5WwO15RbojZ +CR6fkktCl5QOtea5p8SETZpwWfaddQIDAQABo4GnMIGkMB0GA1UdDgQWBBQtOc1g +1gxisDQ7VTmRYI1U9WHVMDB1BgNVHSMEbjBsgBQtOc1g1gxisDQ7VTmRYI1U9WHV +MKFJpEcwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNV +BAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZIIJAL/TuOknjSR5MAwGA1UdEwQF +MAMBAf8wDQYJKoZIhvcNAQEFBQADggEBABxYn5JdJYC8FUITqymJeNZ72/a2GVf8 ++gGlWH+DuiyviEAGMGpv7O4GXfS/6UiUBO7zMe2z15fUvNgd5RQBh4T+l5bA9aS0 +5JhENIpEApiIcEII4ISIk6pTLmAZjWvqq2kStiiFPNvdKFclYqFuKHv847EA8kGz +9u6MuUyFrJipWZ3g8zeYiwLWaAzvimbHomO7HU4pcvYaCSl7O5BQTToKwLfHcx5y +UG6uRf+0auC5QbotiXpYNdXhIbSD/2xXbjxGwYy4yRWHINcbwfK8iVRhR4eSvtBC +WvF3Vp8xLJxp6ujBd+a27AOWEiE1XM8oAoUpEzdIINY1GtUSbXzNboc= +-----END CERTIFICATE----- diff --git a/apis/docker/src/test/resources/image.json b/apis/docker/src/test/resources/image.json new file mode 100644 index 0000000000..8d34de7e41 --- /dev/null +++ b/apis/docker/src/test/resources/image.json @@ -0,0 +1,83 @@ +{ + "Architecture": "amd64", + "Author": "author", + "Comment": "comment", + "Config": { + "AttachStderr": false, + "AttachStdin": false, + "AttachStdout": false, + "Cmd": [ + "/bin/sh", + "-c", + "echo hello world" + ], + "CpuShares": 0, + "Cpuset": "", + "Domainname": "", + "Entrypoint": null, + "Env": [ + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "HOME=/root", + "JAVA_HOME=/usr/lib/jvm/java-7-openjdk-amd64" + ], + "ExposedPorts": { + "8081/tcp": {} + }, + "Hostname": "f22711318734", + "Image": "05794515afd5724df1cdf0e674ae932455fce7dea3c70a94d77119ad1fa954ba", + "Memory": 0, + "MemorySwap": 0, + "NetworkDisabled": false, + "OnBuild": [], + "OpenStdin": false, + "PortSpecs": null, + "StdinOnce": false, + "Tty": false, + "User": "user", + "Volumes": null, + "WorkingDir": "/home/user" + }, + "Container": "0d14967353dbbd2ee78abe209f026f71654da49692fa2b044296ec3c810027b3", + "ContainerConfig": { + "AttachStderr": false, + "AttachStdin": false, + "AttachStdout": false, + "Cmd": [ + "/bin/sh", + "-c", + "echo hello world" + ], + "CpuShares": 0, + "Cpuset": "", + "Domainname": "", + "Entrypoint": null, + "Env": [ + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "HOME=/root", + "JAVA_HOME=/usr/lib/jvm/java-7-openjdk-amd64" + ], + "ExposedPorts": { + "8081/tcp": {} + }, + "Hostname": "f22711318734", + "Image": "05794515afd5724df1cdf0e674ae932455fce7dea3c70a94d77119ad1fa954ba", + "Memory": 0, + "MemorySwap": 0, + "NetworkDisabled": false, + "OnBuild": [], + "OpenStdin": false, + "PortSpecs": null, + "StdinOnce": false, + "Tty": false, + "User": "user", + "Volumes": null, + "WorkingDir": "/home/user" + }, + "Created": "2014-11-24T11:09:20.310023104Z", + "DockerVersion": "1.3.1", + "Id": "cbba6639a342646deed70d7ea6162fa2a0acea9300f911f4e014555fe37d3456", + "Os": "linux", + "Parent": "05794515afd5724df1cdf0e674ae932455fce7dea3c70a94d77119ad1fa954ba", + "Size": 0, + "VirtualSize": 808709069 +} \ No newline at end of file diff --git a/apis/docker/src/test/resources/images.json b/apis/docker/src/test/resources/images.json new file mode 100644 index 0000000000..41d4f12f4b --- /dev/null +++ b/apis/docker/src/test/resources/images.json @@ -0,0 +1,42 @@ +[ + { + "Created": 1416827360, + "Id": "cbba6639a342646deed70d7ea6162fa2a0acea9300f911f4e014555fe37d3456", + "ParentId": "05794515afd5724df1cdf0e674ae932455fce7dea3c70a94d77119ad1fa954ba", + "RepoTags": [ + "test:latest" + ], + "Size": 0, + "VirtualSize": 808709069 + }, + { + "Created": 1416826851, + "Id": "e1e548b03259ae30ba12232b6c16ef5205cf71b0363848e78b0394e1ecba4f57", + "ParentId": "6f36bec79c7f184ceebf7000cfb7244c4bc9b397b6659ac7f420a53d114250d9", + "RepoTags": [ + ":" + ], + "Size": 0, + "VirtualSize": 5609404 + }, + { + "Created": 1414247273, + "Id": "8201388d2b288539aab6aabf5d3b15ec269eba95c6baa9d6771f16540abf3a3f", + "ParentId": "4671e2c549c5b60063e349f520c801dc73b53d2226a5a8e5501845ebe94761ca", + "RepoTags": [ + "dockerfile/java:openjdk-7-jdk" + ], + "Size": 0, + "VirtualSize": 755313702 + }, + { + "Created": 1414108439, + "Id": "5506de2b643be1e6febbf3b8a240760c6843244c41e12aa2f60ccbb7153d17f5", + "ParentId": "22093c35d77bb609b9257ffb2640845ec05018e3d96cb939f68d0e19127f1723", + "RepoTags": [ + "ubuntu:14.04" + ], + "Size": 0, + "VirtualSize": 199257566 + } +] \ No newline at end of file diff --git a/apis/docker/src/test/resources/key.pem b/apis/docker/src/test/resources/key.pem new file mode 100644 index 0000000000..23ebe5ea3b --- /dev/null +++ b/apis/docker/src/test/resources/key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEAuUQcWlhrMXmzLSLiHUADdF98IHKRde7I1eDT91NndOtx4pKf +vVc3WPeiREIMksrU8F1r4A086h5++xxDf27LxR4EC9ry0Q6GgJ9Un9RB9clCWRhL +w8awHAS7HgAEN8YOSCeF3qP+78muxyMkIKQbYn3TqqOzRZcK576hX+a6URNJDhbH +HAzq2fxmrOSRVdPXzKLl48ABfmqJ6+KiXc6e7mQSgmwBLfh51zxmJNNwZ5e+6sfZ +8oz4yM4yKzek53GRSFj+VFNp5nS/x2072fUak2i6DGut5LibFfh1kqskIm+Iq5Ww +O15RbojZCR6fkktCl5QOtea5p8SETZpwWfaddQIDAQABAoIBAAoj2zVqr3tGwPLQ +fPXC4i2FaGLSQnnk9uMV6iQYUYpJtLME+W9Ajcv1ydDYmJ2UMnFxe40IzHO39ZVC +58LaypZgXTJU6oNcuynhDp2s3WtZd6MuvD7b6hmufJtYvuJamb+DQkV8TmDLdiB6 +IOkUcldCscoeKZq+eJ9UhLqeA0aaosVUa2cfxJM2seGhF6iJmrJChvaKlc0xfqxG +l3Y+w01YNQgFBT7vxMzfXHZkVjY8jrrsxhhfyVKiheyaBZ0G3hQCZkBywR6h2bns +2rWM2mOcaWL7+lJiMB0d9el58HzTxFVxuPENbE2Li1SqTG1pn+Fif0VwG3Te0lPo +xz7EL8UCgYEA3wJLL2nfVqS73yf5k2w1+uiBHK4RVHaJeQgxv1QlgeunCwOWZ0dp +MDPQVi9CfWhEQUZKYr118V3ikecOYh+o8ruStz9fNTeA+7IXtBaE1cAbqU9ZgeS2 +vcAIfEzQmN9JtSiFsywJKLON4/aDYXGR1NZDSaXouFQ7T0F/SaL/CUsCgYEA1Kxu +1QjOwFC1tdB/2MF9Fn7X+YWBQsdE66a5HO30FTlMmLb/ecmngB5OxvdvpfMRMQeD +mhifSzAfnTBAzBcuz9/il6sKygfhQX6PyxtTJ5o4XlwOArkaBew8H0gBXwfAL33G +816rNF2kCfgDJisj9iPrl+QAgrg3sJsfIwk5fD8CgYAx075eyqYHIumDM9hUsyHg +fOCUOuROXenbbBRJbpCu1atOD7AkRVVgWsNa7lZJ1OkjOIRYSYK3ukVsWhbhn7dM +/NIMNZGdP1iHZERdjYaCh9jmXH9gQWz/Oo/qzfLxpTo/yt0MqnMlb/DtFWBHfmua +BYGlS/eSb+eMjtLU7iFTvwKBgDiGuFKhK6rMCPARQdnP27p97lOg23FvW28y+iKp +UGXPu/8fLJonMgEIjTGvFJrMFzar45uyjaxDVzPFXoOgac3QmP5s9Mor/AAXboqy +cZCmGfNijkrE/hiy6Gv8DHlAqyE0Ugvfjqu1c+M+az/a2Y0TkQvnCwezhQHIySbb +zc6rAoGAcg9GZnYk64V2c4uzaeTekkLkZhPTv4F7xFWb0JmTurL5BR//TDDk9Oy8 +pd5VoiBnKyCRTo2oC8J8COobxJXbl5qUxEeapjtKlQeBiROWE51K2DHacD2SDgKu +nzn5qonnU2659Aw95bfv+rGljttOu8ySZXVhs4UUKgrQvXErXhU= +-----END RSA PRIVATE KEY----- From f1bf27bfaf4593200852ee090d1af77af717ac3f Mon Sep 17 00:00:00 2001 From: Andrea Turli Date: Sun, 21 Dec 2014 17:35:02 +0100 Subject: [PATCH 25/81] support docker 1.4.1 (API v1.16) --- .../org/jclouds/docker/DockerApiMetadata.java | 2 +- .../java/org/jclouds/docker/domain/Info.java | 36 +++++++++++++-- .../docker/features/MiscApiLiveTest.java | 6 +-- .../jclouds/docker/parse/InfoParseTest.java | 44 +++++++++++-------- apis/docker/src/test/resources/info.json | 14 ++++-- 5 files changed, 73 insertions(+), 29 deletions(-) diff --git a/apis/docker/src/main/java/org/jclouds/docker/DockerApiMetadata.java b/apis/docker/src/main/java/org/jclouds/docker/DockerApiMetadata.java index 25d75babaf..5c105cac51 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/DockerApiMetadata.java +++ b/apis/docker/src/main/java/org/jclouds/docker/DockerApiMetadata.java @@ -67,7 +67,7 @@ public class DockerApiMetadata extends BaseHttpApiMetadata { .identityName("Path to certificate .pem file") .credentialName("Password to key .pem file") .documentation(URI.create("https://docs.docker.com/reference/api/docker_remote_api/")) - .version("1.15") + .version("1.16") .defaultEndpoint("https://127.0.0.1:2376") .defaultProperties(DockerApiMetadata.defaultProperties()) .view(typeToken(ComputeServiceContext.class)) diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/Info.java b/apis/docker/src/main/java/org/jclouds/docker/domain/Info.java index 8a4c504130..c0e2ac70d4 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/Info.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/Info.java @@ -18,6 +18,7 @@ package org.jclouds.docker.domain; import java.util.List; +import org.jclouds.javax.annotation.Nullable; import org.jclouds.json.SerializedNames; import com.google.auto.value.AutoValue; @@ -59,20 +60,49 @@ public abstract class Info { public abstract int swapLimit(); + public abstract String dockerRootDir(); + + /** + * @return a list of daemon labels + */ + @Nullable public abstract List labels(); + + /** + * @return total memory available + */ + public abstract long memTotal(); + + /** + * @return the number of CPUs available on the machine + */ + public abstract int ncpu(); + + /** + * @return a unique ID identifying the daemon + */ + public abstract String id(); + + /** + * @return a user-friendly name describing the running Docker daemon + */ + public abstract String name(); + Info() { } @SerializedNames({ "Containers", "Debug", "Driver", "DriverStatus", "ExecutionDriver", "IPv4Forwarding", "Images", "IndexServerAddress", "InitPath", "InitSha1", "KernelVersion", "MemoryLimit", "NEventsListener", - "NFd", "NGoroutines", "OperatingSystem", "SwapLimit" + "NFd", "NGoroutines", "OperatingSystem", "SwapLimit", "DockerRootDir", "Labels", "MemTotal", "NCPU", + "ID", "Name" }) public static Info create(int containers, int debug, String driver, List> driverStatus, String executionDriver, int iPv4Forwarding, int images, String indexServerAddress, String initPath, String initSha1, String kernelVersion, int memoryLimit, - int nEventsListener, int nFd, int nGoroutines, String operatingSystem, int swapLimit) { + int nEventsListener, int nFd, int nGoroutines, String operatingSystem, int swapLimit, + String dockerRootDir, List labels, long memTotal, int ncpu, String id, String name) { return new AutoValue_Info(containers, debug, driver, driverStatus, executionDriver, iPv4Forwarding, images, indexServerAddress, initPath, initSha1, kernelVersion, memoryLimit, nEventsListener, nFd, nGoroutines, - operatingSystem, swapLimit); + operatingSystem, swapLimit, dockerRootDir, labels, memTotal, ncpu, id, name); } } diff --git a/apis/docker/src/test/java/org/jclouds/docker/features/MiscApiLiveTest.java b/apis/docker/src/test/java/org/jclouds/docker/features/MiscApiLiveTest.java index bfe1b40232..0cbf7a7e77 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/features/MiscApiLiveTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/features/MiscApiLiveTest.java @@ -32,9 +32,9 @@ import com.google.common.collect.Iterables; @Test(groups = "live", testName = "MiscApiLiveTest", singleThreaded = true) public class MiscApiLiveTest extends BaseDockerApiLiveTest { - private static final String API_VERSION = "1.15"; - private static final String VERSION = "1.3.2"; - private static final String GIT_COMMIT = "39fa2fa"; + private static final String API_VERSION = "1.16"; + private static final String VERSION = "1.4.1"; + private static final String GIT_COMMIT = "5bc2ff8"; private static final String GO_VERSION = "go1.3.3"; private static final String KERNEL_VERSION = "3.16.7-tinycore64"; private static final String ARCH = "amd64"; diff --git a/apis/docker/src/test/java/org/jclouds/docker/parse/InfoParseTest.java b/apis/docker/src/test/java/org/jclouds/docker/parse/InfoParseTest.java index 5527e28845..2873e1c2f3 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/parse/InfoParseTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/parse/InfoParseTest.java @@ -38,25 +38,33 @@ public class InfoParseTest extends BaseDockerParseTest { @Override @Consumes(MediaType.APPLICATION_JSON) public Info expected() { - return Info.create(0, - 1, - "aufs", + return Info.create( + 0, // containers + 1, // debug + "aufs", // driver ImmutableList.>of( ImmutableList.of("Root Dir", "/mnt/sda1/var/lib/docker/aufs"), - ImmutableList.of("Dirs", "15") - ), - "native-0.2", - 1, - 15, - "https://index.docker.io/v1/", - "/usr/local/bin/docker", - "", - "3.16.4-tinycore64", - 1, - 0, - 10, - 11, - "Boot2Docker 1.3.0 (TCL 5.4); master : a083df4 - Thu Oct 16 17:05:03 UTC 2014", - 1); + ImmutableList.of("Dirs", "46") + ), // driverStatus + "native-0.2", // ExecutionDriver + 1, // IPv4Forwarding + 46, // Images + "https://index.docker.io/v1/", // IndexServerAddress + "/usr/local/bin/docker", // InitPath + "", // InitSha1 + "3.16.7-tinycore64", // KernelVersion + 1, // MemoryLimit + 0, // NEventsListener + 10, // NFd + 11, // NGoroutines + "Boot2Docker 1.3.0 (TCL 5.4); master : a083df4 - Thu Oct 16 17:05:03 UTC 2014", // OperatingSystem + 1, // SwapLimit + "/mnt/sda1/var/lib/docker", // DockerRootDir + null, // Labels + 2105585664, // MemTotal + 8, // NCPU + "7V5Y:IQ2M:HWIL:AZJV:HKRD:Q7OZ:3EQA:ZHMO:4LAD:OSIY:YBAA:BSX6", // ID + "boot2docker" // name + ); } } diff --git a/apis/docker/src/test/resources/info.json b/apis/docker/src/test/resources/info.json index 6697993652..e869382d52 100644 --- a/apis/docker/src/test/resources/info.json +++ b/apis/docker/src/test/resources/info.json @@ -1,6 +1,7 @@ { "Containers": 0, "Debug": 1, + "DockerRootDir": "/mnt/sda1/var/lib/docker", "Driver": "aufs", "DriverStatus": [ [ @@ -9,20 +10,25 @@ ], [ "Dirs", - "15" + "46" ] ], "ExecutionDriver": "native-0.2", + "ID": "7V5Y:IQ2M:HWIL:AZJV:HKRD:Q7OZ:3EQA:ZHMO:4LAD:OSIY:YBAA:BSX6", "IPv4Forwarding": 1, - "Images": 15, + "Images": 46, "IndexServerAddress": "https://index.docker.io/v1/", "InitPath": "/usr/local/bin/docker", "InitSha1": "", - "KernelVersion": "3.16.4-tinycore64", + "KernelVersion": "3.16.7-tinycore64", + "Labels": null, + "MemTotal": 2105585664, "MemoryLimit": 1, + "NCPU": 8, "NEventsListener": 0, "NFd": 10, "NGoroutines": 11, - "OperatingSystem": "Boot2Docker 1.3.0 (TCL 5.4); master : a083df4 - Thu Oct 16 17:05:03 UTC 2014", + "Name": "boot2docker", + "OperatingSystem": "Boot2Docker 1.4.1 (TCL 5.4); master : 86f7ec8 - Tue Dec 16 23:11:29 UTC 2014", "SwapLimit": 1 } \ No newline at end of file From 6afc3c889d65e08668531d177e0b98cfdcdb93cf Mon Sep 17 00:00:00 2001 From: Andrea Turli Date: Wed, 14 Jan 2015 17:32:11 +0100 Subject: [PATCH 26/81] fix docker unit tests --- .../src/test/java/org/jclouds/docker/parse/InfoParseTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apis/docker/src/test/java/org/jclouds/docker/parse/InfoParseTest.java b/apis/docker/src/test/java/org/jclouds/docker/parse/InfoParseTest.java index 2873e1c2f3..fa31c8bf20 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/parse/InfoParseTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/parse/InfoParseTest.java @@ -57,7 +57,7 @@ public class InfoParseTest extends BaseDockerParseTest { 0, // NEventsListener 10, // NFd 11, // NGoroutines - "Boot2Docker 1.3.0 (TCL 5.4); master : a083df4 - Thu Oct 16 17:05:03 UTC 2014", // OperatingSystem + "Boot2Docker 1.4.1 (TCL 5.4); master : 86f7ec8 - Tue Dec 16 23:11:29 UTC 2014", // OperatingSystem 1, // SwapLimit "/mnt/sda1/var/lib/docker", // DockerRootDir null, // Labels From 09641ec2da2a39ed7ad40a06cb4fcd9ddbe6e3b1 Mon Sep 17 00:00:00 2001 From: Jeremy Daggett Date: Wed, 14 Jan 2015 16:23:19 -0800 Subject: [PATCH 27/81] Split Auto dependencies into auto-service and auto-value --- apis/docker/pom.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/apis/docker/pom.xml b/apis/docker/pom.xml index 620cae139a..aebfce30c9 100644 --- a/apis/docker/pom.xml +++ b/apis/docker/pom.xml @@ -58,6 +58,11 @@ jclouds-compute ${project.version} + + com.google.auto.service + auto-service + provided + com.google.auto.value auto-value From 0418acb6e712e3bd7daf428bd19db3ce7ef87c40 Mon Sep 17 00:00:00 2001 From: Andrea Turli Date: Tue, 16 Dec 2014 09:57:04 +0100 Subject: [PATCH 28/81] AzureCompute: initial work to support ComputeServiceAdapter improve DeploymentToNodeMetadata enhance Deployment value object add support for get/set NetworkConfiguration added AzureComputeTemplateOptions to manage networkConfigurations add support for create/checkAvailable/list storageAccounts add support for inboundPorts add more RoleSize add support for SecurityGroupExtension by using NetworkSecurityGroup fix destroy node fix LocationToLocation fix OsImageToImage --- apis/docker/src/test/resources/logback.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apis/docker/src/test/resources/logback.xml b/apis/docker/src/test/resources/logback.xml index 94f593832e..30157e0f0c 100644 --- a/apis/docker/src/test/resources/logback.xml +++ b/apis/docker/src/test/resources/logback.xml @@ -31,4 +31,4 @@ - \ No newline at end of file + From 08fae12ef4db134bf941f5b9384eda7dc86c337e Mon Sep 17 00:00:00 2001 From: Ignasi Barrera Date: Tue, 24 Mar 2015 10:54:19 +0100 Subject: [PATCH 29/81] Apache jclouds 1.9.0 release --- apis/docker/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apis/docker/pom.xml b/apis/docker/pom.xml index aebfce30c9..8ce1f7b605 100644 --- a/apis/docker/pom.xml +++ b/apis/docker/pom.xml @@ -23,7 +23,7 @@ org.apache.jclouds.labs jclouds-labs - 2.0.0-SNAPSHOT + 1.9.0 From 944f14c687543004b95b546d30f708ba5e7e3533 Mon Sep 17 00:00:00 2001 From: Ignasi Barrera Date: Tue, 24 Mar 2015 10:56:30 +0100 Subject: [PATCH 30/81] Next development version 2.0.0-SNAPSHOT --- apis/docker/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apis/docker/pom.xml b/apis/docker/pom.xml index 8ce1f7b605..aebfce30c9 100644 --- a/apis/docker/pom.xml +++ b/apis/docker/pom.xml @@ -23,7 +23,7 @@ org.apache.jclouds.labs jclouds-labs - 1.9.0 + 2.0.0-SNAPSHOT From 0cea1efd2f530c4e100c5f5430c2ab7fa5b88872 Mon Sep 17 00:00:00 2001 From: Andrew Kennedy Date: Mon, 6 Apr 2015 16:34:51 +0100 Subject: [PATCH 31/81] Add template option to specify direct (unmapped) ports --- .../options/DockerTemplateOptions.java | 53 ++++++++++++------- .../strategy/DockerComputeServiceAdapter.java | 12 +++++ 2 files changed, 46 insertions(+), 19 deletions(-) diff --git a/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java b/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java index 355c617f9d..4c2035c6d0 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java +++ b/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java @@ -54,6 +54,7 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable protected Optional> commands = Optional.absent(); protected Optional> volumes = Optional.absent(); protected Optional> env = Optional.absent(); + protected Optional> directPorts = Optional.absent(); @Override public DockerTemplateOptions clone() { @@ -84,10 +85,13 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable } if (cpuShares.isPresent()) { eTo.cpuShares(cpuShares.get()); - } + } if (env.isPresent()) { eTo.env(env.get()); - } + } + if (directPorts.isPresent()) { + eTo.directPorts(directPorts.get()); + } } } @@ -104,12 +108,13 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable equal(this.memory, that.memory) && equal(this.commands, that.commands) && equal(this.cpuShares, that.cpuShares) && - equal(this.env, that.env); + equal(this.env, that.env) && + equal(this.directPorts, that.directPorts); } @Override public int hashCode() { - return Objects.hashCode(super.hashCode(), volumes, hostname, dns, memory, commands, cpuShares, env); + return Objects.hashCode(super.hashCode(), volumes, hostname, dns, memory, commands, cpuShares, env, directPorts); } @Override @@ -122,6 +127,7 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable .add("commands", commands) .add("volumes", volumes) .add("env", env) + .add("directPorts", directPorts) .toString(); } @@ -168,35 +174,36 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable return this; } - public Optional> getVolumes() { - return volumes; + public DockerTemplateOptions directPorts(List ports) { + this.directPorts = Optional.> of(ImmutableList.copyOf(ports)); + return this; } + public Optional> getVolumes() { return volumes; } + public Optional getDns() { return dns; } public Optional getHostname() { return hostname; } public Optional getMemory() { return memory; } - public Optional> getCommands() { - return commands; - } + public Optional> getCommands() { return commands; } public Optional getCpuShares() { return cpuShares; } - public Optional> getEnv() { - return env; - } + public Optional> getEnv() { return env; } + + public Optional> getDirectPorts() { return directPorts; } public static class Builder { - /** - * @see DockerTemplateOptions#volumes(java.util.Map) - */ - public static DockerTemplateOptions volumes(Map volumes) { - DockerTemplateOptions options = new DockerTemplateOptions(); - return DockerTemplateOptions.class.cast(options.volumes(volumes)); - } + /** + * @see DockerTemplateOptions#volumes(java.util.Map) + */ + public static DockerTemplateOptions volumes(Map volumes) { + DockerTemplateOptions options = new DockerTemplateOptions(); + return DockerTemplateOptions.class.cast(options.volumes(volumes)); + } /** * @see DockerTemplateOptions#dns(String) @@ -251,6 +258,14 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable return DockerTemplateOptions.class.cast(options.env(env)); } + /** + * @see DockerTemplateOptions#directPorts(java.util.List) + */ + public static DockerTemplateOptions directPorts(List directPorts) { + DockerTemplateOptions options = new DockerTemplateOptions(); + return DockerTemplateOptions.class.cast(options.directPorts(directPorts)); + } + // methods that only facilitate returning the correct object type /** diff --git a/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java b/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java index 920071b178..61b5041388 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java +++ b/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java @@ -19,6 +19,8 @@ package org.jclouds.docker.compute.strategy; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.collect.Iterables.find; +import java.util.Arrays; +import java.util.List; import java.util.Map; import java.util.Set; @@ -49,7 +51,9 @@ import org.jclouds.logging.Logger; import com.google.common.base.Predicate; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; @@ -136,6 +140,14 @@ public class DockerComputeServiceAdapter implements .publishAllPorts(true) .privileged(true); + if (templateOptions.getDirectPorts().isPresent()) { + Map>> portBindings = Maps.newHashMap(); + for (Integer port : templateOptions.getDirectPorts().get()) { + portBindings.put(port + "/tcp", Lists.>newArrayList(ImmutableMap.of("HostPort", Integer.toString(port)))); + } + hostConfigBuilder.portBindings(portBindings); + } + if (templateOptions.getDns().isPresent()) { hostConfigBuilder.dns(templateOptions.getDns().get()); } From 1685758f0dbfbf7e012d53407ff8ffaa2ac2eb16 Mon Sep 17 00:00:00 2001 From: Andrew Kennedy Date: Mon, 6 Apr 2015 23:30:40 +0100 Subject: [PATCH 32/81] Updates for new Docker port mapping option based on review comments - Change to portBindings and Map intead of List - Tidy up formatting for DockerTemplateOptions - Remove superfluous checkNotNull calls - Document the Map contents for portBindings option - Remove unused import to fix Checkstyle --- .../options/DockerTemplateOptions.java | 73 ++++++++++++------- .../strategy/DockerComputeServiceAdapter.java | 11 +-- 2 files changed, 54 insertions(+), 30 deletions(-) diff --git a/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java b/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java index 4c2035c6d0..73699d8af0 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java +++ b/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java @@ -54,7 +54,7 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable protected Optional> commands = Optional.absent(); protected Optional> volumes = Optional.absent(); protected Optional> env = Optional.absent(); - protected Optional> directPorts = Optional.absent(); + protected Optional> portBindings = Optional.absent(); @Override public DockerTemplateOptions clone() { @@ -89,8 +89,8 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable if (env.isPresent()) { eTo.env(env.get()); } - if (directPorts.isPresent()) { - eTo.directPorts(directPorts.get()); + if (portBindings.isPresent()) { + eTo.portBindings(portBindings.get()); } } } @@ -109,12 +109,12 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable equal(this.commands, that.commands) && equal(this.cpuShares, that.cpuShares) && equal(this.env, that.env) && - equal(this.directPorts, that.directPorts); + equal(this.portBindings, that.portBindings); } @Override public int hashCode() { - return Objects.hashCode(super.hashCode(), volumes, hostname, dns, memory, commands, cpuShares, env, directPorts); + return Objects.hashCode(super.hashCode(), volumes, hostname, dns, memory, commands, cpuShares, env, portBindings); } @Override @@ -127,14 +127,14 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable .add("commands", commands) .add("volumes", volumes) .add("env", env) - .add("directPorts", directPorts) + .add("portBindings", portBindings) .toString(); } public static final DockerTemplateOptions NONE = new DockerTemplateOptions(); public DockerTemplateOptions volumes(Map volumes) { - this.volumes = Optional.> of(ImmutableMap.copyOf(volumes)); + this.volumes = Optional.>of(ImmutableMap.copyOf(checkNotNull(volumes, "volumes"))); return this; } @@ -154,13 +154,11 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable } public TemplateOptions commands(Iterable commands) { - for (String command : checkNotNull(commands, "commands")) - checkNotNull(command, "all commands must be non-empty"); - this.commands = Optional.> of(ImmutableList.copyOf(commands)); + this.commands = Optional.>of(ImmutableList.copyOf(checkNotNull(commands, "commands"))); return this; } - public TemplateOptions commands(String... commands) { + public TemplateOptions commands(String...commands) { return commands(ImmutableList.copyOf(checkNotNull(commands, "commands"))); } @@ -169,13 +167,27 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable return this; } - public DockerTemplateOptions env(List env) { - this.env = Optional.> of(ImmutableList.copyOf(env)); + public DockerTemplateOptions env(Iterable env) { + this.env = Optional.>of(ImmutableList.copyOf(checkNotNull(env, "env"))); return this; } - public DockerTemplateOptions directPorts(List ports) { - this.directPorts = Optional.> of(ImmutableList.copyOf(ports)); + public DockerTemplateOptions env(String...env) { + return env(ImmutableList.copyOf(checkNotNull(env, "env"))); + } + + /** + * Set port bindings between the Docker host and a container. + *

+ * The {@link Map} keys are host ports number, and the value for an entry is the + * container port number. This is the same order as the arguments for the + * {@code --publish} command-line option to {@code docker run} which is + * {@code hostPort:containerPort}. + * + * @param portBindings the map of host to container port bindings + */ + public DockerTemplateOptions portBindings(Map portBindings) { + this.portBindings = Optional.>of(ImmutableMap.copyOf(checkNotNull(portBindings, "portBindings"))); return this; } @@ -193,12 +205,12 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable public Optional> getEnv() { return env; } - public Optional> getDirectPorts() { return directPorts; } + public Optional> getPortBindings() { return portBindings; } public static class Builder { /** - * @see DockerTemplateOptions#volumes(java.util.Map) + * @see DockerTemplateOptions#volumes(Map) */ public static DockerTemplateOptions volumes(Map volumes) { DockerTemplateOptions options = new DockerTemplateOptions(); @@ -222,7 +234,7 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable } /** - * @see DockerTemplateOptions#memory + * @see DockerTemplateOptions#memory(int) */ public static DockerTemplateOptions memory(int memory) { DockerTemplateOptions options = new DockerTemplateOptions(); @@ -230,13 +242,16 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable } /** - * @see DockerTemplateOptions#commands(Iterable) + * @see DockerTemplateOptions#commands(String[]) */ - public static DockerTemplateOptions commands(String... commands) { + public static DockerTemplateOptions commands(String...commands) { DockerTemplateOptions options = new DockerTemplateOptions(); return DockerTemplateOptions.class.cast(options.commands(commands)); } + /** + * @see DockerTemplateOptions#commands(Iterable) + */ public static DockerTemplateOptions commands(Iterable commands) { DockerTemplateOptions options = new DockerTemplateOptions(); return DockerTemplateOptions.class.cast(options.commands(commands)); @@ -251,19 +266,27 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable } /** - * @see DockerTemplateOptions#env(java.util.List) + * @see DockerTemplateOptions#env(String[]) */ - public static DockerTemplateOptions env(List env) { + public static DockerTemplateOptions env(String...env) { DockerTemplateOptions options = new DockerTemplateOptions(); return DockerTemplateOptions.class.cast(options.env(env)); } /** - * @see DockerTemplateOptions#directPorts(java.util.List) + * @see DockerTemplateOptions#env(Iterable) */ - public static DockerTemplateOptions directPorts(List directPorts) { + public static DockerTemplateOptions env(Iterable env) { DockerTemplateOptions options = new DockerTemplateOptions(); - return DockerTemplateOptions.class.cast(options.directPorts(directPorts)); + return DockerTemplateOptions.class.cast(options.env(env)); + } + + /** + * @see DockerTemplateOptions#portBindings(Map) + */ + public static DockerTemplateOptions portBindings(Map portBindings) { + DockerTemplateOptions options = new DockerTemplateOptions(); + return DockerTemplateOptions.class.cast(options.portBindings(portBindings)); } // methods that only facilitate returning the correct object type diff --git a/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java b/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java index 61b5041388..6da2bc0ac4 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java +++ b/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java @@ -19,7 +19,6 @@ package org.jclouds.docker.compute.strategy; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.collect.Iterables.find; -import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Set; @@ -140,10 +139,11 @@ public class DockerComputeServiceAdapter implements .publishAllPorts(true) .privileged(true); - if (templateOptions.getDirectPorts().isPresent()) { + if (templateOptions.getPortBindings().isPresent()) { Map>> portBindings = Maps.newHashMap(); - for (Integer port : templateOptions.getDirectPorts().get()) { - portBindings.put(port + "/tcp", Lists.>newArrayList(ImmutableMap.of("HostPort", Integer.toString(port)))); + for (Map.Entry entry : templateOptions.getPortBindings().get().entrySet()) { + portBindings.put(entry.getValue() + "/tcp", + Lists.>newArrayList(ImmutableMap.of("HostPort", Integer.toString(entry.getKey())))); } hostConfigBuilder.portBindings(portBindings); } @@ -151,12 +151,13 @@ public class DockerComputeServiceAdapter implements if (templateOptions.getDns().isPresent()) { hostConfigBuilder.dns(templateOptions.getDns().get()); } - // set up for volume bindings + if (templateOptions.getVolumes().isPresent()) { for (Map.Entry entry : templateOptions.getVolumes().get().entrySet()) { hostConfigBuilder.binds(ImmutableList.of(entry.getKey() + ":" + entry.getValue())); } } + HostConfig hostConfig = hostConfigBuilder.build(); api.getContainerApi().startContainer(container.id(), hostConfig); From e4477e579a2e321066eb142c18371104ac0f0834 Mon Sep 17 00:00:00 2001 From: Andrea Turli Date: Fri, 10 Apr 2015 11:38:47 +0200 Subject: [PATCH 33/81] DockerTemplateOptions: use the correct return type --- .../options/DockerTemplateOptions.java | 66 +++++++++---------- 1 file changed, 32 insertions(+), 34 deletions(-) diff --git a/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java b/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java index 73699d8af0..85e1c05112 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java +++ b/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java @@ -16,20 +16,20 @@ */ package org.jclouds.docker.compute.options; -import com.google.common.base.Objects; -import com.google.common.base.Optional; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; +import static com.google.common.base.Objects.equal; +import static com.google.common.base.Preconditions.checkNotNull; +import java.util.List; +import java.util.Map; + import org.jclouds.compute.options.TemplateOptions; import org.jclouds.domain.LoginCredentials; import org.jclouds.javax.annotation.Nullable; import org.jclouds.scriptbuilder.domain.Statement; -import java.util.List; -import java.util.Map; - -import static com.google.common.base.Objects.equal; -import static com.google.common.base.Preconditions.checkNotNull; +import com.google.common.base.Objects; +import com.google.common.base.Optional; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; /** * Contains options supported in the {@code ComputeService#runNode} operation on the @@ -138,31 +138,31 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable return this; } - public TemplateOptions dns(@Nullable String dns) { + public DockerTemplateOptions dns(@Nullable String dns) { this.dns = Optional.fromNullable(dns); return this; } - public TemplateOptions hostname(@Nullable String hostname) { + public DockerTemplateOptions hostname(@Nullable String hostname) { this.hostname = Optional.fromNullable(hostname); return this; } - public TemplateOptions memory(@Nullable Integer memory) { + public DockerTemplateOptions memory(@Nullable Integer memory) { this.memory = Optional.fromNullable(memory); return this; } - public TemplateOptions commands(Iterable commands) { + public DockerTemplateOptions commands(Iterable commands) { this.commands = Optional.>of(ImmutableList.copyOf(checkNotNull(commands, "commands"))); return this; } - public TemplateOptions commands(String...commands) { + public DockerTemplateOptions commands(String...commands) { return commands(ImmutableList.copyOf(checkNotNull(commands, "commands"))); } - public TemplateOptions cpuShares(@Nullable Integer cpuShares) { + public DockerTemplateOptions cpuShares(@Nullable Integer cpuShares) { this.cpuShares = Optional.fromNullable(cpuShares); return this; } @@ -214,7 +214,7 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable */ public static DockerTemplateOptions volumes(Map volumes) { DockerTemplateOptions options = new DockerTemplateOptions(); - return DockerTemplateOptions.class.cast(options.volumes(volumes)); + return options.volumes(volumes); } /** @@ -222,7 +222,7 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable */ public static DockerTemplateOptions dns(String dns) { DockerTemplateOptions options = new DockerTemplateOptions(); - return DockerTemplateOptions.class.cast(options.dns(dns)); + return options.dns(dns); } /** @@ -230,15 +230,15 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable */ public static DockerTemplateOptions hostname(String hostname) { DockerTemplateOptions options = new DockerTemplateOptions(); - return DockerTemplateOptions.class.cast(options.hostname(hostname)); + return options.hostname(hostname); } /** - * @see DockerTemplateOptions#memory(int) + * @see DockerTemplateOptions#memory */ public static DockerTemplateOptions memory(int memory) { DockerTemplateOptions options = new DockerTemplateOptions(); - return DockerTemplateOptions.class.cast(options.memory(memory)); + return options.memory(memory); } /** @@ -246,7 +246,7 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable */ public static DockerTemplateOptions commands(String...commands) { DockerTemplateOptions options = new DockerTemplateOptions(); - return DockerTemplateOptions.class.cast(options.commands(commands)); + return options.commands(commands); } /** @@ -258,11 +258,11 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable } /** - * @see DockerTemplateOptions#cpuShares(int) + * @see DockerTemplateOptions#cpuShares */ public static DockerTemplateOptions cpuShares(int cpuShares) { DockerTemplateOptions options = new DockerTemplateOptions(); - return DockerTemplateOptions.class.cast(options.cpuShares(cpuShares)); + return options.cpuShares(cpuShares); } /** @@ -278,7 +278,7 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable */ public static DockerTemplateOptions env(Iterable env) { DockerTemplateOptions options = new DockerTemplateOptions(); - return DockerTemplateOptions.class.cast(options.env(env)); + return options.env(env); } /** @@ -286,17 +286,15 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable */ public static DockerTemplateOptions portBindings(Map portBindings) { DockerTemplateOptions options = new DockerTemplateOptions(); - return DockerTemplateOptions.class.cast(options.portBindings(portBindings)); + return options.portBindings(portBindings); } - // methods that only facilitate returning the correct object type - /** * @see TemplateOptions#inboundPorts */ public static DockerTemplateOptions inboundPorts(int... ports) { DockerTemplateOptions options = new DockerTemplateOptions(); - return DockerTemplateOptions.class.cast(options.inboundPorts(ports)); + return options.inboundPorts(ports); } /** @@ -304,7 +302,7 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable */ public static DockerTemplateOptions blockOnPort(int port, int seconds) { DockerTemplateOptions options = new DockerTemplateOptions(); - return DockerTemplateOptions.class.cast(options.blockOnPort(port, seconds)); + return options.blockOnPort(port, seconds); } /** @@ -312,7 +310,7 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable */ public static DockerTemplateOptions installPrivateKey(String rsaKey) { DockerTemplateOptions options = new DockerTemplateOptions(); - return DockerTemplateOptions.class.cast(options.installPrivateKey(rsaKey)); + return options.installPrivateKey(rsaKey); } /** @@ -320,7 +318,7 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable */ public static DockerTemplateOptions authorizePublicKey(String rsaKey) { DockerTemplateOptions options = new DockerTemplateOptions(); - return DockerTemplateOptions.class.cast(options.authorizePublicKey(rsaKey)); + return options.authorizePublicKey(rsaKey); } /** @@ -328,7 +326,7 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable */ public static DockerTemplateOptions userMetadata(Map userMetadata) { DockerTemplateOptions options = new DockerTemplateOptions(); - return DockerTemplateOptions.class.cast(options.userMetadata(userMetadata)); + return options.userMetadata(userMetadata); } /** @@ -336,7 +334,7 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable */ public static DockerTemplateOptions nodeNames(Iterable nodeNames) { DockerTemplateOptions options = new DockerTemplateOptions(); - return DockerTemplateOptions.class.cast(options.nodeNames(nodeNames)); + return options.nodeNames(nodeNames); } /** @@ -344,7 +342,7 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable */ public static DockerTemplateOptions networks(Iterable networks) { DockerTemplateOptions options = new DockerTemplateOptions(); - return DockerTemplateOptions.class.cast(options.networks(networks)); + return options.networks(networks); } /** From 26d925c2deb38091286017a74f2cc155000e4d5a Mon Sep 17 00:00:00 2001 From: Andrea Turli Date: Fri, 1 May 2015 16:03:59 +0200 Subject: [PATCH 34/81] add ConnectionSpec.CLEARTEXT for DockerEngine running w/t TLS --- apis/docker/README.md | 2 +- .../options/DockerTemplateOptions.java | 2 -- .../config/DockerOkHttpClientSupplier.java | 9 +++--- .../docker/features/MiscApiLiveTest.java | 31 +++++++------------ 4 files changed, 17 insertions(+), 27 deletions(-) diff --git a/apis/docker/README.md b/apis/docker/README.md index e6e0d9c84f..b3c51a1bc4 100644 --- a/apis/docker/README.md +++ b/apis/docker/README.md @@ -12,7 +12,7 @@ If you are using `boot2docker`, notice that from version v1.3.0 the Docker daemo socket (--tls, or --tlsverify), then you need to import CA certificate into Trusted Certs: - ` keytool -import -trustcacerts -file /Users/andrea/.boot2docker/certs/boot2docker-vm/ca.pem -alias BOOT2DOCKER -keystore $JAVA_HOME/jre/lib/security/cacerts` + `keytool -import -trustcacerts -file /Users/andrea/.boot2docker/certs/boot2docker-vm/ca.pem -alias BOOT2DOCKER -keystore $JAVA_HOME/jre/lib/security/cacerts` by default the passoword is `changeit` diff --git a/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java b/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java index 85e1c05112..45e378c05c 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java +++ b/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java @@ -131,8 +131,6 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable .toString(); } - public static final DockerTemplateOptions NONE = new DockerTemplateOptions(); - public DockerTemplateOptions volumes(Map volumes) { this.volumes = Optional.>of(ImmutableMap.copyOf(checkNotNull(volumes, "volumes"))); return this; diff --git a/apis/docker/src/main/java/org/jclouds/docker/config/DockerOkHttpClientSupplier.java b/apis/docker/src/main/java/org/jclouds/docker/config/DockerOkHttpClientSupplier.java index f8a29b1057..1263dc9b46 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/config/DockerOkHttpClientSupplier.java +++ b/apis/docker/src/main/java/org/jclouds/docker/config/DockerOkHttpClientSupplier.java @@ -16,6 +16,9 @@ */ package org.jclouds.docker.config; +import javax.inject.Inject; +import javax.inject.Singleton; + import org.jclouds.docker.suppliers.SSLContextWithKeysSupplier; import org.jclouds.http.okhttp.OkHttpClientSupplier; @@ -23,8 +26,6 @@ import com.google.common.collect.ImmutableList; import com.squareup.okhttp.ConnectionSpec; import com.squareup.okhttp.OkHttpClient; import com.squareup.okhttp.TlsVersion; -import javax.inject.Inject; -import javax.inject.Singleton; @Singleton public class DockerOkHttpClientSupplier implements OkHttpClientSupplier { @@ -39,10 +40,10 @@ public class DockerOkHttpClientSupplier implements OkHttpClientSupplier { @Override public OkHttpClient get() { OkHttpClient client = new OkHttpClient(); - ConnectionSpec connectionSpecs = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) + ConnectionSpec modernTLS = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) .tlsVersions(TlsVersion.TLS_1_0, TlsVersion.TLS_1_1, TlsVersion.TLS_1_2) .build(); - client.setConnectionSpecs(ImmutableList.of(connectionSpecs)); + client.setConnectionSpecs(ImmutableList.of(modernTLS, ConnectionSpec.CLEARTEXT)); client.setSslSocketFactory(sslContextWithKeysSupplier.get().getSocketFactory()); return client; } diff --git a/apis/docker/src/test/java/org/jclouds/docker/features/MiscApiLiveTest.java b/apis/docker/src/test/java/org/jclouds/docker/features/MiscApiLiveTest.java index 0cbf7a7e77..ce2bc9e1c9 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/features/MiscApiLiveTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/features/MiscApiLiveTest.java @@ -16,41 +16,32 @@ */ package org.jclouds.docker.features; -import org.jclouds.docker.compute.BaseDockerApiLiveTest; -import org.jclouds.docker.options.BuildOptions; -import org.testng.annotations.Test; - -import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNotNull; import java.io.IOException; import java.io.InputStream; import java.net.URISyntaxException; +import org.jclouds.docker.compute.BaseDockerApiLiveTest; +import org.jclouds.docker.options.BuildOptions; +import org.testng.annotations.Test; + import com.google.common.base.Splitter; import com.google.common.collect.Iterables; @Test(groups = "live", testName = "MiscApiLiveTest", singleThreaded = true) public class MiscApiLiveTest extends BaseDockerApiLiveTest { - private static final String API_VERSION = "1.16"; - private static final String VERSION = "1.4.1"; - private static final String GIT_COMMIT = "5bc2ff8"; - private static final String GO_VERSION = "go1.3.3"; - private static final String KERNEL_VERSION = "3.16.7-tinycore64"; - private static final String ARCH = "amd64"; - private static final String OS = "linux"; - private static String imageId; @Test public void testVersion() { - assertEquals(api().getVersion().apiVersion(), API_VERSION); - assertEquals(api().getVersion().version(), VERSION); - assertEquals(api().getVersion().gitCommit(), GIT_COMMIT); - assertEquals(api().getVersion().goVersion(), GO_VERSION); - assertEquals(api().getVersion().kernelVersion(), KERNEL_VERSION); - assertEquals(api().getVersion().arch(), ARCH); - assertEquals(api().getVersion().os(), OS); + assertNotNull(api().getVersion().apiVersion()); + assertNotNull(api().getVersion().version()); + assertNotNull(api().getVersion().gitCommit()); + assertNotNull(api().getVersion().goVersion()); + assertNotNull(api().getVersion().kernelVersion()); + assertNotNull(api().getVersion().arch()); + assertNotNull(api().getVersion().os()); } @Test From 867b7f52e4dc7a0c6b45203c3f679fe5c7be0b58 Mon Sep 17 00:00:00 2001 From: Jakub Bartecek Date: Mon, 4 May 2015 16:45:25 +0200 Subject: [PATCH 35/81] Add option to remove a volume, when removing docker container --- .../options/RemoveContainerOptions.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/apis/docker/src/main/java/org/jclouds/docker/options/RemoveContainerOptions.java b/apis/docker/src/main/java/org/jclouds/docker/options/RemoveContainerOptions.java index 9334e69a4c..7534695c98 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/options/RemoveContainerOptions.java +++ b/apis/docker/src/main/java/org/jclouds/docker/options/RemoveContainerOptions.java @@ -29,6 +29,17 @@ public class RemoveContainerOptions extends BaseHttpRequestOptions { this.queryParameters.put("force", force.toString()); return this; } + + /** + * Remove the volumes associated to the container + * + * @param volume If set to true the volume associated to the container will be removed. + * Otherwise it will not be removed. + */ + public RemoveContainerOptions volume(Boolean volume) { + this.queryParameters.put("v", volume.toString()); + return this; + } public static class Builder { /** @@ -46,5 +57,13 @@ public class RemoveContainerOptions extends BaseHttpRequestOptions { RemoveContainerOptions options = new RemoveContainerOptions(); return options.force(force); } + + /** + * @see RemoveContainerOptions#volume + */ + public static RemoveContainerOptions volume(Boolean volume) { + RemoveContainerOptions options = new RemoveContainerOptions(); + return options.volume(volume); + } } } From be6ce141b661d7f33c51f149efc58ef9a2db6f74 Mon Sep 17 00:00:00 2001 From: Jakub Bartecek Date: Tue, 5 May 2015 10:29:16 +0200 Subject: [PATCH 36/81] Options use boolean primitive instead of object The Boolean object makes passing null value possible, which would cause NPE. --- .../jclouds/docker/options/AttachOptions.java | 30 +++++++++---------- .../jclouds/docker/options/BuildOptions.java | 12 ++++---- .../docker/options/DeleteImageOptions.java | 12 ++++---- .../docker/options/ListContainerOptions.java | 6 ++-- .../docker/options/ListImageOptions.java | 6 ++-- .../options/RemoveContainerOptions.java | 18 +++++------ 6 files changed, 42 insertions(+), 42 deletions(-) diff --git a/apis/docker/src/main/java/org/jclouds/docker/options/AttachOptions.java b/apis/docker/src/main/java/org/jclouds/docker/options/AttachOptions.java index 95fb8b593c..3c21865621 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/options/AttachOptions.java +++ b/apis/docker/src/main/java/org/jclouds/docker/options/AttachOptions.java @@ -25,8 +25,8 @@ public class AttachOptions extends BaseHttpRequestOptions { * When TTY is disabled, the stream is multiplexed to separate stdout and stderr. * @return AttachOptions */ - public AttachOptions stream(Boolean stream) { - this.queryParameters.put("stream", stream.toString()); + public AttachOptions stream(boolean stream) { + this.queryParameters.put("stream", String.valueOf(stream)); return this; } @@ -34,8 +34,8 @@ public class AttachOptions extends BaseHttpRequestOptions { * @param logs require logs to be attached. Default false. * @return AttachOptions */ - public AttachOptions logs(Boolean logs) { - this.queryParameters.put("logs", logs.toString()); + public AttachOptions logs(boolean logs) { + this.queryParameters.put("logs", String.valueOf(logs)); return this; } @@ -43,8 +43,8 @@ public class AttachOptions extends BaseHttpRequestOptions { * @param stdin if stream=true, attach to stdin. Default false * @return AttachOptions */ - public AttachOptions stdin(Boolean stdin) { - this.queryParameters.put("stdin", stdin.toString()); + public AttachOptions stdin(boolean stdin) { + this.queryParameters.put("stdin", String.valueOf(stdin)); return this; } @@ -52,8 +52,8 @@ public class AttachOptions extends BaseHttpRequestOptions { * @param stdout if logs=true, return stdout log, if stream=true, attach to stdout. Default false * @return */ - public AttachOptions stdout(Boolean stdout) { - this.queryParameters.put("stdout", stdout.toString()); + public AttachOptions stdout(boolean stdout) { + this.queryParameters.put("stdout", String.valueOf(stdout)); return this; } @@ -62,8 +62,8 @@ public class AttachOptions extends BaseHttpRequestOptions { * @param stderr if logs=true, return stderr log, if stream=true, attach to stderr. Default false * @return */ - public AttachOptions stderr(Boolean stderr) { - this.queryParameters.put("stderr", stderr.toString()); + public AttachOptions stderr(boolean stderr) { + this.queryParameters.put("stderr", String.valueOf(stderr)); return this; } @@ -72,7 +72,7 @@ public class AttachOptions extends BaseHttpRequestOptions { /** * @see org.jclouds.docker.options.AttachOptions#stream */ - public static AttachOptions stream(Boolean stream) { + public static AttachOptions stream(boolean stream) { AttachOptions options = new AttachOptions(); return options.stream(stream); } @@ -80,7 +80,7 @@ public class AttachOptions extends BaseHttpRequestOptions { /** * @see org.jclouds.docker.options.AttachOptions#logs(Boolean) */ - public static AttachOptions logs(Boolean logs) { + public static AttachOptions logs(boolean logs) { AttachOptions options = new AttachOptions(); return options.logs(logs); } @@ -88,7 +88,7 @@ public class AttachOptions extends BaseHttpRequestOptions { /** * @see org.jclouds.docker.options.AttachOptions#stdin(Boolean) */ - public static AttachOptions stdin(Boolean stdin) { + public static AttachOptions stdin(boolean stdin) { AttachOptions options = new AttachOptions(); return options.stdin(stdin); } @@ -96,7 +96,7 @@ public class AttachOptions extends BaseHttpRequestOptions { /** * @see org.jclouds.docker.options.AttachOptions#stdout(Boolean) */ - public static AttachOptions stdout(Boolean stdout) { + public static AttachOptions stdout(boolean stdout) { AttachOptions options = new AttachOptions(); return options.stdout(stdout); } @@ -104,7 +104,7 @@ public class AttachOptions extends BaseHttpRequestOptions { /** * @see org.jclouds.docker.options.AttachOptions#stderr(Boolean) */ - public static AttachOptions stderr(Boolean stderr) { + public static AttachOptions stderr(boolean stderr) { AttachOptions options = new AttachOptions(); return options.stderr(stderr); } diff --git a/apis/docker/src/main/java/org/jclouds/docker/options/BuildOptions.java b/apis/docker/src/main/java/org/jclouds/docker/options/BuildOptions.java index 14bc4a4829..6653a56c3e 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/options/BuildOptions.java +++ b/apis/docker/src/main/java/org/jclouds/docker/options/BuildOptions.java @@ -25,13 +25,13 @@ public class BuildOptions extends BaseHttpRequestOptions { return this; } - public BuildOptions verbose(Boolean verbose) { - this.queryParameters.put("verbose", verbose.toString()); + public BuildOptions verbose(boolean verbose) { + this.queryParameters.put("verbose", String.valueOf(verbose)); return this; } - public BuildOptions nocache(Boolean nocache) { - this.queryParameters.put("nocache", nocache.toString()); + public BuildOptions nocache(boolean nocache) { + this.queryParameters.put("nocache", String.valueOf(nocache)); return this; } @@ -48,7 +48,7 @@ public class BuildOptions extends BaseHttpRequestOptions { /** * @see BuildOptions#verbose(Boolean) */ - public static BuildOptions verbose(Boolean verbose) { + public static BuildOptions verbose(boolean verbose) { BuildOptions options = new BuildOptions(); return options.verbose(verbose); } @@ -56,7 +56,7 @@ public class BuildOptions extends BaseHttpRequestOptions { /** * @see BuildOptions#nocache(Boolean) */ - public static BuildOptions nocache(Boolean nocache) { + public static BuildOptions nocache(boolean nocache) { BuildOptions options = new BuildOptions(); return options.nocache(nocache); } diff --git a/apis/docker/src/main/java/org/jclouds/docker/options/DeleteImageOptions.java b/apis/docker/src/main/java/org/jclouds/docker/options/DeleteImageOptions.java index 1ac20d98e8..c284fd9a10 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/options/DeleteImageOptions.java +++ b/apis/docker/src/main/java/org/jclouds/docker/options/DeleteImageOptions.java @@ -20,13 +20,13 @@ import org.jclouds.http.options.BaseHttpRequestOptions; public class DeleteImageOptions extends BaseHttpRequestOptions { - public DeleteImageOptions force(Boolean force) { - this.queryParameters.put("force", force.toString()); + public DeleteImageOptions force(boolean force) { + this.queryParameters.put("force", String.valueOf(force)); return this; } - public DeleteImageOptions noPrune(Boolean noPrune) { - this.queryParameters.put("noPrune", noPrune.toString()); + public DeleteImageOptions noPrune(boolean noPrune) { + this.queryParameters.put("noPrune", String.valueOf(noPrune)); return this; } @@ -35,7 +35,7 @@ public class DeleteImageOptions extends BaseHttpRequestOptions { /** * @see DeleteImageOptions#force */ - public static DeleteImageOptions force(Boolean force) { + public static DeleteImageOptions force(boolean force) { DeleteImageOptions options = new DeleteImageOptions(); return options.force(force); } @@ -43,7 +43,7 @@ public class DeleteImageOptions extends BaseHttpRequestOptions { /** * @see DeleteImageOptions#noPrune */ - public static DeleteImageOptions noPrune(Boolean noPrune) { + public static DeleteImageOptions noPrune(boolean noPrune) { DeleteImageOptions options = new DeleteImageOptions(); return options.noPrune(noPrune); } diff --git a/apis/docker/src/main/java/org/jclouds/docker/options/ListContainerOptions.java b/apis/docker/src/main/java/org/jclouds/docker/options/ListContainerOptions.java index 7f73804c65..173f6fba86 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/options/ListContainerOptions.java +++ b/apis/docker/src/main/java/org/jclouds/docker/options/ListContainerOptions.java @@ -20,8 +20,8 @@ import org.jclouds.http.options.BaseHttpRequestOptions; public class ListContainerOptions extends BaseHttpRequestOptions { - public ListContainerOptions all(Boolean all) { - this.queryParameters.put("all", all.toString()); + public ListContainerOptions all(boolean all) { + this.queryParameters.put("all", String.valueOf(all)); return this; } @@ -50,7 +50,7 @@ public class ListContainerOptions extends BaseHttpRequestOptions { /** * @see ListContainerOptions#all */ - public static ListContainerOptions all(Boolean all) { + public static ListContainerOptions all(boolean all) { ListContainerOptions options = new ListContainerOptions(); return options.all(all); } diff --git a/apis/docker/src/main/java/org/jclouds/docker/options/ListImageOptions.java b/apis/docker/src/main/java/org/jclouds/docker/options/ListImageOptions.java index 613eff63ec..f4704d0a9d 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/options/ListImageOptions.java +++ b/apis/docker/src/main/java/org/jclouds/docker/options/ListImageOptions.java @@ -20,8 +20,8 @@ import org.jclouds.http.options.BaseHttpRequestOptions; public class ListImageOptions extends BaseHttpRequestOptions { - public ListImageOptions all(Boolean all) { - this.queryParameters.put("all", all.toString()); + public ListImageOptions all(boolean all) { + this.queryParameters.put("all", String.valueOf(all)); return this; } @@ -29,7 +29,7 @@ public class ListImageOptions extends BaseHttpRequestOptions { /** * @see ListImageOptions#all */ - public static ListImageOptions all(Boolean all) { + public static ListImageOptions all(boolean all) { ListImageOptions options = new ListImageOptions(); return options.all(all); } diff --git a/apis/docker/src/main/java/org/jclouds/docker/options/RemoveContainerOptions.java b/apis/docker/src/main/java/org/jclouds/docker/options/RemoveContainerOptions.java index 7534695c98..f4a67955b8 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/options/RemoveContainerOptions.java +++ b/apis/docker/src/main/java/org/jclouds/docker/options/RemoveContainerOptions.java @@ -20,13 +20,13 @@ import org.jclouds.http.options.BaseHttpRequestOptions; public class RemoveContainerOptions extends BaseHttpRequestOptions { - public RemoveContainerOptions verbose(Boolean verbose) { - this.queryParameters.put("verbose", verbose.toString()); + public RemoveContainerOptions verbose(boolean verbose) { + this.queryParameters.put("verbose", String.valueOf(verbose)); return this; } - public RemoveContainerOptions force(Boolean force) { - this.queryParameters.put("force", force.toString()); + public RemoveContainerOptions force(boolean force) { + this.queryParameters.put("force", String.valueOf(force)); return this; } @@ -36,8 +36,8 @@ public class RemoveContainerOptions extends BaseHttpRequestOptions { * @param volume If set to true the volume associated to the container will be removed. * Otherwise it will not be removed. */ - public RemoveContainerOptions volume(Boolean volume) { - this.queryParameters.put("v", volume.toString()); + public RemoveContainerOptions volume(boolean volume) { + this.queryParameters.put("v", String.valueOf(volume)); return this; } @@ -45,7 +45,7 @@ public class RemoveContainerOptions extends BaseHttpRequestOptions { /** * @see RemoveContainerOptions#verbose */ - public static RemoveContainerOptions verbose(Boolean verbose) { + public static RemoveContainerOptions verbose(boolean verbose) { RemoveContainerOptions options = new RemoveContainerOptions(); return options.verbose(verbose); } @@ -53,7 +53,7 @@ public class RemoveContainerOptions extends BaseHttpRequestOptions { /** * @see RemoveContainerOptions#force */ - public static RemoveContainerOptions force(Boolean force) { + public static RemoveContainerOptions force(boolean force) { RemoveContainerOptions options = new RemoveContainerOptions(); return options.force(force); } @@ -61,7 +61,7 @@ public class RemoveContainerOptions extends BaseHttpRequestOptions { /** * @see RemoveContainerOptions#volume */ - public static RemoveContainerOptions volume(Boolean volume) { + public static RemoveContainerOptions volume(boolean volume) { RemoveContainerOptions options = new RemoveContainerOptions(); return options.volume(volume); } From 4edaf1822639a1f1ee6d7d825f9fcdde6fd39588 Mon Sep 17 00:00:00 2001 From: Andrew Kennedy Date: Thu, 23 Apr 2015 18:40:08 +0100 Subject: [PATCH 37/81] Minor fixes for the Docker provider - Tidy up DockerTemplateOptions and add tests for new options - Make Dns a list of strings in HostConfig - Change template option builder to remove use of optionals - Update tests to verify HostConfig.Dns as list --- .../org/jclouds/docker/DockerApiMetadata.java | 2 +- .../options/DockerTemplateOptions.java | 163 +++++++++--------- .../strategy/DockerComputeServiceAdapter.java | 46 ++--- .../config/DockerOkHttpClientSupplier.java | 6 +- .../org/jclouds/docker/domain/HostConfig.java | 12 +- .../suppliers/SSLContextWithKeysSupplier.java | 7 +- .../options/DockerTemplateOptionsTest.java | 55 ++++-- .../docker/parse/ContainerParseTest.java | 1 + apis/docker/src/test/resources/container.json | 2 +- 9 files changed, 166 insertions(+), 128 deletions(-) diff --git a/apis/docker/src/main/java/org/jclouds/docker/DockerApiMetadata.java b/apis/docker/src/main/java/org/jclouds/docker/DockerApiMetadata.java index 5c105cac51..ea10c70b85 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/DockerApiMetadata.java +++ b/apis/docker/src/main/java/org/jclouds/docker/DockerApiMetadata.java @@ -65,7 +65,7 @@ public class DockerApiMetadata extends BaseHttpApiMetadata { id("docker") .name("Docker API") .identityName("Path to certificate .pem file") - .credentialName("Password to key .pem file") + .credentialName("Path to key .pem file") .documentation(URI.create("https://docs.docker.com/reference/api/docker_remote_api/")) .version("1.16") .defaultEndpoint("https://127.0.0.1:2376") diff --git a/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java b/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java index 45e378c05c..d028ef93f2 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java +++ b/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java @@ -18,43 +18,46 @@ package org.jclouds.docker.compute.options; import static com.google.common.base.Objects.equal; import static com.google.common.base.Preconditions.checkNotNull; + import java.util.List; import java.util.Map; +import org.jclouds.compute.ComputeService; import org.jclouds.compute.options.TemplateOptions; import org.jclouds.domain.LoginCredentials; import org.jclouds.javax.annotation.Nullable; import org.jclouds.scriptbuilder.domain.Statement; import com.google.common.base.Objects; -import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; /** - * Contains options supported in the {@code ComputeService#runNode} operation on the - * "docker" provider.

Usage

The recommended way to instantiate a - * DockerTemplateOptions object is to statically import DockerTemplateOptions.* and invoke a static - * creation method followed by an instance mutator (if needed): - *

- * - * import static org.jclouds.docker.compute.options.DockerTemplateOptions.Builder.*; - *

+ * Contains options supported by the {@link ComputeService#createNodesInGroup(String, int, TemplateOptions) createNodes} + * operation on the docker provider. + * + *

Usage

+ * + * The recommended way to instantiate a + * DockerTemplateOptions object is to statically import {@code DockerTemplateOptions.Builder.*} + * and invoke one of the static creation methods, followed by an instance mutator if needed. + * + *
{@code import static org.jclouds.docker.compute.options.DockerTemplateOptions.Builder.*;
+ *
  * ComputeService api = // get connection
  * templateBuilder.options(inboundPorts(22, 80, 8080, 443));
- * Set set = api.createNodesInGroup(tag, 2, templateBuilder.build());
- * 
+ * Set set = api.createNodesInGroup(tag, 2, templateBuilder.build());}
*/ public class DockerTemplateOptions extends TemplateOptions implements Cloneable { - protected Optional dns = Optional.absent(); - protected Optional hostname = Optional.absent(); - protected Optional memory = Optional.absent(); - protected Optional cpuShares = Optional.absent(); - protected Optional> commands = Optional.absent(); - protected Optional> volumes = Optional.absent(); - protected Optional> env = Optional.absent(); - protected Optional> portBindings = Optional.absent(); + protected List dns = ImmutableList.of(); + protected String hostname; + protected Integer memory; + protected Integer cpuShares; + protected List commands = ImmutableList.of(); + protected Map volumes = ImmutableMap.of(); + protected List env = ImmutableList.of(); + protected Map portBindings = ImmutableMap.of(); @Override public DockerTemplateOptions clone() { @@ -68,29 +71,23 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable super.copyTo(to); if (to instanceof DockerTemplateOptions) { DockerTemplateOptions eTo = DockerTemplateOptions.class.cast(to); - if (volumes.isPresent()) { - eTo.volumes(getVolumes().get()); + if (!volumes.isEmpty()) { + eTo.volumes(volumes); } - if (hostname.isPresent()) { - eTo.hostname(hostname.get()); + eTo.hostname(hostname); + if (!dns.isEmpty()) { + eTo.dns(dns); } - if (dns.isPresent()) { - eTo.dns(dns.get()); + eTo.memory(memory); + eTo.cpuShares(cpuShares); + if (commands.isEmpty()) { + eTo.commands(commands); } - if (memory.isPresent()) { - eTo.memory(memory.get()); + if (!env.isEmpty()) { + eTo.env(env); } - if (commands.isPresent()) { - eTo.commands(commands.get()); - } - if (cpuShares.isPresent()) { - eTo.cpuShares(cpuShares.get()); - } - if (env.isPresent()) { - eTo.env(env.get()); - } - if (portBindings.isPresent()) { - eTo.portBindings(portBindings.get()); + if (!portBindings.isEmpty()) { + eTo.portBindings(portBindings); } } } @@ -132,27 +129,31 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable } public DockerTemplateOptions volumes(Map volumes) { - this.volumes = Optional.>of(ImmutableMap.copyOf(checkNotNull(volumes, "volumes"))); + this.volumes = ImmutableMap.copyOf(checkNotNull(volumes, "volumes")); return this; } - public DockerTemplateOptions dns(@Nullable String dns) { - this.dns = Optional.fromNullable(dns); + public DockerTemplateOptions dns(Iterable dns) { + this.dns = ImmutableList.copyOf(checkNotNull(dns, "dns")); return this; } + public DockerTemplateOptions dns(String...dns) { + return dns(ImmutableList.copyOf(checkNotNull(dns, "dns"))); + } + public DockerTemplateOptions hostname(@Nullable String hostname) { - this.hostname = Optional.fromNullable(hostname); + this.hostname = hostname; return this; } public DockerTemplateOptions memory(@Nullable Integer memory) { - this.memory = Optional.fromNullable(memory); + this.memory = memory; return this; } public DockerTemplateOptions commands(Iterable commands) { - this.commands = Optional.>of(ImmutableList.copyOf(checkNotNull(commands, "commands"))); + this.commands = ImmutableList.copyOf(checkNotNull(commands, "commands")); return this; } @@ -161,12 +162,12 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable } public DockerTemplateOptions cpuShares(@Nullable Integer cpuShares) { - this.cpuShares = Optional.fromNullable(cpuShares); + this.cpuShares = cpuShares; return this; } public DockerTemplateOptions env(Iterable env) { - this.env = Optional.>of(ImmutableList.copyOf(checkNotNull(env, "env"))); + this.env = ImmutableList.copyOf(checkNotNull(env, "env")); return this; } @@ -185,25 +186,25 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable * @param portBindings the map of host to container port bindings */ public DockerTemplateOptions portBindings(Map portBindings) { - this.portBindings = Optional.>of(ImmutableMap.copyOf(checkNotNull(portBindings, "portBindings"))); + this.portBindings = ImmutableMap.copyOf(checkNotNull(portBindings, "portBindings")); return this; } - public Optional> getVolumes() { return volumes; } + public Map getVolumes() { return volumes; } - public Optional getDns() { return dns; } + public List getDns() { return dns; } - public Optional getHostname() { return hostname; } + public String getHostname() { return hostname; } - public Optional getMemory() { return memory; } + public Integer getMemory() { return memory; } - public Optional> getCommands() { return commands; } + public List getCommands() { return commands; } - public Optional getCpuShares() { return cpuShares; } + public Integer getCpuShares() { return cpuShares; } - public Optional> getEnv() { return env; } + public List getEnv() { return env; } - public Optional> getPortBindings() { return portBindings; } + public Map getPortBindings() { return portBindings; } public static class Builder { @@ -216,9 +217,17 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable } /** - * @see DockerTemplateOptions#dns(String) + * @see DockerTemplateOptions#dns(String...) */ - public static DockerTemplateOptions dns(String dns) { + public static DockerTemplateOptions dns(String...dns) { + DockerTemplateOptions options = new DockerTemplateOptions(); + return options.dns(dns); + } + + /** + * @see DockerTemplateOptions#dns(Iterable) + */ + public static DockerTemplateOptions dns(Iterable dns) { DockerTemplateOptions options = new DockerTemplateOptions(); return options.dns(dns); } @@ -226,21 +235,21 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable /** * @see DockerTemplateOptions#hostname(String) */ - public static DockerTemplateOptions hostname(String hostname) { + public static DockerTemplateOptions hostname(@Nullable String hostname) { DockerTemplateOptions options = new DockerTemplateOptions(); return options.hostname(hostname); } /** - * @see DockerTemplateOptions#memory + * @see DockerTemplateOptions#memory(Integer) */ - public static DockerTemplateOptions memory(int memory) { + public static DockerTemplateOptions memory(@Nullable Integer memory) { DockerTemplateOptions options = new DockerTemplateOptions(); return options.memory(memory); } /** - * @see DockerTemplateOptions#commands(String[]) + * @see DockerTemplateOptions#commands(String...) */ public static DockerTemplateOptions commands(String...commands) { DockerTemplateOptions options = new DockerTemplateOptions(); @@ -252,23 +261,23 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable */ public static DockerTemplateOptions commands(Iterable commands) { DockerTemplateOptions options = new DockerTemplateOptions(); - return DockerTemplateOptions.class.cast(options.commands(commands)); + return options.commands(commands); } /** - * @see DockerTemplateOptions#cpuShares + * @see DockerTemplateOptions#cpuShares(Integer) */ - public static DockerTemplateOptions cpuShares(int cpuShares) { + public static DockerTemplateOptions cpuShares(@Nullable Integer cpuShares) { DockerTemplateOptions options = new DockerTemplateOptions(); return options.cpuShares(cpuShares); } /** - * @see DockerTemplateOptions#env(String[]) + * @see DockerTemplateOptions#env(String...) */ public static DockerTemplateOptions env(String...env) { DockerTemplateOptions options = new DockerTemplateOptions(); - return DockerTemplateOptions.class.cast(options.env(env)); + return options.env(env); } /** @@ -288,7 +297,7 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable } /** - * @see TemplateOptions#inboundPorts + * @see TemplateOptions#inboundPorts(int...) */ public static DockerTemplateOptions inboundPorts(int... ports) { DockerTemplateOptions options = new DockerTemplateOptions(); @@ -296,7 +305,7 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable } /** - * @see TemplateOptions#port + * @see TemplateOptions#blockOnPort(int, int) */ public static DockerTemplateOptions blockOnPort(int port, int seconds) { DockerTemplateOptions options = new DockerTemplateOptions(); @@ -304,7 +313,7 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable } /** - * @see TemplateOptions#installPrivateKey + * @see TemplateOptions#installPrivateKey(String) */ public static DockerTemplateOptions installPrivateKey(String rsaKey) { DockerTemplateOptions options = new DockerTemplateOptions(); @@ -312,7 +321,7 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable } /** - * @see TemplateOptions#authorizePublicKey + * @see TemplateOptions#authorizePublicKey(String) */ public static DockerTemplateOptions authorizePublicKey(String rsaKey) { DockerTemplateOptions options = new DockerTemplateOptions(); @@ -320,7 +329,7 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable } /** - * @see TemplateOptions#userMetadata + * @see TemplateOptions#userMetadata(Map) */ public static DockerTemplateOptions userMetadata(Map userMetadata) { DockerTemplateOptions options = new DockerTemplateOptions(); @@ -344,7 +353,7 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable } /** - * @see TemplateOptions#overrideLoginUser + * @see TemplateOptions#overrideLoginUser(String) */ public static DockerTemplateOptions overrideLoginUser(String user) { DockerTemplateOptions options = new DockerTemplateOptions(); @@ -352,7 +361,7 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable } /** - * @see TemplateOptions#overrideLoginPassword + * @see TemplateOptions#overrideLoginPassword(String) */ public static DockerTemplateOptions overrideLoginPassword(String password) { DockerTemplateOptions options = new DockerTemplateOptions(); @@ -360,7 +369,7 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable } /** - * @see TemplateOptions#overrideLoginPrivateKey + * @see TemplateOptions#overrideLoginPrivateKey(String) */ public static DockerTemplateOptions overrideLoginPrivateKey(String privateKey) { DockerTemplateOptions options = new DockerTemplateOptions(); @@ -368,7 +377,7 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable } /** - * @see TemplateOptions#overrideAuthenticateSudo + * @see TemplateOptions#overrideAuthenticateSudo(boolean) */ public static DockerTemplateOptions overrideAuthenticateSudo(boolean authenticateSudo) { DockerTemplateOptions options = new DockerTemplateOptions(); @@ -376,7 +385,7 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable } /** - * @see TemplateOptions#overrideLoginCredentials + * @see TemplateOptions#overrideLoginCredentials(LoginCredentials) */ public static DockerTemplateOptions overrideLoginCredentials(LoginCredentials credentials) { DockerTemplateOptions options = new DockerTemplateOptions(); @@ -384,7 +393,7 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable } /** - * @see TemplateOptions#blockUntilRunning + * @see TemplateOptions#blockUntilRunning(boolean) */ public static DockerTemplateOptions blockUntilRunning(boolean blockUntilRunning) { DockerTemplateOptions options = new DockerTemplateOptions(); diff --git a/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java b/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java index 6da2bc0ac4..0aa93cf53c 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java +++ b/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java @@ -97,38 +97,28 @@ public class DockerComputeServiceAdapter implements .image(imageId) .exposedPorts(exposedPorts); - if (templateOptions.getCommands().isPresent()) { - containerConfigBuilder.cmd(templateOptions.getCommands().get()); + if (!templateOptions.getCommands().isEmpty()) { + containerConfigBuilder.cmd(templateOptions.getCommands()); } - if (templateOptions.getMemory().isPresent()) { - containerConfigBuilder.memory(templateOptions.getMemory().get()); + containerConfigBuilder.memory(templateOptions.getMemory()); + + containerConfigBuilder.hostname(templateOptions.getHostname()); + + containerConfigBuilder.cpuShares(templateOptions.getCpuShares()); + + if (!templateOptions.getEnv().isEmpty()) { + containerConfigBuilder.env(templateOptions.getEnv()); } - if (templateOptions.getHostname().isPresent()) { - containerConfigBuilder.hostname(templateOptions.getHostname().get()); - } - - if (templateOptions.getCpuShares().isPresent()) { - containerConfigBuilder.cpuShares(templateOptions.getCpuShares().get()); - } - - if (templateOptions.getEnv().isPresent()) { - containerConfigBuilder.env(templateOptions.getEnv().get()); - } - - if (templateOptions.getVolumes().isPresent()) { + if (!templateOptions.getVolumes().isEmpty()) { Map volumes = Maps.newLinkedHashMap(); - for (String containerDir : templateOptions.getVolumes().get().values()) { + for (String containerDir : templateOptions.getVolumes().values()) { volumes.put(containerDir, Maps.newHashMap()); } 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); @@ -139,21 +129,21 @@ public class DockerComputeServiceAdapter implements .publishAllPorts(true) .privileged(true); - if (templateOptions.getPortBindings().isPresent()) { + if (!templateOptions.getPortBindings().isEmpty()) { Map>> portBindings = Maps.newHashMap(); - for (Map.Entry entry : templateOptions.getPortBindings().get().entrySet()) { + for (Map.Entry entry : templateOptions.getPortBindings().entrySet()) { portBindings.put(entry.getValue() + "/tcp", Lists.>newArrayList(ImmutableMap.of("HostPort", Integer.toString(entry.getKey())))); } hostConfigBuilder.portBindings(portBindings); } - if (templateOptions.getDns().isPresent()) { - hostConfigBuilder.dns(templateOptions.getDns().get()); + if (!templateOptions.getDns().isEmpty()) { + hostConfigBuilder.dns(templateOptions.getDns()); } - if (templateOptions.getVolumes().isPresent()) { - for (Map.Entry entry : templateOptions.getVolumes().get().entrySet()) { + if (!templateOptions.getVolumes().isEmpty()) { + for (Map.Entry entry : templateOptions.getVolumes().entrySet()) { hostConfigBuilder.binds(ImmutableList.of(entry.getKey() + ":" + entry.getValue())); } } diff --git a/apis/docker/src/main/java/org/jclouds/docker/config/DockerOkHttpClientSupplier.java b/apis/docker/src/main/java/org/jclouds/docker/config/DockerOkHttpClientSupplier.java index 1263dc9b46..3888b98875 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/config/DockerOkHttpClientSupplier.java +++ b/apis/docker/src/main/java/org/jclouds/docker/config/DockerOkHttpClientSupplier.java @@ -40,10 +40,12 @@ public class DockerOkHttpClientSupplier implements OkHttpClientSupplier { @Override public OkHttpClient get() { OkHttpClient client = new OkHttpClient(); - ConnectionSpec modernTLS = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) + ConnectionSpec tlsSpec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) .tlsVersions(TlsVersion.TLS_1_0, TlsVersion.TLS_1_1, TlsVersion.TLS_1_2) .build(); - client.setConnectionSpecs(ImmutableList.of(modernTLS, ConnectionSpec.CLEARTEXT)); + ConnectionSpec cleartextSpec = new ConnectionSpec.Builder(ConnectionSpec.CLEARTEXT) + .build(); + client.setConnectionSpecs(ImmutableList.of(tlsSpec, cleartextSpec)); client.setSslSocketFactory(sslContextWithKeysSupplier.get().getSocketFactory()); return client; } diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/HostConfig.java b/apis/docker/src/main/java/org/jclouds/docker/domain/HostConfig.java index 30efd9cd97..8922d18c5b 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/HostConfig.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/HostConfig.java @@ -39,7 +39,7 @@ public abstract class HostConfig { public abstract boolean privileged(); - @Nullable public abstract String dns(); + public abstract List dns(); @Nullable public abstract String dnsSearch(); @@ -57,9 +57,9 @@ public abstract class HostConfig { @SerializedNames({ "ContainerIDFile", "Binds", "LxcConf", "Privileged", "Dns", "DnsSearch", "PortBindings", "Links", "PublishAllPorts", "VolumesFrom" }) public static HostConfig create(String containerIDFile, List binds, List> lxcConf, - boolean privileged, String dns, String dnsSearch, Map>> portBindings, + boolean privileged, List dns, String dnsSearch, Map>> portBindings, List links, boolean publishAllPorts, List volumesFrom) { - return new AutoValue_HostConfig(containerIDFile, copyOf(binds), copyOf(lxcConf), privileged, dns, dnsSearch, + return new AutoValue_HostConfig(containerIDFile, copyOf(binds), copyOf(lxcConf), privileged, copyOf(dns), dnsSearch, copyOf(portBindings), copyOf(links), publishAllPorts, copyOf(volumesFrom)); } @@ -77,7 +77,7 @@ public abstract class HostConfig { private List binds = Lists.newArrayList(); private List> lxcConf = Lists.newArrayList(); private boolean privileged; - private String dns; + private List dns = Lists.newArrayList(); private String dnsSearch; private Map>> portBindings = Maps.newLinkedHashMap(); private List links = Lists.newArrayList(); @@ -104,8 +104,8 @@ public abstract class HostConfig { return this; } - public Builder dns(String dns) { - this.dns = dns; + public Builder dns(List dns) { + this.dns.addAll(checkNotNull(dns, "dns")); return this; } diff --git a/apis/docker/src/main/java/org/jclouds/docker/suppliers/SSLContextWithKeysSupplier.java b/apis/docker/src/main/java/org/jclouds/docker/suppliers/SSLContextWithKeysSupplier.java index 7aeee187e3..83ccacd9b3 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/suppliers/SSLContextWithKeysSupplier.java +++ b/apis/docker/src/main/java/org/jclouds/docker/suppliers/SSLContextWithKeysSupplier.java @@ -18,6 +18,7 @@ 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.IOException; @@ -49,6 +50,7 @@ import org.jclouds.domain.Credentials; import org.jclouds.http.HttpUtils; import org.jclouds.http.config.SSLModule.TrustAllCerts; import org.jclouds.location.Provider; +import org.jclouds.util.Closeables2; import com.google.common.base.Charsets; import com.google.common.base.Supplier; @@ -95,9 +97,8 @@ public class SSLContextWithKeysSupplier implements Supplier { } private static PrivateKey getKey(String privateKey) { - + PEMParser pemParser = new PEMParser(new StringReader(privateKey)); try { - PEMParser pemParser = new PEMParser(new StringReader(privateKey)); Object object = pemParser.readObject(); if (Security.getProvider("BC") == null) { Security.addProvider(new BouncyCastleProvider()); @@ -107,6 +108,8 @@ public class SSLContextWithKeysSupplier implements Supplier { return keyPair.getPrivate(); } catch (IOException ex) { throw new RuntimeException("Invalid private key", ex); + } finally { + Closeables2.closeQuietly(pemParser); } } diff --git a/apis/docker/src/test/java/org/jclouds/docker/compute/options/DockerTemplateOptionsTest.java b/apis/docker/src/test/java/org/jclouds/docker/compute/options/DockerTemplateOptionsTest.java index 3a982288b8..30a992b06d 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/compute/options/DockerTemplateOptionsTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/compute/options/DockerTemplateOptionsTest.java @@ -21,7 +21,7 @@ import static org.testng.Assert.assertEquals; import org.jclouds.compute.options.TemplateOptions; import org.testng.annotations.Test; -import com.google.common.base.Optional; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; /** @@ -32,31 +32,64 @@ public class DockerTemplateOptionsTest { @Test public void testHostname() { - TemplateOptions options = new DockerTemplateOptions().hostname("hostname"); - assertEquals(options.as(DockerTemplateOptions.class).getHostname(), Optional.of("hostname")); + TemplateOptions options = DockerTemplateOptions.Builder.hostname("hostname"); + assertEquals(options.as(DockerTemplateOptions.class).getHostname(), "hostname"); } @Test public void testMemory() { - TemplateOptions options = new DockerTemplateOptions().memory(1024); - assertEquals(options.as(DockerTemplateOptions.class).getMemory(), Optional.of(1024)); + TemplateOptions options = DockerTemplateOptions.Builder.memory(1024); + assertEquals(options.as(DockerTemplateOptions.class).getMemory(), Integer.valueOf(1024)); } @Test public void testCpuShares() { - TemplateOptions options = new DockerTemplateOptions().cpuShares(2); - assertEquals(options.as(DockerTemplateOptions.class).getCpuShares(), Optional.of(2)); + TemplateOptions options = DockerTemplateOptions.Builder.cpuShares(2); + assertEquals(options.as(DockerTemplateOptions.class).getCpuShares(), Integer.valueOf(2)); } @Test public void testVolumes() { - TemplateOptions options = new DockerTemplateOptions().volumes(ImmutableMap.of("/tmp", "/tmp")); - assertEquals(options.as(DockerTemplateOptions.class).getVolumes(), Optional.of(ImmutableMap.of("/tmp", "/tmp"))); + TemplateOptions options = DockerTemplateOptions.Builder.volumes(ImmutableMap.of("/tmp", "/tmp")); + assertEquals(options.as(DockerTemplateOptions.class).getVolumes(), ImmutableMap.of("/tmp", "/tmp")); } @Test public void testDns() { - TemplateOptions options = new DockerTemplateOptions().dns("8.8.8.8"); - assertEquals(options.as(DockerTemplateOptions.class).getDns(), Optional.of("8.8.8.8")); + TemplateOptions options = DockerTemplateOptions.Builder.dns("8.8.8.8", "8.8.4.4"); + assertEquals(options.as(DockerTemplateOptions.class).getDns(), ImmutableList.of("8.8.8.8", "8.8.4.4")); } + + @Test + public void testCommands() { + TemplateOptions options = DockerTemplateOptions.Builder.commands("chmod 666 /etc/*", "rm -rf /var/run"); + assertEquals(options.as(DockerTemplateOptions.class).getCommands(), ImmutableList.of("chmod 666 /etc/*", "rm -rf /var/run")); + } + + @Test + public void testEnv() { + TemplateOptions options = DockerTemplateOptions.Builder.env(ImmutableList.of("HOST=abc", "PORT=1234")); + assertEquals(options.as(DockerTemplateOptions.class).getEnv(), ImmutableList.of("HOST=abc", "PORT=1234")); + } + + @Test + public void testPortBindings() { + TemplateOptions options = DockerTemplateOptions.Builder.portBindings(ImmutableMap.builder().put(8443, 443).put(8080, 80).build()); + assertEquals(options.as(DockerTemplateOptions.class).getPortBindings(), ImmutableMap.builder().put(8443, 443).put(8080, 80).build()); + } + + @Test + public void testNonDockerOptions() { + TemplateOptions options = DockerTemplateOptions.Builder.userMetadata(ImmutableMap.of("key", "value")).cpuShares(1); + assertEquals(options.as(DockerTemplateOptions.class).getUserMetadata(), ImmutableMap.of("key", "value")); + assertEquals(options.as(DockerTemplateOptions.class).getCpuShares(), Integer.valueOf(1)); + } + + @Test + public void testMultipleOptions() { + TemplateOptions options = DockerTemplateOptions.Builder.memory(512).cpuShares(4); + assertEquals(options.as(DockerTemplateOptions.class).getMemory(), Integer.valueOf(512)); + assertEquals(options.as(DockerTemplateOptions.class).getCpuShares(), Integer.valueOf(4)); + } + } diff --git a/apis/docker/src/test/java/org/jclouds/docker/parse/ContainerParseTest.java b/apis/docker/src/test/java/org/jclouds/docker/parse/ContainerParseTest.java index dc5c791b03..96354e12f3 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/parse/ContainerParseTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/parse/ContainerParseTest.java @@ -82,6 +82,7 @@ public class ContainerParseTest extends BaseDockerParseTest { "6783/tcp", ImmutableList.>of(ImmutableMap.of("HostIp", "", "HostPort", "6783")), "6783/udp", ImmutableList.>of(ImmutableMap.of("HostIp", "", "HostPort", "6783"))) ) + .dns(ImmutableList.of("8.8.8.8", "8.8.4.4")) .privileged(true) .build()) .driver("aufs") diff --git a/apis/docker/src/test/resources/container.json b/apis/docker/src/test/resources/container.json index 1356e9fa91..0b85764c39 100644 --- a/apis/docker/src/test/resources/container.json +++ b/apis/docker/src/test/resources/container.json @@ -56,7 +56,7 @@ "CapDrop": null, "ContainerIDFile": "", "Devices": [], - "Dns": null, + "Dns": [ "8.8.8.8", "8.8.4.4" ], "DnsSearch": null, "ExtraHosts": null, "Links": null, From 55a7d8ce3cdd293667202515e52c25691ac83759 Mon Sep 17 00:00:00 2001 From: Csaba Palfi Date: Mon, 13 Jul 2015 22:26:26 +0200 Subject: [PATCH 38/81] [docker] upgrade to docker 1.7 and refactor TLS support --- apis/docker/README.md | 10 +- apis/docker/pom.xml | 33 +++- .../org/jclouds/docker/DockerApiMetadata.java | 3 + .../docker/config/DockerHttpApiModule.java | 17 +- .../config/DockerOkHttpClientSupplier.java | 36 ++-- .../org/jclouds/docker/domain/Config.java | 37 ++-- .../java/org/jclouds/docker/domain/Info.java | 16 +- .../suppliers/DockerSSLContextSupplier.java | 64 +++++++ .../DockerUntrustedSSLContextSupplier.java | 60 ++++++ .../docker/suppliers/SSLContextBuilder.java | 174 ++++++++++++++++++ .../suppliers/SSLContextWithKeysSupplier.java | 166 ----------------- .../docker/compute/BaseDockerApiLiveTest.java | 4 +- .../docker/features/ContainerApiLiveTest.java | 2 +- .../jclouds/docker/parse/InfoParseTest.java | 8 +- apis/docker/src/test/resources/info.json | 8 +- 15 files changed, 402 insertions(+), 236 deletions(-) create mode 100644 apis/docker/src/main/java/org/jclouds/docker/suppliers/DockerSSLContextSupplier.java create mode 100644 apis/docker/src/main/java/org/jclouds/docker/suppliers/DockerUntrustedSSLContextSupplier.java create mode 100644 apis/docker/src/main/java/org/jclouds/docker/suppliers/SSLContextBuilder.java delete mode 100644 apis/docker/src/main/java/org/jclouds/docker/suppliers/SSLContextWithKeysSupplier.java diff --git a/apis/docker/README.md b/apis/docker/README.md index b3c51a1bc4..c5080e6ef8 100644 --- a/apis/docker/README.md +++ b/apis/docker/README.md @@ -8,15 +8,11 @@ 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 import CA certificate into Trusted Certs: - - `keytool -import -trustcacerts -file /Users/andrea/.boot2docker/certs/boot2docker-vm/ca.pem -alias BOOT2DOCKER -keystore $JAVA_HOME/jre/lib/security/cacerts` +If you are using `boot2docker` then it can also manage certificates and help you setup `DOCKER_CERT_PATH` and `DOCKER_HOST` environment variables. (See `boot2docker shellinit`) -by default the passoword is `changeit` +Assuming these environment variables are setup correctly there are no further setups steps are required. -N.B.: From `Docker 1.3.2+` the server doesn't accept sslv3 protocol (https://github.com/docker/docker/pull/8588/files) +Live tests then can now be run: `mvn -Plive integration-test` #How it works diff --git a/apis/docker/pom.xml b/apis/docker/pom.xml index aebfce30c9..d93d43ba18 100644 --- a/apis/docker/pom.xml +++ b/apis/docker/pom.xml @@ -34,10 +34,11 @@ bundle - https://localhost:4243 - 1.15 - FIXME - FIXME + 1.19 + ${env.DOCKER_CERT_PATH}/cert.pem + ${env.DOCKER_CERT_PATH}/key.pem + ${env.DOCKER_CERT_PATH}/ca.pem + false org.jclouds.docker*;version="${project.version}" org.jclouds.compute.internal;version="${project.version}", @@ -134,6 +135,26 @@ live + + org.codehaus.mojo + build-helper-maven-plugin + 1.9.1 + + + regex-property + + regex-property + + + test.docker.endpoint + ${env.DOCKER_HOST} + tcp + https + false + + + + org.apache.maven.plugins maven-surefire-plugin @@ -149,8 +170,10 @@ ${test.docker.endpoint} ${test.docker.api-version} - ${test.docker.identity} + ${test.docker.identity} ${test.docker.credential} + ${test.docker.cacert.path} + ${test.jclouds.trust-all-certs} diff --git a/apis/docker/src/main/java/org/jclouds/docker/DockerApiMetadata.java b/apis/docker/src/main/java/org/jclouds/docker/DockerApiMetadata.java index ea10c70b85..65855419f5 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/DockerApiMetadata.java +++ b/apis/docker/src/main/java/org/jclouds/docker/DockerApiMetadata.java @@ -37,6 +37,8 @@ import static org.jclouds.reflect.Reflection2.typeToken; @AutoService(ApiMetadata.class) public class DockerApiMetadata extends BaseHttpApiMetadata { + public static final String DOCKER_CA_CERT_PATH = "docker.cacert.path"; + @Override public Builder toBuilder() { return new Builder().fromApiMetadata(this); @@ -55,6 +57,7 @@ public class DockerApiMetadata extends BaseHttpApiMetadata { properties.setProperty(Constants.PROPERTY_CONNECTION_TIMEOUT, "1200000"); // 15 minutes properties.setProperty(ComputeServiceProperties.IMAGE_LOGIN_USER, "root:password"); properties.setProperty(TEMPLATE, "osFamily=UBUNTU,os64Bit=true"); + properties.setProperty(DOCKER_CA_CERT_PATH, ""); return properties; } diff --git a/apis/docker/src/main/java/org/jclouds/docker/config/DockerHttpApiModule.java b/apis/docker/src/main/java/org/jclouds/docker/config/DockerHttpApiModule.java index ed23d795ff..afd8e36d93 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/config/DockerHttpApiModule.java +++ b/apis/docker/src/main/java/org/jclouds/docker/config/DockerHttpApiModule.java @@ -16,8 +16,14 @@ */ package org.jclouds.docker.config; +import com.google.common.base.Supplier; +import com.google.inject.AbstractModule; +import com.google.inject.TypeLiteral; +import com.google.inject.name.Names; +import com.google.inject.util.Modules; import org.jclouds.docker.DockerApi; import org.jclouds.docker.handlers.DockerErrorHandler; +import org.jclouds.docker.suppliers.DockerUntrustedSSLContextSupplier; import org.jclouds.http.HttpErrorHandler; import org.jclouds.http.annotation.ClientError; import org.jclouds.http.annotation.Redirection; @@ -28,6 +34,8 @@ import org.jclouds.http.okhttp.config.OkHttpCommandExecutorServiceModule; import org.jclouds.rest.ConfiguresHttpApi; import org.jclouds.rest.config.HttpApiModule; +import javax.net.ssl.SSLContext; + /** * Configures the Docker connection. */ @@ -48,8 +56,13 @@ public class DockerHttpApiModule extends HttpApiModule { @Override protected void configure() { super.configure(); - install(new OkHttpCommandExecutorServiceModule()); + install(Modules.override(new OkHttpCommandExecutorServiceModule()).with(new AbstractModule() { + @Override + protected void configure() { + bind(new TypeLiteral>() {}).annotatedWith(Names.named("untrusted")).to(DockerUntrustedSSLContextSupplier.class); + } + })); bind(OkHttpClientSupplier.class).to(DockerOkHttpClientSupplier.class); - } + } } diff --git a/apis/docker/src/main/java/org/jclouds/docker/config/DockerOkHttpClientSupplier.java b/apis/docker/src/main/java/org/jclouds/docker/config/DockerOkHttpClientSupplier.java index 3888b98875..6bc171ffd0 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/config/DockerOkHttpClientSupplier.java +++ b/apis/docker/src/main/java/org/jclouds/docker/config/DockerOkHttpClientSupplier.java @@ -19,7 +19,7 @@ package org.jclouds.docker.config; import javax.inject.Inject; import javax.inject.Singleton; -import org.jclouds.docker.suppliers.SSLContextWithKeysSupplier; +import org.jclouds.docker.suppliers.DockerSSLContextSupplier; import org.jclouds.http.okhttp.OkHttpClientSupplier; import com.google.common.collect.ImmutableList; @@ -30,24 +30,24 @@ import com.squareup.okhttp.TlsVersion; @Singleton public class DockerOkHttpClientSupplier implements OkHttpClientSupplier { - private final SSLContextWithKeysSupplier sslContextWithKeysSupplier; + private final DockerSSLContextSupplier dockerSSLContextSupplier; - @Inject - DockerOkHttpClientSupplier(SSLContextWithKeysSupplier sslContextWithKeysSupplier) { - this.sslContextWithKeysSupplier = sslContextWithKeysSupplier; - } + @Inject + DockerOkHttpClientSupplier(DockerSSLContextSupplier dockerSSLContextSupplier) { + this.dockerSSLContextSupplier = dockerSSLContextSupplier; + } - @Override - public OkHttpClient get() { - OkHttpClient client = new OkHttpClient(); - ConnectionSpec tlsSpec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) - .tlsVersions(TlsVersion.TLS_1_0, TlsVersion.TLS_1_1, TlsVersion.TLS_1_2) - .build(); - ConnectionSpec cleartextSpec = new ConnectionSpec.Builder(ConnectionSpec.CLEARTEXT) - .build(); - client.setConnectionSpecs(ImmutableList.of(tlsSpec, cleartextSpec)); - client.setSslSocketFactory(sslContextWithKeysSupplier.get().getSocketFactory()); - return client; - } + @Override + public OkHttpClient get() { + OkHttpClient client = new OkHttpClient(); + ConnectionSpec tlsSpec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) + .tlsVersions(TlsVersion.TLS_1_0, TlsVersion.TLS_1_1, TlsVersion.TLS_1_2) + .build(); + ConnectionSpec cleartextSpec = new ConnectionSpec.Builder(ConnectionSpec.CLEARTEXT) + .build(); + client.setConnectionSpecs(ImmutableList.of(tlsSpec, cleartextSpec)); + client.setSslSocketFactory(dockerSSLContextSupplier.get().getSocketFactory()); + return client; + } } diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/Config.java b/apis/docker/src/main/java/org/jclouds/docker/domain/Config.java index 05dcc4863b..464f562f16 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/Config.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/Config.java @@ -101,8 +101,6 @@ public abstract class Config { @Nullable public abstract String networkMode(); - public abstract Map devices(); - Config() { } @@ -112,7 +110,7 @@ public abstract class Config { "AttachStderr", "Tty", "OpenStdin", "StdinOnce", "Env", "Cmd", "Entrypoint", "Image", "Volumes", "WorkingDir", "NetworkDisabled", "ExposedPorts", "SecurityOpts", "HostConfig", "Binds", "Links", "LxcConf", "PortBindings", "PublishAllPorts", "Privileged", "Dns", "DnsSearch", "VolumesFrom", - "CapAdd", "CapDrop", "RestartPolicy", "NetworkMode", "Devices" + "CapAdd", "CapDrop", "RestartPolicy", "NetworkMode" }) public static Config create(String hostname, String domainname, String user, int memory, int memorySwap, int cpuShares, boolean attachStdin, boolean attachStdout, boolean attachStderr, boolean tty, @@ -121,13 +119,12 @@ public abstract class Config { Map exposedPorts, List securityOpts, HostConfig hostConfig, List binds, List links, List> lxcConf, Map>> portBindings, boolean publishAllPorts, boolean privileged, List dns, String dnsSearch, String volumesFrom, - List capAdd, List capDrop, Map restartPolicy, String networkMode, Map devices) { + List capAdd, List capDrop, Map restartPolicy, String networkMode) { return new AutoValue_Config(hostname, domainname, user, memory, memorySwap, cpuShares, attachStdin, attachStdout, attachStderr, tty, openStdin, stdinOnce, copyOf(env), copyOf(cmd), copyOf(entrypoint), image, copyOf(volumes), workingDir, networkDisabled, copyOf(exposedPorts), copyOf(securityOpts), hostConfig, copyOf(binds), copyOf(links), copyOf(lxcConf), copyOf(portBindings), publishAllPorts, privileged, - copyOf(dns), dnsSearch, volumesFrom, copyOf(capAdd), copyOf(capDrop), copyOf(restartPolicy), networkMode, - copyOf(devices)); + copyOf(dns), dnsSearch, volumesFrom, copyOf(capAdd), copyOf(capDrop), copyOf(restartPolicy), networkMode); } public static Builder builder() { @@ -174,7 +171,6 @@ public abstract class Config { private List capDrop = Lists.newArrayList(); private Map restartPolicy = Maps.newHashMap(); private String networkMode; - private Map devices = Maps.newHashMap(); public Builder hostname(String hostname) { this.hostname = hostname; @@ -191,18 +187,24 @@ public abstract class Config { return this; } - public Builder memory(int memory) { - this.memory = memory; + public Builder memory(Integer memory) { + if (memory != null) { + this.memory = memory; + } return this; } - public Builder memorySwap(int memorySwap) { - this.memorySwap = memorySwap; + public Builder memorySwap(Integer memorySwap) { + if (memorySwap != null) { + this.memorySwap = memorySwap; + } return this; } - public Builder cpuShares(int cpuShares) { - this.cpuShares = cpuShares; + public Builder cpuShares(Integer cpuShares) { + if (cpuShares != null) { + this.cpuShares = cpuShares; + } return this; } @@ -351,17 +353,12 @@ public abstract class Config { return this; } - public Builder devices(Map devices) { - this.devices = devices; - return this; - } - public Config build() { return Config.create(hostname, domainname, user, memory, memorySwap, cpuShares, attachStdin, attachStdout, attachStderr, tty, openStdin, stdinOnce, env, cmd, entrypoint, image, volumes, workingDir, networkDisabled, exposedPorts, securityOpts, hostConfig, binds, links, lxcConf, portBindings, publishAllPorts, privileged, dns, dnsSearch, volumesFrom, capAdd, capDrop, restartPolicy, - networkMode, devices); + networkMode); } public Builder fromConfig(Config in) { @@ -373,7 +370,7 @@ public abstract class Config { .hostConfig(in.hostConfig()).binds(in.binds()).links(in.links()).lxcConf(in.lxcConf()) .portBindings(in.portBindings()).publishAllPorts(in.publishAllPorts()).privileged(in.privileged()) .dns(in.dns()).dnsSearch(in.dnsSearch()).volumesFrom(in.volumesFrom()).capAdd(in.capAdd()) - .capDrop(in.capDrop()).restartPolicy(in.restartPolicy()).networkMode(in.networkMode()).devices(in.devices()); + .capDrop(in.capDrop()).restartPolicy(in.restartPolicy()).networkMode(in.networkMode()); } } diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/Info.java b/apis/docker/src/main/java/org/jclouds/docker/domain/Info.java index c0e2ac70d4..e2b51ca630 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/Info.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/Info.java @@ -28,7 +28,7 @@ public abstract class Info { public abstract int containers(); - public abstract int debug(); + public abstract boolean debug(); public abstract String driver(); @@ -36,7 +36,7 @@ public abstract class Info { public abstract String executionDriver(); - public abstract int iPv4Forwarding(); + public abstract boolean iPv4Forwarding(); public abstract int images(); @@ -48,7 +48,7 @@ public abstract class Info { public abstract String kernelVersion(); - public abstract int memoryLimit(); + public abstract boolean memoryLimit(); public abstract int nEventsListener(); @@ -58,7 +58,7 @@ public abstract class Info { public abstract String operatingSystem(); - public abstract int swapLimit(); + public abstract boolean swapLimit(); public abstract String dockerRootDir(); @@ -96,10 +96,10 @@ public abstract class Info { "NFd", "NGoroutines", "OperatingSystem", "SwapLimit", "DockerRootDir", "Labels", "MemTotal", "NCPU", "ID", "Name" }) - public static Info create(int containers, int debug, String driver, List> driverStatus, - String executionDriver, int iPv4Forwarding, int images, String indexServerAddress, - String initPath, String initSha1, String kernelVersion, int memoryLimit, - int nEventsListener, int nFd, int nGoroutines, String operatingSystem, int swapLimit, + public static Info create(int containers, boolean debug, String driver, List> driverStatus, + String executionDriver, boolean iPv4Forwarding, int images, String indexServerAddress, + String initPath, String initSha1, String kernelVersion, boolean memoryLimit, + int nEventsListener, int nFd, int nGoroutines, String operatingSystem, boolean swapLimit, String dockerRootDir, List labels, long memTotal, int ncpu, String id, String name) { return new AutoValue_Info(containers, debug, driver, driverStatus, executionDriver, iPv4Forwarding, images, indexServerAddress, initPath, initSha1, kernelVersion, memoryLimit, nEventsListener, nFd, nGoroutines, diff --git a/apis/docker/src/main/java/org/jclouds/docker/suppliers/DockerSSLContextSupplier.java b/apis/docker/src/main/java/org/jclouds/docker/suppliers/DockerSSLContextSupplier.java new file mode 100644 index 0000000000..ed901f36dd --- /dev/null +++ b/apis/docker/src/main/java/org/jclouds/docker/suppliers/DockerSSLContextSupplier.java @@ -0,0 +1,64 @@ +/* + * 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 com.google.common.base.Strings; +import com.google.common.base.Supplier; +import org.jclouds.docker.DockerApiMetadata; +import org.jclouds.domain.Credentials; +import org.jclouds.location.Provider; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; +import javax.net.ssl.SSLContext; +import java.io.IOException; +import java.security.GeneralSecurityException; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Throwables.propagate; + +@Singleton +public class DockerSSLContextSupplier implements Supplier { + private final Supplier creds; + private final String caCertPath; + + + @Inject + DockerSSLContextSupplier(@Provider Supplier creds, @Named(DockerApiMetadata.DOCKER_CA_CERT_PATH) String caCertPath) { + this.creds = creds; + this.caCertPath = caCertPath; + } + + @Override + public SSLContext get() { + Credentials currentCreds = checkNotNull(creds.get(), "credential supplier returned null"); + try { + SSLContextBuilder builder = new SSLContextBuilder(); + builder.clientKeyAndCertificate(currentCreds.credential, currentCreds.identity); + if (!Strings.isNullOrEmpty(caCertPath)) { + builder.caCertificate(caCertPath); + } + return builder.build(); + } catch (GeneralSecurityException e) { + throw propagate(e); + } catch (IOException e) { + throw propagate(e); + } + } + +} diff --git a/apis/docker/src/main/java/org/jclouds/docker/suppliers/DockerUntrustedSSLContextSupplier.java b/apis/docker/src/main/java/org/jclouds/docker/suppliers/DockerUntrustedSSLContextSupplier.java new file mode 100644 index 0000000000..d0c9077f51 --- /dev/null +++ b/apis/docker/src/main/java/org/jclouds/docker/suppliers/DockerUntrustedSSLContextSupplier.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.suppliers; + +import com.google.common.base.Supplier; +import org.jclouds.domain.Credentials; +import org.jclouds.http.config.SSLModule; +import org.jclouds.location.Provider; + +import javax.inject.Inject; +import javax.inject.Singleton; +import javax.net.ssl.SSLContext; +import java.io.IOException; +import java.security.GeneralSecurityException; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Throwables.propagate; + +@Singleton +public class DockerUntrustedSSLContextSupplier implements Supplier { + private final Supplier creds; + private final SSLModule.TrustAllCerts insecureTrustManager; + + + @Inject + DockerUntrustedSSLContextSupplier(@Provider Supplier creds, SSLModule.TrustAllCerts insecureTrustManager) { + this.creds = creds; + this.insecureTrustManager = insecureTrustManager; + } + + @Override + public SSLContext get() { + Credentials currentCreds = checkNotNull(creds.get(), "credential supplier returned null"); + try { + SSLContextBuilder builder = new SSLContextBuilder(); + builder.clientKeyAndCertificate(currentCreds.credential, currentCreds.identity); + builder.trustManager(insecureTrustManager); + return builder.build(); + } catch (GeneralSecurityException e) { + throw propagate(e); + } catch (IOException e) { + throw propagate(e); + } + } + +} diff --git a/apis/docker/src/main/java/org/jclouds/docker/suppliers/SSLContextBuilder.java b/apis/docker/src/main/java/org/jclouds/docker/suppliers/SSLContextBuilder.java new file mode 100644 index 0000000000..6030def592 --- /dev/null +++ b/apis/docker/src/main/java/org/jclouds/docker/suppliers/SSLContextBuilder.java @@ -0,0 +1,174 @@ +/* + * 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 com.google.common.base.Charsets; +import com.google.common.io.Files; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.openssl.PEMKeyPair; +import org.bouncycastle.openssl.PEMParser; +import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; +import org.jclouds.util.Closeables2; + +import javax.net.ssl.KeyManager; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509ExtendedKeyManager; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.io.StringReader; +import java.net.Socket; +import java.security.GeneralSecurityException; +import java.security.KeyManagementException; +import java.security.KeyPair; +import java.security.KeyStore; +import java.security.NoSuchAlgorithmException; +import java.security.Principal; +import java.security.PrivateKey; +import java.security.SecureRandom; +import java.security.Security; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; + +import static com.google.common.base.Throwables.propagate; + +public class SSLContextBuilder { + + private KeyManager[] keyManagers; + private TrustManager[] trustManagers; + + public SSLContextBuilder() { } + + public SSLContextBuilder clientKeyAndCertificate(String keyPath, String certPath) throws IOException, CertificateException { + X509Certificate certificate = getCertificate(loadFile(certPath)); + PrivateKey privateKey = getKey(loadFile(keyPath)); + keyManagers = new KeyManager[]{new InMemoryKeyManager(certificate, privateKey)}; + return this; + } + + public SSLContextBuilder caCertificate(String caCertPath){ + trustManagers = getTrustManagerWithCaCert(caCertPath); + return this; + } + + public SSLContextBuilder trustManager(TrustManager trustManager) { + trustManagers = new TrustManager[]{trustManager}; + return this; + } + + public SSLContext build() throws NoSuchAlgorithmException, KeyManagementException { + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(keyManagers, trustManagers, new SecureRandom()); + return sslContext; + } + + private TrustManager[] getTrustManagerWithCaCert(String caCertPath) { + try { + X509Certificate caCert = getCertificate(loadFile(caCertPath)); + KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); + trustStore.load(null, null); + trustStore.setCertificateEntry("ca", caCert); + TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + tmf.init(trustStore); + return tmf.getTrustManagers(); + } catch (GeneralSecurityException e) { + throw propagate(e); + } catch (IOException e) { + throw propagate(e); + } + } + + private static X509Certificate getCertificate(String certificate) { + try { + return (X509Certificate) CertificateFactory.getInstance("X.509").generateCertificate( + new ByteArrayInputStream(certificate.getBytes(Charsets.UTF_8))); + } catch (CertificateException ex) { + throw new RuntimeException("Invalid certificate", ex); + } + } + + private static PrivateKey getKey(String privateKey) { + PEMParser pemParser = new PEMParser(new StringReader(privateKey)); + try { + Object object = pemParser.readObject(); + if (Security.getProvider("BC") == null) { + Security.addProvider(new BouncyCastleProvider()); + } + JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC"); + KeyPair keyPair = converter.getKeyPair((PEMKeyPair) object); + return keyPair.getPrivate(); + } catch (IOException ex) { + throw new RuntimeException("Invalid private key", ex); + } finally { + Closeables2.closeQuietly(pemParser); + } + } + + private static String loadFile(final String filePath) throws IOException { + return Files.toString(new File(filePath), Charsets.UTF_8); + } + + private static class InMemoryKeyManager extends X509ExtendedKeyManager { + private static final String DEFAULT_ALIAS = "docker"; + + private final X509Certificate certificate; + + private final PrivateKey privateKey; + + public InMemoryKeyManager(final X509Certificate certificate, final PrivateKey privateKey) + throws IOException, CertificateException { + this.certificate = certificate; + this.privateKey = privateKey; + } + + @Override + public String chooseClientAlias(final String[] keyType, final Principal[] issuers, + final Socket socket) { + return DEFAULT_ALIAS; + } + + @Override + public String chooseServerAlias(final String keyType, final Principal[] issuers, + final Socket socket) { + return DEFAULT_ALIAS; + } + + @Override + public X509Certificate[] getCertificateChain(final String alias) { + return new X509Certificate[]{certificate}; + } + + @Override + public String[] getClientAliases(final String keyType, final Principal[] issuers) { + return new String[]{DEFAULT_ALIAS}; + } + + @Override + public PrivateKey getPrivateKey(final String alias) { + return privateKey; + } + + @Override + public String[] getServerAliases(final String keyType, final Principal[] issuers) { + return new String[]{DEFAULT_ALIAS}; + } + } + +} diff --git a/apis/docker/src/main/java/org/jclouds/docker/suppliers/SSLContextWithKeysSupplier.java b/apis/docker/src/main/java/org/jclouds/docker/suppliers/SSLContextWithKeysSupplier.java deleted file mode 100644 index 83ccacd9b3..0000000000 --- a/apis/docker/src/main/java/org/jclouds/docker/suppliers/SSLContextWithKeysSupplier.java +++ /dev/null @@ -1,166 +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.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.IOException; -import java.io.StringReader; -import java.net.Socket; -import java.security.KeyManagementException; -import java.security.KeyPair; -import java.security.NoSuchAlgorithmException; -import java.security.Principal; -import java.security.PrivateKey; -import java.security.SecureRandom; -import java.security.Security; -import java.security.cert.CertificateException; -import java.security.cert.CertificateFactory; -import java.security.cert.X509Certificate; - -import javax.inject.Inject; -import javax.inject.Singleton; -import javax.net.ssl.KeyManager; -import javax.net.ssl.SSLContext; -import javax.net.ssl.TrustManager; -import javax.net.ssl.X509ExtendedKeyManager; - -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.bouncycastle.openssl.PEMKeyPair; -import org.bouncycastle.openssl.PEMParser; -import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; -import org.jclouds.domain.Credentials; -import org.jclouds.http.HttpUtils; -import org.jclouds.http.config.SSLModule.TrustAllCerts; -import org.jclouds.location.Provider; -import org.jclouds.util.Closeables2; - -import com.google.common.base.Charsets; -import com.google.common.base.Supplier; -import com.google.common.io.Files; - -@Singleton -public class SSLContextWithKeysSupplier implements Supplier { - private final TrustManager[] trustManager; - private final Supplier creds; - - @Inject - SSLContextWithKeysSupplier(@Provider Supplier creds, HttpUtils utils, TrustAllCerts trustAllCerts) { - this.trustManager = utils.trustAllCerts() ? new TrustManager[]{trustAllCerts} : null; - this.creds = creds; - } - - @Override - public SSLContext get() { - Credentials currentCreds = checkNotNull(creds.get(), "credential supplier returned null"); - try { - SSLContext sslContext = SSLContext.getInstance("TLS"); - X509Certificate certificate = getCertificate(loadFile(currentCreds.identity)); - PrivateKey privateKey = getKey(loadFile(currentCreds.credential)); - sslContext.init(new KeyManager[]{new InMemoryKeyManager(certificate, privateKey)}, trustManager, new SecureRandom()); - return sslContext; - } catch (NoSuchAlgorithmException e) { - throw propagate(e); - } catch (KeyManagementException e) { - throw propagate(e); - } catch (CertificateException e) { - throw propagate(e); - } catch (IOException e) { - throw propagate(e); - } - } - - private static X509Certificate getCertificate(String certificate) { - try { - return (X509Certificate) CertificateFactory.getInstance("X.509").generateCertificate( - new ByteArrayInputStream(certificate.getBytes(Charsets.UTF_8))); - } catch (CertificateException ex) { - throw new RuntimeException("Invalid certificate", ex); - } - } - - private static PrivateKey getKey(String privateKey) { - PEMParser pemParser = new PEMParser(new StringReader(privateKey)); - try { - Object object = pemParser.readObject(); - if (Security.getProvider("BC") == null) { - Security.addProvider(new BouncyCastleProvider()); - } - JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC"); - KeyPair keyPair = converter.getKeyPair((PEMKeyPair) object); - return keyPair.getPrivate(); - } catch (IOException ex) { - throw new RuntimeException("Invalid private key", ex); - } finally { - Closeables2.closeQuietly(pemParser); - } - } - - private static String loadFile(final String filePath) throws IOException { - return Files.toString(new File(filePath), Charsets.UTF_8); - } - - private static class InMemoryKeyManager extends X509ExtendedKeyManager { - private static final String DEFAULT_ALIAS = "docker"; - - private final X509Certificate certificate; - - private final PrivateKey privateKey; - - public InMemoryKeyManager(final X509Certificate certificate, final PrivateKey privateKey) - throws IOException, CertificateException { - this.certificate = certificate; - this.privateKey = privateKey; - } - - @Override - public String chooseClientAlias(final String[] keyType, final Principal[] issuers, - final Socket socket) { - return DEFAULT_ALIAS; - } - - @Override - public String chooseServerAlias(final String keyType, final Principal[] issuers, - final Socket socket) { - return DEFAULT_ALIAS; - } - - @Override - public X509Certificate[] getCertificateChain(final String alias) { - return new X509Certificate[]{certificate}; - } - - @Override - public String[] getClientAliases(final String keyType, final Principal[] issuers) { - return new String[]{DEFAULT_ALIAS}; - } - - @Override - public PrivateKey getPrivateKey(final String alias) { - return privateKey; - } - - @Override - public String[] getServerAliases(final String keyType, final Principal[] issuers) { - return new String[]{DEFAULT_ALIAS}; - } - } - -} diff --git a/apis/docker/src/test/java/org/jclouds/docker/compute/BaseDockerApiLiveTest.java b/apis/docker/src/test/java/org/jclouds/docker/compute/BaseDockerApiLiveTest.java index 447c9e4d68..5778d9af79 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/compute/BaseDockerApiLiveTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/compute/BaseDockerApiLiveTest.java @@ -29,6 +29,7 @@ import org.jboss.shrinkwrap.api.GenericArchive; import org.jboss.shrinkwrap.api.ShrinkWrap; 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; @@ -56,8 +57,9 @@ public class BaseDockerApiLiveTest extends BaseApiLiveTest { @Override protected Properties setupProperties() { Properties overrides = super.setupProperties(); - overrides.setProperty("jclouds.trust-all-certs", "false"); overrides.setProperty(ComputeServiceProperties.IMAGE_LOGIN_USER, "root:password"); + setIfTestSystemPropertyPresent(overrides, provider + ".cacert.path"); + setIfTestSystemPropertyPresent(overrides, Constants.PROPERTY_TRUST_ALL_CERTS); return overrides; } diff --git a/apis/docker/src/test/java/org/jclouds/docker/features/ContainerApiLiveTest.java b/apis/docker/src/test/java/org/jclouds/docker/features/ContainerApiLiveTest.java index 684f5debb9..23e7e7b78e 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/features/ContainerApiLiveTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/features/ContainerApiLiveTest.java @@ -128,7 +128,7 @@ public class ContainerApiLiveTest extends BaseDockerApiLiveTest { @Test(dependsOnMethods = "testRestartContainer") public void testWaitContainer() { api().stopContainer(container.id(), 1); - assertEquals(api().wait(container.id()).statusCode(), -1); + assertEquals(api().wait(container.id()).statusCode(), 137); } @Test(dependsOnMethods = "testWaitContainer") diff --git a/apis/docker/src/test/java/org/jclouds/docker/parse/InfoParseTest.java b/apis/docker/src/test/java/org/jclouds/docker/parse/InfoParseTest.java index fa31c8bf20..87d149cb21 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/parse/InfoParseTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/parse/InfoParseTest.java @@ -40,25 +40,25 @@ public class InfoParseTest extends BaseDockerParseTest { public Info expected() { return Info.create( 0, // containers - 1, // debug + true, // debug "aufs", // driver ImmutableList.>of( ImmutableList.of("Root Dir", "/mnt/sda1/var/lib/docker/aufs"), ImmutableList.of("Dirs", "46") ), // driverStatus "native-0.2", // ExecutionDriver - 1, // IPv4Forwarding + true, // IPv4Forwarding 46, // Images "https://index.docker.io/v1/", // IndexServerAddress "/usr/local/bin/docker", // InitPath "", // InitSha1 "3.16.7-tinycore64", // KernelVersion - 1, // MemoryLimit + true, // MemoryLimit 0, // NEventsListener 10, // NFd 11, // NGoroutines "Boot2Docker 1.4.1 (TCL 5.4); master : 86f7ec8 - Tue Dec 16 23:11:29 UTC 2014", // OperatingSystem - 1, // SwapLimit + true, // SwapLimit "/mnt/sda1/var/lib/docker", // DockerRootDir null, // Labels 2105585664, // MemTotal diff --git a/apis/docker/src/test/resources/info.json b/apis/docker/src/test/resources/info.json index e869382d52..4c67e6a7ec 100644 --- a/apis/docker/src/test/resources/info.json +++ b/apis/docker/src/test/resources/info.json @@ -1,6 +1,6 @@ { "Containers": 0, - "Debug": 1, + "Debug": true, "DockerRootDir": "/mnt/sda1/var/lib/docker", "Driver": "aufs", "DriverStatus": [ @@ -15,7 +15,7 @@ ], "ExecutionDriver": "native-0.2", "ID": "7V5Y:IQ2M:HWIL:AZJV:HKRD:Q7OZ:3EQA:ZHMO:4LAD:OSIY:YBAA:BSX6", - "IPv4Forwarding": 1, + "IPv4Forwarding": true, "Images": 46, "IndexServerAddress": "https://index.docker.io/v1/", "InitPath": "/usr/local/bin/docker", @@ -23,12 +23,12 @@ "KernelVersion": "3.16.7-tinycore64", "Labels": null, "MemTotal": 2105585664, - "MemoryLimit": 1, + "MemoryLimit": true, "NCPU": 8, "NEventsListener": 0, "NFd": 10, "NGoroutines": 11, "Name": "boot2docker", "OperatingSystem": "Boot2Docker 1.4.1 (TCL 5.4); master : 86f7ec8 - Tue Dec 16 23:11:29 UTC 2014", - "SwapLimit": 1 + "SwapLimit": true } \ No newline at end of file From 586e31805ad710169b29303028289f6ad396725d Mon Sep 17 00:00:00 2001 From: Ignasi Barrera Date: Fri, 14 Aug 2015 10:44:06 +0200 Subject: [PATCH 39/81] [JCLOUDS-898] Allow insecure connection in Docker. Original author was @andreaturli and the original patch was #172. --- .../docker/config/DockerOkHttpClientSupplier.java | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/apis/docker/src/main/java/org/jclouds/docker/config/DockerOkHttpClientSupplier.java b/apis/docker/src/main/java/org/jclouds/docker/config/DockerOkHttpClientSupplier.java index 6bc171ffd0..f9278f0207 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/config/DockerOkHttpClientSupplier.java +++ b/apis/docker/src/main/java/org/jclouds/docker/config/DockerOkHttpClientSupplier.java @@ -16,12 +16,17 @@ */ package org.jclouds.docker.config; +import java.io.File; + import javax.inject.Inject; import javax.inject.Singleton; import org.jclouds.docker.suppliers.DockerSSLContextSupplier; +import org.jclouds.domain.Credentials; import org.jclouds.http.okhttp.OkHttpClientSupplier; +import org.jclouds.location.Provider; +import com.google.common.base.Supplier; import com.google.common.collect.ImmutableList; import com.squareup.okhttp.ConnectionSpec; import com.squareup.okhttp.OkHttpClient; @@ -31,10 +36,12 @@ import com.squareup.okhttp.TlsVersion; public class DockerOkHttpClientSupplier implements OkHttpClientSupplier { private final DockerSSLContextSupplier dockerSSLContextSupplier; + private final Supplier creds; @Inject - DockerOkHttpClientSupplier(DockerSSLContextSupplier dockerSSLContextSupplier) { + DockerOkHttpClientSupplier(DockerSSLContextSupplier dockerSSLContextSupplier, @Provider Supplier creds) { this.dockerSSLContextSupplier = dockerSSLContextSupplier; + this.creds = creds; } @Override @@ -46,7 +53,10 @@ public class DockerOkHttpClientSupplier implements OkHttpClientSupplier { ConnectionSpec cleartextSpec = new ConnectionSpec.Builder(ConnectionSpec.CLEARTEXT) .build(); client.setConnectionSpecs(ImmutableList.of(tlsSpec, cleartextSpec)); - client.setSslSocketFactory(dockerSSLContextSupplier.get().getSocketFactory()); + // check if identity and credential are files, to set up sslContext + if (new File(creds.get().identity).isFile() && new File(creds.get().credential).isFile()) { + client.setSslSocketFactory(dockerSSLContextSupplier.get().getSocketFactory()); + } return client; } From 644541a2f4b9cbdbc68a29108c77f1cf46980657 Mon Sep 17 00:00:00 2001 From: Josef Cacek Date: Sun, 16 Aug 2015 15:13:25 +0200 Subject: [PATCH 40/81] JCLOUDS-990: fix condition DockerTemplateOptions.copyTo() method --- .../compute/options/DockerTemplateOptions.java | 2 +- .../options/DockerTemplateOptionsTest.java | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java b/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java index d028ef93f2..efe0171434 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java +++ b/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java @@ -80,7 +80,7 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable } eTo.memory(memory); eTo.cpuShares(cpuShares); - if (commands.isEmpty()) { + if (!commands.isEmpty()) { eTo.commands(commands); } if (!env.isEmpty()) { diff --git a/apis/docker/src/test/java/org/jclouds/docker/compute/options/DockerTemplateOptionsTest.java b/apis/docker/src/test/java/org/jclouds/docker/compute/options/DockerTemplateOptionsTest.java index 30a992b06d..7badb9271f 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/compute/options/DockerTemplateOptionsTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/compute/options/DockerTemplateOptionsTest.java @@ -92,4 +92,22 @@ public class DockerTemplateOptionsTest { assertEquals(options.as(DockerTemplateOptions.class).getCpuShares(), Integer.valueOf(4)); } + @Test + public void testCopyTo() { + DockerTemplateOptions options = DockerTemplateOptions.Builder + .memory(512) + .cpuShares(4) + .commands("test") + .portBindings( + ImmutableMap. builder() + .put(8443, 443).build()).hostname("hostname") + .userMetadata(ImmutableMap.of("key", "value")) + .env(ImmutableList.of("HOST=abc", "PORT=1234")) + .dns("8.8.8.8", "8.8.4.4") + .volumes(ImmutableMap.of("/tmp", "/tmp")); + DockerTemplateOptions optionsCopy = new DockerTemplateOptions(); + options.copyTo(optionsCopy); + assertEquals(optionsCopy, options); + } + } From 15651822be7a019f55c94b54fcbdc1b473f5eb4c Mon Sep 17 00:00:00 2001 From: Josef Cacek Date: Thu, 17 Sep 2015 08:18:52 +0200 Subject: [PATCH 41/81] JCLOUDS-996 fix location of NetworkMode parameter from Docker Remote API and make it configurable from DockerTemplateOptions --- .../options/DockerTemplateOptions.java | 23 +++++++++++++++++++ .../strategy/DockerComputeServiceAdapter.java | 2 ++ .../org/jclouds/docker/domain/Config.java | 19 ++++----------- .../org/jclouds/docker/domain/HostConfig.java | 20 ++++++++++++---- .../options/DockerTemplateOptionsTest.java | 11 ++++++++- .../docker/parse/ContainerParseTest.java | 1 + 6 files changed, 56 insertions(+), 20 deletions(-) diff --git a/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java b/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java index efe0171434..aac6fac933 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java +++ b/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java @@ -58,6 +58,7 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable protected Map volumes = ImmutableMap.of(); protected List env = ImmutableList.of(); protected Map portBindings = ImmutableMap.of(); + protected String networkMode; @Override public DockerTemplateOptions clone() { @@ -89,6 +90,7 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable if (!portBindings.isEmpty()) { eTo.portBindings(portBindings); } + eTo.networkMode(networkMode); } } @@ -190,6 +192,17 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable return this; } + + /** + * Sets the networking mode for the container. Supported values are: bridge, host, and container:[name|id] + * @param networkMode + * @return this instance + */ + public DockerTemplateOptions networkMode(@Nullable String networkMode) { + this.networkMode = networkMode; + return this; + } + public Map getVolumes() { return volumes; } public List getDns() { return dns; } @@ -206,6 +219,8 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable public Map getPortBindings() { return portBindings; } + public String getNetworkMode() { return networkMode; } + public static class Builder { /** @@ -296,6 +311,14 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable return options.portBindings(portBindings); } + /** + * @see DockerTemplateOptions#hostname(String) + */ + public static DockerTemplateOptions networkMode(@Nullable String networkMode) { + DockerTemplateOptions options = new DockerTemplateOptions(); + return options.networkMode(networkMode); + } + /** * @see TemplateOptions#inboundPorts(int...) */ diff --git a/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java b/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java index 0aa93cf53c..44242c6d2c 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java +++ b/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java @@ -148,6 +148,8 @@ public class DockerComputeServiceAdapter implements } } + hostConfigBuilder.networkMode(templateOptions.getNetworkMode()); + HostConfig hostConfig = hostConfigBuilder.build(); api.getContainerApi().startContainer(container.id(), hostConfig); diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/Config.java b/apis/docker/src/main/java/org/jclouds/docker/domain/Config.java index 464f562f16..b16f4b56c9 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/Config.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/Config.java @@ -99,8 +99,6 @@ public abstract class Config { public abstract Map restartPolicy(); - @Nullable public abstract String networkMode(); - Config() { } @@ -110,7 +108,7 @@ public abstract class Config { "AttachStderr", "Tty", "OpenStdin", "StdinOnce", "Env", "Cmd", "Entrypoint", "Image", "Volumes", "WorkingDir", "NetworkDisabled", "ExposedPorts", "SecurityOpts", "HostConfig", "Binds", "Links", "LxcConf", "PortBindings", "PublishAllPorts", "Privileged", "Dns", "DnsSearch", "VolumesFrom", - "CapAdd", "CapDrop", "RestartPolicy", "NetworkMode" + "CapAdd", "CapDrop", "RestartPolicy" }) public static Config create(String hostname, String domainname, String user, int memory, int memorySwap, int cpuShares, boolean attachStdin, boolean attachStdout, boolean attachStderr, boolean tty, @@ -119,12 +117,12 @@ public abstract class Config { Map exposedPorts, List securityOpts, HostConfig hostConfig, List binds, List links, List> lxcConf, Map>> portBindings, boolean publishAllPorts, boolean privileged, List dns, String dnsSearch, String volumesFrom, - List capAdd, List capDrop, Map restartPolicy, String networkMode) { + List capAdd, List capDrop, Map restartPolicy) { return new AutoValue_Config(hostname, domainname, user, memory, memorySwap, cpuShares, attachStdin, attachStdout, attachStderr, tty, openStdin, stdinOnce, copyOf(env), copyOf(cmd), copyOf(entrypoint), image, copyOf(volumes), workingDir, networkDisabled, copyOf(exposedPorts), copyOf(securityOpts), hostConfig, copyOf(binds), copyOf(links), copyOf(lxcConf), copyOf(portBindings), publishAllPorts, privileged, - copyOf(dns), dnsSearch, volumesFrom, copyOf(capAdd), copyOf(capDrop), copyOf(restartPolicy), networkMode); + copyOf(dns), dnsSearch, volumesFrom, copyOf(capAdd), copyOf(capDrop), copyOf(restartPolicy)); } public static Builder builder() { @@ -170,7 +168,6 @@ public abstract class Config { private List capAdd = Lists.newArrayList(); private List capDrop = Lists.newArrayList(); private Map restartPolicy = Maps.newHashMap(); - private String networkMode; public Builder hostname(String hostname) { this.hostname = hostname; @@ -348,17 +345,11 @@ public abstract class Config { return this; } - public Builder networkMode(String networkMode) { - this.networkMode = networkMode; - return this; - } - public Config build() { return Config.create(hostname, domainname, user, memory, memorySwap, cpuShares, attachStdin, attachStdout, attachStderr, tty, openStdin, stdinOnce, env, cmd, entrypoint, image, volumes, workingDir, networkDisabled, exposedPorts, securityOpts, hostConfig, binds, links, lxcConf, portBindings, - publishAllPorts, privileged, dns, dnsSearch, volumesFrom, capAdd, capDrop, restartPolicy, - networkMode); + publishAllPorts, privileged, dns, dnsSearch, volumesFrom, capAdd, capDrop, restartPolicy); } public Builder fromConfig(Config in) { @@ -370,7 +361,7 @@ public abstract class Config { .hostConfig(in.hostConfig()).binds(in.binds()).links(in.links()).lxcConf(in.lxcConf()) .portBindings(in.portBindings()).publishAllPorts(in.publishAllPorts()).privileged(in.privileged()) .dns(in.dns()).dnsSearch(in.dnsSearch()).volumesFrom(in.volumesFrom()).capAdd(in.capAdd()) - .capDrop(in.capDrop()).restartPolicy(in.restartPolicy()).networkMode(in.networkMode()); + .capDrop(in.capDrop()).restartPolicy(in.restartPolicy()); } } diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/HostConfig.java b/apis/docker/src/main/java/org/jclouds/docker/domain/HostConfig.java index 8922d18c5b..f2c1e8718b 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/HostConfig.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/HostConfig.java @@ -51,16 +51,19 @@ public abstract class HostConfig { public abstract List volumesFrom(); + @Nullable + public abstract String networkMode(); + HostConfig() { } @SerializedNames({ "ContainerIDFile", "Binds", "LxcConf", "Privileged", "Dns", "DnsSearch", "PortBindings", - "Links", "PublishAllPorts", "VolumesFrom" }) + "Links", "PublishAllPorts", "VolumesFrom", "NetworkMode" }) public static HostConfig create(String containerIDFile, List binds, List> lxcConf, boolean privileged, List dns, String dnsSearch, Map>> portBindings, - List links, boolean publishAllPorts, List volumesFrom) { + List links, boolean publishAllPorts, List volumesFrom, String networkMode) { return new AutoValue_HostConfig(containerIDFile, copyOf(binds), copyOf(lxcConf), privileged, copyOf(dns), dnsSearch, - copyOf(portBindings), copyOf(links), publishAllPorts, copyOf(volumesFrom)); + copyOf(portBindings), copyOf(links), publishAllPorts, copyOf(volumesFrom), networkMode); } public static Builder builder() { @@ -83,6 +86,7 @@ public abstract class HostConfig { private List links = Lists.newArrayList(); private boolean publishAllPorts; private List volumesFrom = Lists.newArrayList(); + private String networkMode; public Builder containerIDFile(String containerIDFile) { this.containerIDFile = containerIDFile; @@ -134,15 +138,21 @@ public abstract class HostConfig { return this; } + public Builder networkMode(String networkMode) { + this.networkMode = networkMode; + return this; + } + public HostConfig build() { return HostConfig.create(containerIDFile, binds, lxcConf, privileged, dns, dnsSearch, portBindings, links, - publishAllPorts, volumesFrom); + publishAllPorts, volumesFrom, networkMode); } public Builder fromHostConfig(HostConfig in) { return this.containerIDFile(in.containerIDFile()).binds(in.binds()).lxcConf(in.lxcConf()) .privileged(in.privileged()).dns(in.dns()).dnsSearch(in.dnsSearch()).links(in.links()) - .portBindings(in.portBindings()).publishAllPorts(in.publishAllPorts()).volumesFrom(in.volumesFrom()); + .portBindings(in.portBindings()).publishAllPorts(in.publishAllPorts()).volumesFrom(in.volumesFrom()) + .networkMode(in.networkMode()); } } } diff --git a/apis/docker/src/test/java/org/jclouds/docker/compute/options/DockerTemplateOptionsTest.java b/apis/docker/src/test/java/org/jclouds/docker/compute/options/DockerTemplateOptionsTest.java index 7badb9271f..d20019ab30 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/compute/options/DockerTemplateOptionsTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/compute/options/DockerTemplateOptionsTest.java @@ -78,6 +78,13 @@ public class DockerTemplateOptionsTest { assertEquals(options.as(DockerTemplateOptions.class).getPortBindings(), ImmutableMap.builder().put(8443, 443).put(8080, 80).build()); } + + @Test + public void testNetworkMode() { + TemplateOptions options = DockerTemplateOptions.Builder.networkMode("host"); + assertEquals(options.as(DockerTemplateOptions.class).getNetworkMode(), "host"); + } + @Test public void testNonDockerOptions() { TemplateOptions options = DockerTemplateOptions.Builder.userMetadata(ImmutableMap.of("key", "value")).cpuShares(1); @@ -100,7 +107,9 @@ public class DockerTemplateOptionsTest { .commands("test") .portBindings( ImmutableMap. builder() - .put(8443, 443).build()).hostname("hostname") + .put(8443, 443).build()) + .hostname("hostname") + .networkMode("host") .userMetadata(ImmutableMap.of("key", "value")) .env(ImmutableList.of("HOST=abc", "PORT=1234")) .dns("8.8.8.8", "8.8.4.4") diff --git a/apis/docker/src/test/java/org/jclouds/docker/parse/ContainerParseTest.java b/apis/docker/src/test/java/org/jclouds/docker/parse/ContainerParseTest.java index 96354e12f3..bcbe44b598 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/parse/ContainerParseTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/parse/ContainerParseTest.java @@ -84,6 +84,7 @@ public class ContainerParseTest extends BaseDockerParseTest { ) .dns(ImmutableList.of("8.8.8.8", "8.8.4.4")) .privileged(true) + .networkMode("bridge") .build()) .driver("aufs") .execDriver("native-0.2") From 3901403379a7a2dcedef7945af037e042230514f Mon Sep 17 00:00:00 2001 From: Andrew Kennedy Date: Thu, 17 Sep 2015 13:06:37 +0100 Subject: [PATCH 42/81] Added ExtraHosts option to template --- .../options/DockerTemplateOptions.java | 37 +++++++++++++++++-- .../strategy/DockerComputeServiceAdapter.java | 8 ++++ .../org/jclouds/docker/domain/HostConfig.java | 20 +++++++--- .../docker/parse/ContainerParseTest.java | 1 + apis/docker/src/test/resources/container.json | 2 +- 5 files changed, 57 insertions(+), 11 deletions(-) diff --git a/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java b/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java index aac6fac933..cce2b2217c 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java +++ b/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java @@ -59,6 +59,7 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable protected List env = ImmutableList.of(); protected Map portBindings = ImmutableMap.of(); protected String networkMode; + protected Map extraHosts = ImmutableMap.of(); @Override public DockerTemplateOptions clone() { @@ -91,6 +92,9 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable eTo.portBindings(portBindings); } eTo.networkMode(networkMode); + if (!extraHosts.isEmpty()) { + eTo.extraHosts(extraHosts); + } } } @@ -108,12 +112,13 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable equal(this.commands, that.commands) && equal(this.cpuShares, that.cpuShares) && equal(this.env, that.env) && - equal(this.portBindings, that.portBindings); + equal(this.portBindings, that.portBindings) && + equal(this.extraHosts, that.extraHosts); } @Override public int hashCode() { - return Objects.hashCode(super.hashCode(), volumes, hostname, dns, memory, commands, cpuShares, env, portBindings); + return Objects.hashCode(super.hashCode(), volumes, hostname, dns, memory, commands, cpuShares, env, portBindings, extraHosts); } @Override @@ -127,6 +132,7 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable .add("volumes", volumes) .add("env", env) .add("portBindings", portBindings) + .add("extraHosts", extraHosts) .toString(); } @@ -192,7 +198,6 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable return this; } - /** * Sets the networking mode for the container. Supported values are: bridge, host, and container:[name|id] * @param networkMode @@ -203,6 +208,20 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable return this; } + /** + * Set extra hosts file entries for a container. + *

+ * The {@link Map} keys are host names, and the value is an IP address that + * can be accessed by the container. This is the same order as the arguments for the + * {@code --add-host} command-line option to {@code docker run}. + * + * @param extraHosts the map of host names to IP addresses + */ + public DockerTemplateOptions extraHosts(Map extraHosts) { + this.extraHosts = ImmutableMap.copyOf(checkNotNull(extraHosts, "extraHosts")); + return this; + } + public Map getVolumes() { return volumes; } public List getDns() { return dns; } @@ -221,6 +240,8 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable public String getNetworkMode() { return networkMode; } + public Map getExtraHosts() { return extraHosts; } + public static class Builder { /** @@ -312,13 +333,21 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable } /** - * @see DockerTemplateOptions#hostname(String) + * @see DockerTemplateOptions#networkMode(String) */ public static DockerTemplateOptions networkMode(@Nullable String networkMode) { DockerTemplateOptions options = new DockerTemplateOptions(); return options.networkMode(networkMode); } + /** + * @see DockerTemplateOptions#extraHosts(Map) + */ + public static DockerTemplateOptions extraHosts(Map extraHosts) { + DockerTemplateOptions options = new DockerTemplateOptions(); + return options.extraHosts(extraHosts); + } + /** * @see TemplateOptions#inboundPorts(int...) */ diff --git a/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java b/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java index 44242c6d2c..62ea752372 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java +++ b/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java @@ -142,6 +142,14 @@ public class DockerComputeServiceAdapter implements hostConfigBuilder.dns(templateOptions.getDns()); } + if (!templateOptions.getExtraHosts().isEmpty()) { + List extraHosts = Lists.newArrayList(); + for (Map.Entry entry : templateOptions.getExtraHosts().entrySet()) { + extraHosts.add(entry.getKey() + ":" + entry.getValue()); + } + hostConfigBuilder.extraHosts(extraHosts); + } + if (!templateOptions.getVolumes().isEmpty()) { for (Map.Entry entry : templateOptions.getVolumes().entrySet()) { hostConfigBuilder.binds(ImmutableList.of(entry.getKey() + ":" + entry.getValue())); diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/HostConfig.java b/apis/docker/src/main/java/org/jclouds/docker/domain/HostConfig.java index f2c1e8718b..17a4f380f8 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/HostConfig.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/HostConfig.java @@ -47,6 +47,8 @@ public abstract class HostConfig { public abstract List links(); + public abstract List extraHosts(); + public abstract boolean publishAllPorts(); public abstract List volumesFrom(); @@ -58,12 +60,12 @@ public abstract class HostConfig { } @SerializedNames({ "ContainerIDFile", "Binds", "LxcConf", "Privileged", "Dns", "DnsSearch", "PortBindings", - "Links", "PublishAllPorts", "VolumesFrom", "NetworkMode" }) + "Links", "ExtraHosts", "PublishAllPorts", "VolumesFrom", "NetworkMode" }) public static HostConfig create(String containerIDFile, List binds, List> lxcConf, boolean privileged, List dns, String dnsSearch, Map>> portBindings, - List links, boolean publishAllPorts, List volumesFrom, String networkMode) { + List links, List extraHosts, boolean publishAllPorts, List volumesFrom, String networkMode) { return new AutoValue_HostConfig(containerIDFile, copyOf(binds), copyOf(lxcConf), privileged, copyOf(dns), dnsSearch, - copyOf(portBindings), copyOf(links), publishAllPorts, copyOf(volumesFrom), networkMode); + copyOf(portBindings), copyOf(links), copyOf(extraHosts), publishAllPorts, copyOf(volumesFrom), networkMode); } public static Builder builder() { @@ -84,6 +86,7 @@ public abstract class HostConfig { private String dnsSearch; private Map>> portBindings = Maps.newLinkedHashMap(); private List links = Lists.newArrayList(); + private List extraHosts = Lists.newArrayList(); private boolean publishAllPorts; private List volumesFrom = Lists.newArrayList(); private String networkMode; @@ -123,6 +126,11 @@ public abstract class HostConfig { return this; } + public Builder extraHosts(List extraHosts) { + this.extraHosts.addAll(checkNotNull(extraHosts, "extraHosts")); + return this; + } + public Builder portBindings(Map>> portBindings) { this.portBindings.putAll(portBindings); return this; @@ -145,14 +153,14 @@ public abstract class HostConfig { public HostConfig build() { return HostConfig.create(containerIDFile, binds, lxcConf, privileged, dns, dnsSearch, portBindings, links, - publishAllPorts, volumesFrom, networkMode); + extraHosts, publishAllPorts, volumesFrom, networkMode); } public Builder fromHostConfig(HostConfig in) { return this.containerIDFile(in.containerIDFile()).binds(in.binds()).lxcConf(in.lxcConf()) .privileged(in.privileged()).dns(in.dns()).dnsSearch(in.dnsSearch()).links(in.links()) - .portBindings(in.portBindings()).publishAllPorts(in.publishAllPorts()).volumesFrom(in.volumesFrom()) - .networkMode(in.networkMode()); + .extraHosts(in.extraHosts()).portBindings(in.portBindings()).publishAllPorts(in.publishAllPorts()) + .volumesFrom(in.volumesFrom()).networkMode(in.networkMode()); } } } diff --git a/apis/docker/src/test/java/org/jclouds/docker/parse/ContainerParseTest.java b/apis/docker/src/test/java/org/jclouds/docker/parse/ContainerParseTest.java index bcbe44b598..ff2e2fe058 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/parse/ContainerParseTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/parse/ContainerParseTest.java @@ -83,6 +83,7 @@ public class ContainerParseTest extends BaseDockerParseTest { "6783/udp", ImmutableList.>of(ImmutableMap.of("HostIp", "", "HostPort", "6783"))) ) .dns(ImmutableList.of("8.8.8.8", "8.8.4.4")) + .extraHosts(ImmutableList.of("extra:169.254.0.1")) .privileged(true) .networkMode("bridge") .build()) diff --git a/apis/docker/src/test/resources/container.json b/apis/docker/src/test/resources/container.json index 0b85764c39..5a13e13fda 100644 --- a/apis/docker/src/test/resources/container.json +++ b/apis/docker/src/test/resources/container.json @@ -58,7 +58,7 @@ "Devices": [], "Dns": [ "8.8.8.8", "8.8.4.4" ], "DnsSearch": null, - "ExtraHosts": null, + "ExtraHosts": [ "extra:169.254.0.1" ], "Links": null, "LxcConf": [], "NetworkMode": "bridge", From e21767dbdebf54669a38b2ac609329d2b917dccb Mon Sep 17 00:00:00 2001 From: Josef Cacek Date: Thu, 17 Sep 2015 18:07:04 +0200 Subject: [PATCH 43/81] JCLOUDS-997 Allow nullable docker configuration - the null has another meaning than empty list/map (e.g. CMD: null=default, emptyList=no-command) --- .../options/DockerTemplateOptions.java | 57 ++++++++++++---- .../strategy/DockerComputeServiceAdapter.java | 13 +--- .../org/jclouds/docker/domain/Config.java | 25 +++---- .../docker/internal/NullSafeCopies.java | 40 +++++++++++- .../options/DockerTemplateOptionsTest.java | 9 ++- .../org/jclouds/docker/domain/ConfigTest.java | 65 +++++++++++++++++++ 6 files changed, 172 insertions(+), 37 deletions(-) create mode 100644 apis/docker/src/test/java/org/jclouds/docker/domain/ConfigTest.java diff --git a/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java b/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java index cce2b2217c..4088a547c9 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java +++ b/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java @@ -24,6 +24,7 @@ import java.util.Map; import org.jclouds.compute.ComputeService; import org.jclouds.compute.options.TemplateOptions; +import org.jclouds.docker.internal.NullSafeCopies; import org.jclouds.domain.LoginCredentials; import org.jclouds.javax.annotation.Nullable; import org.jclouds.scriptbuilder.domain.Statement; @@ -54,9 +55,10 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable protected String hostname; protected Integer memory; protected Integer cpuShares; - protected List commands = ImmutableList.of(); + protected List entrypoint; + protected List commands; protected Map volumes = ImmutableMap.of(); - protected List env = ImmutableList.of(); + protected List env; protected Map portBindings = ImmutableMap.of(); protected String networkMode; protected Map extraHosts = ImmutableMap.of(); @@ -82,12 +84,9 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable } eTo.memory(memory); eTo.cpuShares(cpuShares); - if (!commands.isEmpty()) { - eTo.commands(commands); - } - if (!env.isEmpty()) { - eTo.env(env); - } + eTo.entrypoint(entrypoint); + eTo.commands(commands); + eTo.env(env); if (!portBindings.isEmpty()) { eTo.portBindings(portBindings); } @@ -109,6 +108,7 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable equal(this.hostname, that.hostname) && equal(this.dns, that.dns) && equal(this.memory, that.memory) && + equal(this.entrypoint, that.entrypoint) && equal(this.commands, that.commands) && equal(this.cpuShares, that.cpuShares) && equal(this.env, that.env) && @@ -118,7 +118,7 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable @Override public int hashCode() { - return Objects.hashCode(super.hashCode(), volumes, hostname, dns, memory, commands, cpuShares, env, portBindings, extraHosts); + return Objects.hashCode(super.hashCode(), volumes, hostname, dns, memory, entrypoint, commands, cpuShares, env, portBindings, extraHosts); } @Override @@ -128,6 +128,7 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable .add("hostname", hostname) .add("memory", memory) .add("cpuShares", cpuShares) + .add("entrypoint", entrypoint) .add("commands", commands) .add("volumes", volumes) .add("env", env) @@ -160,13 +161,24 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable return this; } + public DockerTemplateOptions entrypoint(Iterable entrypoint) { + this.entrypoint = NullSafeCopies.copyWithNullOf(entrypoint); + return this; + } + + public DockerTemplateOptions entrypoint(String... entrypoint) { + this.entrypoint = NullSafeCopies.copyWithNullOf(entrypoint); + return this; + } + public DockerTemplateOptions commands(Iterable commands) { - this.commands = ImmutableList.copyOf(checkNotNull(commands, "commands")); + this.commands = NullSafeCopies.copyWithNullOf(commands); return this; } public DockerTemplateOptions commands(String...commands) { - return commands(ImmutableList.copyOf(checkNotNull(commands, "commands"))); + this.commands = NullSafeCopies.copyWithNullOf(commands); + return this; } public DockerTemplateOptions cpuShares(@Nullable Integer cpuShares) { @@ -175,12 +187,13 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable } public DockerTemplateOptions env(Iterable env) { - this.env = ImmutableList.copyOf(checkNotNull(env, "env")); + this.env = NullSafeCopies.copyWithNullOf(env); return this; } public DockerTemplateOptions env(String...env) { - return env(ImmutableList.copyOf(checkNotNull(env, "env"))); + this.env = NullSafeCopies.copyWithNullOf(env); + return this; } /** @@ -230,6 +243,8 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable public Integer getMemory() { return memory; } + public List getEntrypoint() { return entrypoint; } + public List getCommands() { return commands; } public Integer getCpuShares() { return cpuShares; } @@ -284,6 +299,22 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable return options.memory(memory); } + /** + * @see DockerTemplateOptions#entrypoint(String...) + */ + public static DockerTemplateOptions entrypoint(String...entrypoint) { + DockerTemplateOptions options = new DockerTemplateOptions(); + return options.entrypoint(entrypoint); + } + + /** + * @see DockerTemplateOptions#entrypoint(Iterable) + */ + public static DockerTemplateOptions entrypoint(Iterable entrypoint) { + DockerTemplateOptions options = new DockerTemplateOptions(); + return options.entrypoint(entrypoint); + } + /** * @see DockerTemplateOptions#commands(String...) */ diff --git a/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java b/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java index 62ea752372..b1041d012f 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java +++ b/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java @@ -97,19 +97,12 @@ public class DockerComputeServiceAdapter implements .image(imageId) .exposedPorts(exposedPorts); - if (!templateOptions.getCommands().isEmpty()) { - containerConfigBuilder.cmd(templateOptions.getCommands()); - } - + containerConfigBuilder.entrypoint(templateOptions.getEntrypoint()); + containerConfigBuilder.cmd(templateOptions.getCommands()); containerConfigBuilder.memory(templateOptions.getMemory()); - containerConfigBuilder.hostname(templateOptions.getHostname()); - containerConfigBuilder.cpuShares(templateOptions.getCpuShares()); - - if (!templateOptions.getEnv().isEmpty()) { - containerConfigBuilder.env(templateOptions.getEnv()); - } + containerConfigBuilder.env(templateOptions.getEnv()); if (!templateOptions.getVolumes().isEmpty()) { Map volumes = Maps.newLinkedHashMap(); diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/Config.java b/apis/docker/src/main/java/org/jclouds/docker/domain/Config.java index b16f4b56c9..2203a85ffb 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/Config.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/Config.java @@ -18,6 +18,7 @@ package org.jclouds.docker.domain; import static com.google.common.base.Preconditions.checkNotNull; import static org.jclouds.docker.internal.NullSafeCopies.copyOf; +import static org.jclouds.docker.internal.NullSafeCopies.copyWithNullOf; import java.util.List; import java.util.Map; @@ -55,11 +56,11 @@ public abstract class Config { public abstract boolean stdinOnce(); - public abstract List env(); + @Nullable public abstract List env(); - public abstract List cmd(); + @Nullable public abstract List cmd(); - public abstract List entrypoint(); + @Nullable public abstract List entrypoint(); public abstract String image(); @@ -119,10 +120,11 @@ public abstract class Config { boolean publishAllPorts, boolean privileged, List dns, String dnsSearch, String volumesFrom, List capAdd, List capDrop, Map restartPolicy) { return new AutoValue_Config(hostname, domainname, user, memory, memorySwap, cpuShares, attachStdin, - attachStdout, attachStderr, tty, openStdin, stdinOnce, copyOf(env), copyOf(cmd), copyOf(entrypoint), - image, copyOf(volumes), workingDir, networkDisabled, copyOf(exposedPorts), copyOf(securityOpts), hostConfig, + attachStdout, attachStderr, tty, openStdin, stdinOnce, copyWithNullOf(env), copyWithNullOf(cmd), + copyWithNullOf(entrypoint), image, copyOf(volumes), workingDir, networkDisabled, + copyOf(exposedPorts), copyOf(securityOpts), hostConfig, copyOf(binds), copyOf(links), copyOf(lxcConf), copyOf(portBindings), publishAllPorts, privileged, - copyOf(dns), dnsSearch, volumesFrom, copyOf(capAdd), copyOf(capDrop), copyOf(restartPolicy)); + copyWithNullOf(dns), dnsSearch, volumesFrom, copyOf(capAdd), copyOf(capDrop), copyOf(restartPolicy)); } public static Builder builder() { @@ -146,9 +148,9 @@ public abstract class Config { private boolean tty; private boolean openStdin; private boolean stdinOnce; - private List env = Lists.newArrayList(); - private List cmd = Lists.newArrayList(); - private List entrypoint = Lists.newArrayList(); + private List env; + private List cmd; + private List entrypoint; private String image; private Map volumes = Maps.newHashMap(); private String workingDir; @@ -281,7 +283,7 @@ public abstract class Config { } public Builder hostConfig(HostConfig hostConfig) { - this.hostConfig = checkNotNull(hostConfig, "hostConfig"); + this.hostConfig = hostConfig; return this; } @@ -356,7 +358,8 @@ public abstract class Config { return hostname(in.hostname()).domainname(in.domainname()).user(in.user()).memory(in.memory()) .memorySwap(in.memorySwap()).cpuShares(in.cpuShares()).attachStdin(in.attachStdin()) .attachStdout(in.attachStdout()).attachStderr(in.attachStderr()).tty(in.tty()) - .image(in.image()).volumes(in.volumes()).workingDir(in.workingDir()) + .openStdin(in.openStdin()).stdinOnce(in.stdinOnce()).env(in.env()).cmd(in.cmd()) + .entrypoint(in.entrypoint()).image(in.image()).volumes(in.volumes()).workingDir(in.workingDir()) .networkDisabled(in.networkDisabled()).exposedPorts(in.exposedPorts()).securityOpts(in.securityOpts()) .hostConfig(in.hostConfig()).binds(in.binds()).links(in.links()).lxcConf(in.lxcConf()) .portBindings(in.portBindings()).publishAllPorts(in.publishAllPorts()).privileged(in.privileged()) diff --git a/apis/docker/src/main/java/org/jclouds/docker/internal/NullSafeCopies.java b/apis/docker/src/main/java/org/jclouds/docker/internal/NullSafeCopies.java index c02d449525..ad86bbbf71 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/internal/NullSafeCopies.java +++ b/apis/docker/src/main/java/org/jclouds/docker/internal/NullSafeCopies.java @@ -27,11 +27,47 @@ import com.google.common.collect.ImmutableMap; public class NullSafeCopies { public static Map copyOf(@Nullable Map map) { - return map != null ? ImmutableMap.copyOf(map) : ImmutableMap.of(); + return map != null ? ImmutableMap.copyOf(map) : ImmutableMap. of(); } public static List copyOf(@Nullable List list) { - return list != null ? ImmutableList.copyOf(list) : ImmutableList.of(); + return list != null ? ImmutableList.copyOf(list) : ImmutableList. of(); + } + + /** + * Copies given List with keeping null value if provided. + * + * @param list + * instance to copy (maybe null) + * @return if the parameter is not-null then immutable copy; + * null otherwise + */ + public static List copyWithNullOf(@Nullable List list) { + return list != null ? ImmutableList.copyOf(list) : null; + } + + /** + * Copies given {@link Iterable} into immutable {@link List} with keeping null value if provided. + * + * @param iterable + * instance to copy (maybe null) + * @return if the parameter is not-null then immutable copy; + * null otherwise + */ + public static List copyWithNullOf(@Nullable Iterable iterable) { + return iterable != null ? ImmutableList.copyOf(iterable) : null; + } + + /** + * Copies given array into immutable {@link List} with keeping null value if provided. + * + * @param array + * instance to copy (maybe null) + * @return if the parameter is not-null then immutable copy; + * null otherwise + */ + public static List copyWithNullOf(@Nullable E[] array) { + return array != null ? ImmutableList.copyOf(array) : null; } private NullSafeCopies() { diff --git a/apis/docker/src/test/java/org/jclouds/docker/compute/options/DockerTemplateOptionsTest.java b/apis/docker/src/test/java/org/jclouds/docker/compute/options/DockerTemplateOptionsTest.java index d20019ab30..a1145f5a8d 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/compute/options/DockerTemplateOptionsTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/compute/options/DockerTemplateOptionsTest.java @@ -60,6 +60,12 @@ public class DockerTemplateOptionsTest { assertEquals(options.as(DockerTemplateOptions.class).getDns(), ImmutableList.of("8.8.8.8", "8.8.4.4")); } + @Test + public void testEntrypoint() { + TemplateOptions options = DockerTemplateOptions.Builder.entrypoint("/bin/sh", "-c"); + assertEquals(options.as(DockerTemplateOptions.class).getEntrypoint(), ImmutableList.of("/bin/sh", "-c")); + } + @Test public void testCommands() { TemplateOptions options = DockerTemplateOptions.Builder.commands("chmod 666 /etc/*", "rm -rf /var/run"); @@ -104,7 +110,8 @@ public class DockerTemplateOptionsTest { DockerTemplateOptions options = DockerTemplateOptions.Builder .memory(512) .cpuShares(4) - .commands("test") + .entrypoint("entry", "point") + .commands("test", "abc") .portBindings( ImmutableMap. builder() .put(8443, 443).build()) diff --git a/apis/docker/src/test/java/org/jclouds/docker/domain/ConfigTest.java b/apis/docker/src/test/java/org/jclouds/docker/domain/ConfigTest.java new file mode 100644 index 0000000000..0eef24d38c --- /dev/null +++ b/apis/docker/src/test/java/org/jclouds/docker/domain/ConfigTest.java @@ -0,0 +1,65 @@ +/* + * 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.assertj.core.api.Assertions.assertThat; + +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; + +@Test(groups = "unit", testName = "ConfigTest") +public class ConfigTest { + + @Test + public void testFromConfig() { + Config origConfig = Config.builder() + .hostname("6c9932f478bd") + .env(ImmutableList.of("PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin")) + .image("57e570db16baba1e8c0d6f3c15868ddb400f64ff76ec948e65c3ca3f15fb3587") + .domainname("") + .user("") + .cmd(ImmutableList.of("-name", "7a:63:a2:39:7b:0f")) + .entrypoint(ImmutableList.of("/home/weave/weaver", "-iface", "ethwe", "-wait", "5")) + .image("zettio/weave") + .workingDir("/home/weave") + .exposedPorts(ImmutableMap.of("6783/tcp", ImmutableMap.of(), "6783/udp", ImmutableMap.of())) + .build(); + Config newConfig = Config.builder().fromConfig(origConfig).build(); + assertThat(origConfig).isEqualTo(newConfig); + } + + + @Test + public void testNullValuesPropagation() { + Config config = Config.builder() + .image("zettio/weave") + .build(); + + assertThat(config.cmd()).isNull(); + assertThat(config.entrypoint()).isNull(); + assertThat(config.env()).isNull(); + assertThat(config.hostname()).isNull(); + assertThat(config.domainname()).isNull(); + assertThat(config.workingDir()).isNull(); + assertThat(config.hostConfig()).isNull(); + assertThat(config.dns()).isNull(); + assertThat(config.dnsSearch()).isNull(); + assertThat(config.volumesFrom()).isNull(); + } +} From 4e308868bdb2eebf1ac6e61b9550be76597a1042 Mon Sep 17 00:00:00 2001 From: Josef Cacek Date: Mon, 28 Sep 2015 11:08:59 +0200 Subject: [PATCH 44/81] [JCLOUDS-1006] Fix DockerUntrustedSSLContextSupplier to allow tests runs against endpoints without TLS configured --- .../suppliers/DockerUntrustedSSLContextSupplier.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/apis/docker/src/main/java/org/jclouds/docker/suppliers/DockerUntrustedSSLContextSupplier.java b/apis/docker/src/main/java/org/jclouds/docker/suppliers/DockerUntrustedSSLContextSupplier.java index d0c9077f51..42efa13da3 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/suppliers/DockerUntrustedSSLContextSupplier.java +++ b/apis/docker/src/main/java/org/jclouds/docker/suppliers/DockerUntrustedSSLContextSupplier.java @@ -24,10 +24,11 @@ import org.jclouds.location.Provider; import javax.inject.Inject; import javax.inject.Singleton; import javax.net.ssl.SSLContext; + +import java.io.File; import java.io.IOException; import java.security.GeneralSecurityException; -import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Throwables.propagate; @Singleton @@ -44,10 +45,13 @@ public class DockerUntrustedSSLContextSupplier implements Supplier { @Override public SSLContext get() { - Credentials currentCreds = checkNotNull(creds.get(), "credential supplier returned null"); + Credentials currentCreds = creds.get(); try { SSLContextBuilder builder = new SSLContextBuilder(); - builder.clientKeyAndCertificate(currentCreds.credential, currentCreds.identity); + // check if identity and credential are files, to set up sslContext + if (currentCreds!=null && new File(currentCreds.identity).isFile() && new File(currentCreds.credential).isFile()) { + builder.clientKeyAndCertificate(currentCreds.credential, currentCreds.identity); + } builder.trustManager(insecureTrustManager); return builder.build(); } catch (GeneralSecurityException e) { From 288d38761025856fdb604610cc4c0707e1ff8512 Mon Sep 17 00:00:00 2001 From: Josef Cacek Date: Mon, 28 Sep 2015 11:20:30 +0200 Subject: [PATCH 45/81] Format code of DockerUntrustedSSLContextSupplier to fit jclouds code conventions and checkstyle settings --- .../DockerUntrustedSSLContextSupplier.java | 51 ++++++++++--------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/apis/docker/src/main/java/org/jclouds/docker/suppliers/DockerUntrustedSSLContextSupplier.java b/apis/docker/src/main/java/org/jclouds/docker/suppliers/DockerUntrustedSSLContextSupplier.java index 42efa13da3..880a48a839 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/suppliers/DockerUntrustedSSLContextSupplier.java +++ b/apis/docker/src/main/java/org/jclouds/docker/suppliers/DockerUntrustedSSLContextSupplier.java @@ -33,32 +33,33 @@ import static com.google.common.base.Throwables.propagate; @Singleton public class DockerUntrustedSSLContextSupplier implements Supplier { - private final Supplier creds; - private final SSLModule.TrustAllCerts insecureTrustManager; + private final Supplier creds; + private final SSLModule.TrustAllCerts insecureTrustManager; + @Inject + DockerUntrustedSSLContextSupplier(@Provider Supplier creds, + SSLModule.TrustAllCerts insecureTrustManager) { + this.creds = creds; + this.insecureTrustManager = insecureTrustManager; + } - @Inject - DockerUntrustedSSLContextSupplier(@Provider Supplier creds, SSLModule.TrustAllCerts insecureTrustManager) { - this.creds = creds; - this.insecureTrustManager = insecureTrustManager; - } - - @Override - public SSLContext get() { - Credentials currentCreds = creds.get(); - try { - SSLContextBuilder builder = new SSLContextBuilder(); - // check if identity and credential are files, to set up sslContext - if (currentCreds!=null && new File(currentCreds.identity).isFile() && new File(currentCreds.credential).isFile()) { - builder.clientKeyAndCertificate(currentCreds.credential, currentCreds.identity); - } - builder.trustManager(insecureTrustManager); - return builder.build(); - } catch (GeneralSecurityException e) { - throw propagate(e); - } catch (IOException e) { - throw propagate(e); - } - } + @Override + public SSLContext get() { + Credentials currentCreds = creds.get(); + try { + SSLContextBuilder builder = new SSLContextBuilder(); + // check if identity and credential are files, to set up sslContext + if (currentCreds != null && new File(currentCreds.identity).isFile() + && new File(currentCreds.credential).isFile()) { + builder.clientKeyAndCertificate(currentCreds.credential, currentCreds.identity); + } + builder.trustManager(insecureTrustManager); + return builder.build(); + } catch (GeneralSecurityException e) { + throw propagate(e); + } catch (IOException e) { + throw propagate(e); + } + } } From d7b3f5d98ff786b79583ee08f670dba0cc308344 Mon Sep 17 00:00:00 2001 From: Josef Cacek Date: Fri, 2 Oct 2015 15:00:55 +0200 Subject: [PATCH 46/81] [JCLOUDS-1011] force closing HTTP client connection after using Docker build REST API method --- .../org/jclouds/docker/features/MiscApi.java | 4 ++-- .../docker/features/MiscApiMockTest.java | 22 +++++++++++++++++-- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/apis/docker/src/main/java/org/jclouds/docker/features/MiscApi.java b/apis/docker/src/main/java/org/jclouds/docker/features/MiscApi.java index 6c49436632..8e5548ede7 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/features/MiscApi.java +++ b/apis/docker/src/main/java/org/jclouds/docker/features/MiscApi.java @@ -65,7 +65,7 @@ public interface MiscApi { @Named("image:build") @POST @Path("/build") - @Headers(keys = "Content-Type", values = "application/tar") + @Headers(keys = { "Content-Type", "Connection" }, values = { "application/tar", "close" }) InputStream build(Payload inputStream); /** @@ -79,7 +79,7 @@ public interface MiscApi { @Named("image:build") @POST @Path("/build") - @Headers(keys = "Content-Type", values = "application/tar") + @Headers(keys = { "Content-Type", "Connection" }, values = { "application/tar", "close" }) InputStream build(Payload inputStream, BuildOptions options); } diff --git a/apis/docker/src/test/java/org/jclouds/docker/features/MiscApiMockTest.java b/apis/docker/src/test/java/org/jclouds/docker/features/MiscApiMockTest.java index 630d7af3b1..4fe87caac6 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/features/MiscApiMockTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/features/MiscApiMockTest.java @@ -18,6 +18,8 @@ package org.jclouds.docker.features; import com.squareup.okhttp.mockwebserver.MockResponse; import com.squareup.okhttp.mockwebserver.MockWebServer; +import com.squareup.okhttp.mockwebserver.RecordedRequest; + import org.jclouds.docker.DockerApi; import org.jclouds.docker.internal.BaseDockerMockTest; import org.jclouds.docker.parse.InfoParseTest; @@ -26,11 +28,14 @@ import org.jclouds.io.Payload; import org.jclouds.io.Payloads; import org.testng.annotations.Test; +import static org.assertj.core.api.Assertions.assertThat; import static org.jclouds.docker.compute.BaseDockerApiLiveTest.tarredDockerfile; import static org.testng.Assert.assertEquals; import java.io.File; import java.io.FileInputStream; +import javax.ws.rs.core.HttpHeaders; + /** * Mock tests for the {@link org.jclouds.docker.features.MiscApi} class. */ @@ -64,7 +69,8 @@ public class MiscApiMockTest extends BaseDockerMockTest { MiscApi api = api(DockerApi.class, server.getUrl("/").toString()).getMiscApi(); try { api.build(tarredDockerfile()); - assertSent(server, "POST", "/build"); + RecordedRequest request = assertSent(server, "POST", "/build"); + assertDockerBuildHttpHeaders(request); } finally { server.shutdown(); } @@ -79,10 +85,22 @@ public class MiscApiMockTest extends BaseDockerMockTest { payload.getContentMetadata().setContentLength(file.length()); try { api.build(payload); - assertSent(server, "POST", "/build"); + RecordedRequest request = assertSent(server, "POST", "/build"); + assertDockerBuildHttpHeaders(request); } finally { server.shutdown(); } } + /** + * Asserts that correct values of HTTP headers are used in Docker build REST + * API calls. + * + * @param request + */ + private void assertDockerBuildHttpHeaders(RecordedRequest request) { + assertThat(request.getHeader("Connection")).isEqualTo("close"); + assertThat(request.getHeader(HttpHeaders.CONTENT_TYPE)).isEqualTo("application/tar"); + } + } From f6ad2cc3803332595bf6af8a08f669c2e66a0e19 Mon Sep 17 00:00:00 2001 From: Josef Cacek Date: Mon, 28 Sep 2015 23:08:00 +0200 Subject: [PATCH 47/81] [JCLOUDS-1007] Implemented Docker Exec support in MiscApi --- .../java/org/jclouds/docker/domain/Exec.java | 35 ++++++ .../docker/domain/ExecCreateParams.java | 72 ++++++++++++ .../jclouds/docker/domain/ExecInspect.java | 40 +++++++ .../docker/domain/ExecStartParams.java | 47 ++++++++ .../org/jclouds/docker/features/MiscApi.java | 55 +++++++++ .../docker/util/DockerInputStream.java | 73 ++++++++++++ .../jclouds/docker/util/StdStreamData.java | 87 ++++++++++++++ .../docker/features/MiscApiLiveTest.java | 111 +++++++++++++++++- .../docker/features/MiscApiMockTest.java | 93 +++++++++++++-- apis/docker/src/test/resources/exec.json | 1 + apis/docker/src/test/resources/exec.start | Bin 0 -> 29 bytes .../src/test/resources/execInspect.json | 1 + 12 files changed, 604 insertions(+), 11 deletions(-) create mode 100644 apis/docker/src/main/java/org/jclouds/docker/domain/Exec.java create mode 100644 apis/docker/src/main/java/org/jclouds/docker/domain/ExecCreateParams.java create mode 100644 apis/docker/src/main/java/org/jclouds/docker/domain/ExecInspect.java create mode 100644 apis/docker/src/main/java/org/jclouds/docker/domain/ExecStartParams.java create mode 100644 apis/docker/src/main/java/org/jclouds/docker/util/DockerInputStream.java create mode 100644 apis/docker/src/main/java/org/jclouds/docker/util/StdStreamData.java create mode 100644 apis/docker/src/test/resources/exec.json create mode 100644 apis/docker/src/test/resources/exec.start create mode 100644 apis/docker/src/test/resources/execInspect.json diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/Exec.java b/apis/docker/src/main/java/org/jclouds/docker/domain/Exec.java new file mode 100644 index 0000000000..7511f63547 --- /dev/null +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/Exec.java @@ -0,0 +1,35 @@ +/* + * 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; + +/** + * Represents a response from Exec Create call (POST /containers/(id)/exec). + */ +@AutoValue +public abstract class Exec { + + public abstract String id(); + + @SerializedNames({ "Id"}) + public static Exec create(String id) { + return new AutoValue_Exec(id); + } +} diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/ExecCreateParams.java b/apis/docker/src/main/java/org/jclouds/docker/domain/ExecCreateParams.java new file mode 100644 index 0000000000..3f6009bc5f --- /dev/null +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/ExecCreateParams.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.domain; + +import static org.jclouds.docker.internal.NullSafeCopies.copyOf; + +import java.util.List; + +import org.jclouds.json.SerializedNames; + +import com.google.auto.value.AutoValue; + +/** + * Json Parameters (some of them) of Exec Create call. + */ +@AutoValue +public abstract class ExecCreateParams { + + public abstract boolean attachStdout(); + + public abstract boolean attachStderr(); + + public abstract List cmd(); + + @SerializedNames({ "AttachStdout", "AttachStderr", "Cmd" }) + private static ExecCreateParams create(boolean attachStdout, boolean attachStderr, List cmd) { + return builder().attachStdout(attachStdout).attachStderr(attachStderr).cmd(cmd).build(); + } + + /** + * Creates builder for {@link ExecCreateParams}, it sets + * {@link #attachStderr()} and {@link #attachStdout()} to true as a default. + * + * @return new {@link ExecCreateParams.Builder} instance + */ + public static Builder builder() { + return new AutoValue_ExecCreateParams.Builder().attachStderr(true).attachStdout(true); + } + + @AutoValue.Builder + public abstract static class Builder { + + public abstract Builder attachStdout(boolean b); + + public abstract Builder attachStderr(boolean b); + + public abstract Builder cmd(List cmd); + + abstract List cmd(); + + abstract ExecCreateParams autoBuild(); + + public ExecCreateParams build() { + cmd(copyOf(cmd())); + return autoBuild(); + } + } +} diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/ExecInspect.java b/apis/docker/src/main/java/org/jclouds/docker/domain/ExecInspect.java new file mode 100644 index 0000000000..d15bccfc13 --- /dev/null +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/ExecInspect.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.domain; + +import org.jclouds.json.SerializedNames; + +import com.google.auto.value.AutoValue; + +/** + * Represents a response (part of it) from Exec Inspect call ( + * GET /exec/(id)/json). + */ +@AutoValue +public abstract class ExecInspect { + + public abstract String id(); + + public abstract boolean running(); + + public abstract int exitCode(); + + @SerializedNames({ "ID", "Running", "ExitCode"}) + public static ExecInspect create(String id, boolean running, int exitCode) { + return new AutoValue_ExecInspect(id, running, exitCode); + } +} diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/ExecStartParams.java b/apis/docker/src/main/java/org/jclouds/docker/domain/ExecStartParams.java new file mode 100644 index 0000000000..f8046f8abf --- /dev/null +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/ExecStartParams.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.domain; + +import org.jclouds.json.SerializedNames; + +import com.google.auto.value.AutoValue; + +/** + * Json Parameter(s) (some of them) of Exec Start call. + */ +@AutoValue +public abstract class ExecStartParams { + + public abstract boolean detach(); + + @SerializedNames({ "Detach" }) + public static ExecStartParams create(boolean detach) { + return builder().detach(detach).build(); + } + + public static Builder builder() { + return new AutoValue_ExecStartParams.Builder().detach(false); + } + + @AutoValue.Builder + public abstract static class Builder { + + public abstract Builder detach(boolean b); + + public abstract ExecStartParams build(); + } +} diff --git a/apis/docker/src/main/java/org/jclouds/docker/features/MiscApi.java b/apis/docker/src/main/java/org/jclouds/docker/features/MiscApi.java index 8e5548ede7..5df5d04b95 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/features/MiscApi.java +++ b/apis/docker/src/main/java/org/jclouds/docker/features/MiscApi.java @@ -23,13 +23,21 @@ import javax.ws.rs.Consumes; 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.docker.domain.Exec; +import org.jclouds.docker.domain.ExecCreateParams; +import org.jclouds.docker.domain.ExecInspect; +import org.jclouds.docker.domain.ExecStartParams; import org.jclouds.docker.domain.Info; import org.jclouds.docker.domain.Version; import org.jclouds.docker.options.BuildOptions; +import org.jclouds.docker.util.DockerInputStream; import org.jclouds.io.Payload; +import org.jclouds.rest.annotations.BinderParam; import org.jclouds.rest.annotations.Headers; +import org.jclouds.rest.binders.BindToJsonPayload; @Consumes(MediaType.APPLICATION_JSON) @Path("/v{jclouds.api-version}") @@ -82,4 +90,51 @@ public interface MiscApi { @Headers(keys = { "Content-Type", "Connection" }, values = { "application/tar", "close" }) InputStream build(Payload inputStream, BuildOptions options); + /** + * Sets up an exec instance in a running container with given Id. + * + * @param containerId + * container Id + * @param execCreateParams + * exec parameters + * @return an instance which holds exec identifier + */ + @Named("container:exec") + @POST + @Path("/containers/{id}/exec") + Exec execCreate(@PathParam("id") String containerId, + @BinderParam(BindToJsonPayload.class) ExecCreateParams execCreateParams); + + /** + * Starts a previously set up exec instance id. If + * {@link ExecStartParams#detach()} is true, this API returns after starting + * the exec command. Otherwise, this API sets up an interactive session with + * the exec command. + * + * @param execId + * exec instance id + * @param execStartParams + * start parameters + * @return raw docker stream which can be wrapped to + * {@link DockerInputStream} + * @see #execCreate(String, ExecCreateParams) + * @see DockerInputStream + */ + @Named("exec:start") + @POST + @Path("/exec/{id}/start") + InputStream execStart(@PathParam("id") String execId, + @BinderParam(BindToJsonPayload.class) ExecStartParams execStartParams); + + /** + * Returns low-level information about the exec command id. + * + * @param execId + * exec instance id + * @return details about exec instance + */ + @Named("exec:inspect") + @GET + @Path("/exec/{id}/json") + ExecInspect execInspect(@PathParam("id") String execId); } diff --git a/apis/docker/src/main/java/org/jclouds/docker/util/DockerInputStream.java b/apis/docker/src/main/java/org/jclouds/docker/util/DockerInputStream.java new file mode 100644 index 0000000000..dacd7b7681 --- /dev/null +++ b/apis/docker/src/main/java/org/jclouds/docker/util/DockerInputStream.java @@ -0,0 +1,73 @@ +/* + * 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.util; + +import java.io.DataInputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * Extension to {@link DataInputStream} which adds method + * {@link #readStdStreamData()} to allow read multiplexed standard streams. + */ +public final class DockerInputStream extends DataInputStream { + + /** + * Ctor from superclass. + * + * @param in + * @see DataInputStream#DataInputStream(InputStream) + */ + public DockerInputStream(InputStream in) { + super(in); + } + + /** + * @return {@link StdStreamData} instance read from the input stream or + * null if we reached end of the stream. + * @throws IOException + */ + public StdStreamData readStdStreamData() throws IOException { + byte[] header = new byte[8]; + // try to read first byte from the message header - just to check if we + // are at the end + // of stream + if (-1 == read(header, 0, 1)) { + return null; + } + // read the rest of the header + readFully(header, 1, 7); + // decode size as an unsigned int + long size = (long) (header[4] & 0xFF) << 24 | (header[5] & 0xFF) << 16 | (header[6] & 0xFF) << 8 + | (header[7] & 0xFF); + + byte[] payload; + // The size from the header is an unsigned int so it can happen the byte + // array has not a sufficient size and we'll have to truncate the frame + payload = new byte[(int) Math.min(Integer.MAX_VALUE, size)]; + readFully(payload); + boolean truncated = false; + if (size > Integer.MAX_VALUE) { + truncated = true; + // skip the rest + readFully(new byte[(int) (size - Integer.MAX_VALUE)]); + } + return new StdStreamData(header[0], payload, truncated); + } + +} diff --git a/apis/docker/src/main/java/org/jclouds/docker/util/StdStreamData.java b/apis/docker/src/main/java/org/jclouds/docker/util/StdStreamData.java new file mode 100644 index 0000000000..f8f5f9a150 --- /dev/null +++ b/apis/docker/src/main/java/org/jclouds/docker/util/StdStreamData.java @@ -0,0 +1,87 @@ +/* + * 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.util; + +import java.util.Arrays; + +/** + * Representation of single message from docker-raw-stream. It holds stream + * type, data (payload) and flag which says if the payload was truncated. The + * truncation can occur when the frame size is greater than + * {@link Integer#MAX_VALUE}. + */ +public final class StdStreamData { + + private final StdStreamType type; + private final byte[] payload; + private final boolean truncated; + + /** + * Ctor. + * + * @param streamTypeId + * standard stream type (0=stdIn, 1=stdOut, 2=stdErr) + * @param payload + * message data - must not be null + * @param truncated + * @throws ArrayIndexOutOfBoundsException + * if streamTypeId is not an index in {@link StdStreamType} enum. + * @throws NullPointerException + * if provided payload is null + */ + StdStreamData(byte streamTypeId, byte[] payload, boolean truncated) + throws ArrayIndexOutOfBoundsException, NullPointerException { + this.type = StdStreamType.values()[streamTypeId]; + this.payload = Arrays.copyOf(payload, payload.length); + this.truncated = truncated; + } + + /** + * Type of stream. + * + * @return + */ + public StdStreamType getType() { + return type; + } + + /** + * Data from this message. + * + * @return payload. + */ + public byte[] getPayload() { + return payload; + } + + /** + * Flag which says if the payload was truncated (because of size). + * + * @return true if truncated + */ + public boolean isTruncated() { + return truncated; + } + + /** + * Standard streams enum. The order of entries is important! + */ + public static enum StdStreamType { + IN, OUT, ERR; + } +} diff --git a/apis/docker/src/test/java/org/jclouds/docker/features/MiscApiLiveTest.java b/apis/docker/src/test/java/org/jclouds/docker/features/MiscApiLiveTest.java index ce2bc9e1c9..8cdd76bf5e 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/features/MiscApiLiveTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/features/MiscApiLiveTest.java @@ -16,23 +16,79 @@ */ package org.jclouds.docker.features; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; + +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.URISyntaxException; +import org.assertj.core.api.Fail; import org.jclouds.docker.compute.BaseDockerApiLiveTest; +import org.jclouds.docker.domain.Config; +import org.jclouds.docker.domain.Container; +import org.jclouds.docker.domain.Exec; +import org.jclouds.docker.domain.ExecCreateParams; +import org.jclouds.docker.domain.ExecInspect; +import org.jclouds.docker.domain.ExecStartParams; +import org.jclouds.docker.domain.Image; import org.jclouds.docker.options.BuildOptions; +import org.jclouds.docker.options.CreateImageOptions; +import org.jclouds.docker.options.RemoveContainerOptions; +import org.jclouds.docker.util.DockerInputStream; +import org.jclouds.docker.util.StdStreamData; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import com.google.common.base.Splitter; +import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; @Test(groups = "live", testName = "MiscApiLiveTest", singleThreaded = true) public class MiscApiLiveTest extends BaseDockerApiLiveTest { + protected static final String BUSYBOX_IMAGE_TAG = "busybox:ubuntu-12.04"; + private static String imageId; + private Container container = null; + private Image image = null; + private Exec exec = 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); + Config containerConfig = Config.builder().image(image.id()) + .cmd(ImmutableList.of("/bin/sh", "-c", "touch hello; while true; do echo hello world; sleep 1; done")) + .build(); + container = api.getContainerApi().createContainer("miscApiTest", containerConfig); + assertNotNull(container); + api.getContainerApi().startContainer(container.id()); + assertTrue(api.getContainerApi().inspectContainer(container.id()).state().running()); + } + + @AfterClass + protected void tearDown() { + if (container != null) { + if (api.getContainerApi().inspectContainer(container.id()) != null) { + api.getContainerApi().removeContainer(container.id(), RemoveContainerOptions.Builder.force(true)); + } + } + if (image != null) { + api.getImageApi().deleteImage(BUSYBOX_IMAGE_TAG); + } + } + @Test public void testVersion() { assertNotNull(api().getVersion().apiVersion()); @@ -61,9 +117,62 @@ public class MiscApiLiveTest extends BaseDockerApiLiveTest { assertNotNull(imageId); } + @Test + public void testExecCreate() { + exec = api().execCreate(container.id(), + ExecCreateParams.builder() + .cmd(ImmutableList. of("/bin/sh", "-c", + "echo -n Standard >&1 && echo -n Error >&2 && exit 2")) + .attachStderr(true).attachStdout(true).build()); + assertNotNull(exec); + assertNotNull(exec.id()); + } + + @Test(dependsOnMethods = "testExecCreate") + public void testExecStart() throws IOException { + final ExecStartParams startParams = ExecStartParams.builder().detach(false).build(); + DockerInputStream inputStream = null; + try { + inputStream = new DockerInputStream(api().execStart(exec.id(), startParams)); + assertNotNull(inputStream); + ByteArrayOutputStream baosOut = new ByteArrayOutputStream(); + ByteArrayOutputStream baosErr = new ByteArrayOutputStream(); + StdStreamData data = null; + while (null != (data = inputStream.readStdStreamData())) { + assertFalse(data.isTruncated()); + switch (data.getType()) { + case OUT: + baosOut.write(data.getPayload()); + break; + case ERR: + baosErr.write(data.getPayload()); + break; + default: + Fail.fail("Unexpected Stream type"); + break; + } + } + assertEquals(baosOut.toString(), "Standard"); + assertEquals(baosErr.toString(), "Error"); + } finally { + if (inputStream != null) { + inputStream.close(); + } + } + } + + @Test(dependsOnMethods = "testExecStart") + public void testExecInspect() throws IOException { + ExecInspect execInspect = api().execInspect(exec.id()); + assertNotNull(execInspect); + assertEquals(execInspect.id(), exec.id()); + assertEquals(execInspect.running(), false); + assertEquals(execInspect.exitCode(), 2); + } + + private MiscApi api() { return api.getMiscApi(); } - } diff --git a/apis/docker/src/test/java/org/jclouds/docker/features/MiscApiMockTest.java b/apis/docker/src/test/java/org/jclouds/docker/features/MiscApiMockTest.java index 4fe87caac6..dd5d965ac5 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/features/MiscApiMockTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/features/MiscApiMockTest.java @@ -16,25 +16,40 @@ */ package org.jclouds.docker.features; -import com.squareup.okhttp.mockwebserver.MockResponse; -import com.squareup.okhttp.mockwebserver.MockWebServer; -import com.squareup.okhttp.mockwebserver.RecordedRequest; +import static org.assertj.core.api.Assertions.assertThat; +import static org.jclouds.docker.compute.BaseDockerApiLiveTest.tarredDockerfile; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertNull; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +import javax.ws.rs.core.HttpHeaders; import org.jclouds.docker.DockerApi; +import org.jclouds.docker.config.DockerParserModule; +import org.jclouds.docker.domain.Exec; +import org.jclouds.docker.domain.ExecCreateParams; +import org.jclouds.docker.domain.ExecInspect; +import org.jclouds.docker.domain.ExecStartParams; import org.jclouds.docker.internal.BaseDockerMockTest; import org.jclouds.docker.parse.InfoParseTest; import org.jclouds.docker.parse.VersionParseTest; +import org.jclouds.docker.util.DockerInputStream; +import org.jclouds.docker.util.StdStreamData; +import org.jclouds.docker.util.StdStreamData.StdStreamType; import org.jclouds.io.Payload; import org.jclouds.io.Payloads; import org.testng.annotations.Test; -import static org.assertj.core.api.Assertions.assertThat; -import static org.jclouds.docker.compute.BaseDockerApiLiveTest.tarredDockerfile; -import static org.testng.Assert.assertEquals; -import java.io.File; -import java.io.FileInputStream; - -import javax.ws.rs.core.HttpHeaders; +import com.google.common.collect.ImmutableList; +import com.squareup.okhttp.mockwebserver.MockResponse; +import com.squareup.okhttp.mockwebserver.MockWebServer; +import com.squareup.okhttp.mockwebserver.RecordedRequest; /** * Mock tests for the {@link org.jclouds.docker.features.MiscApi} class. @@ -92,6 +107,64 @@ public class MiscApiMockTest extends BaseDockerMockTest { } } + + public void testExecCreate() throws Exception { + MockWebServer server = mockWebServer(new MockResponse().setBody(payloadFromResource("/exec.json"))); + MiscApi api = api(DockerApi.class, server.getUrl("/").toString(), new DockerParserModule()).getMiscApi(); + try { + final String containerId = "a40d212a0a379de00426a1da2a8fd3fd20d5f74fd7c2dd42f6c93a6b1b0e6974"; + final ExecCreateParams execParams = ExecCreateParams.builder() + .cmd(ImmutableList. of("/bin/sh", "-c", "echo -n Standard >&1 && echo -n Error >&2")) + .attachStderr(true).attachStdout(true).build(); + final Exec expectedExec = Exec.create("dbf45d296388032ebb9872edb75847f6655a72b4e9ab0d99ae1c75589c4ca957"); + assertEquals(api.execCreate(containerId, execParams), expectedExec); + assertSent(server, "POST", "/containers/" + containerId + "/exec"); + } finally { + server.shutdown(); + } + } + + public void testExecStart() throws Exception { + MockWebServer server = mockWebServer(new MockResponse().setBody(payloadFromResource("/exec.start"))); + MiscApi api = api(DockerApi.class, server.getUrl("/").toString(), new DockerParserModule()).getMiscApi(); + DockerInputStream dis = null; + try { + final String execId = "dbf45d296388032ebb9872edb75847f6655a72b4e9ab0d99ae1c75589c4ca957"; + final ExecStartParams startParams = ExecStartParams.builder().detach(false).build(); + dis = new DockerInputStream(api.execStart(execId, startParams)); + + final StdStreamData msg1 = dis.readStdStreamData(); + assertFalse(msg1.isTruncated()); + assertEquals(msg1.getPayload(), "Standard".getBytes(StandardCharsets.UTF_8)); + assertEquals(msg1.getType(), StdStreamType.OUT); + + final StdStreamData msg2 = dis.readStdStreamData(); + assertFalse(msg2.isTruncated()); + assertEquals(msg2.getPayload(), "Error".getBytes(StandardCharsets.UTF_8)); + assertEquals(msg2.getType(), StdStreamType.ERR); + + assertNull(dis.readStdStreamData()); + assertSent(server, "POST", "/exec/" + execId + "/start"); + } finally { + if (dis != null) { + dis.close(); + } + server.shutdown(); + } + } + + public void testExecInspect() throws IOException, InterruptedException { + MockWebServer server = mockWebServer(new MockResponse().setBody(payloadFromResource("/execInspect.json"))); + MiscApi api = api(DockerApi.class, server.getUrl("/").toString(), new DockerParserModule()).getMiscApi(); + final String expectedExecId = "fda1cf8064863fc0667c691c69793fdb7d0bd4a1fabb8250536abe5203e4208a"; + ExecInspect execInspect = api.execInspect(expectedExecId); + assertNotNull(execInspect); + assertEquals(execInspect.id(), expectedExecId); + assertEquals(execInspect.running(), false); + assertEquals(execInspect.exitCode(), 2); + assertSent(server, "GET", "/exec/" + expectedExecId + "/json"); + } + /** * Asserts that correct values of HTTP headers are used in Docker build REST * API calls. diff --git a/apis/docker/src/test/resources/exec.json b/apis/docker/src/test/resources/exec.json new file mode 100644 index 0000000000..d5f626505c --- /dev/null +++ b/apis/docker/src/test/resources/exec.json @@ -0,0 +1 @@ +{"Id":"dbf45d296388032ebb9872edb75847f6655a72b4e9ab0d99ae1c75589c4ca957"} diff --git a/apis/docker/src/test/resources/exec.start b/apis/docker/src/test/resources/exec.start new file mode 100644 index 0000000000000000000000000000000000000000..0d726243cc6d5307c5103094049a4f392e2ca033 GIT binary patch literal 29 ccmZQ%00WNTlEl1}#G(`?2%pups3^Y(06vigOaK4? literal 0 HcmV?d00001 diff --git a/apis/docker/src/test/resources/execInspect.json b/apis/docker/src/test/resources/execInspect.json new file mode 100644 index 0000000000..a47cf49d09 --- /dev/null +++ b/apis/docker/src/test/resources/execInspect.json @@ -0,0 +1 @@ +{"ID":"fda1cf8064863fc0667c691c69793fdb7d0bd4a1fabb8250536abe5203e4208a","Running":false,"ExitCode":2,"ProcessConfig":{"privileged":false,"user":"","tty":false,"entrypoint":"/bin/sh","arguments":["-c","echo -n Standard \u003e\u00261 \u0026\u0026 echo -n Error \u003e\u00262 \u0026\u0026 exit 2"]},"OpenStdin":false,"OpenStderr":true,"OpenStdout":true,"Container":{"State":{"Running":true,"Paused":false,"Restarting":false,"OOMKilled":false,"Dead":false,"Pid":1561,"ExitCode":0,"Error":"","StartedAt":"2015-09-29T12:36:21.011469908Z","FinishedAt":"0001-01-01T00:00:00Z"},"ID":"07695c5170a1cbf3402552202fd28d2beb81fce5958a244e9ad7c8cb0ded936e","Created":"2015-09-29T12:36:19.829846377Z","Path":"/bin/sh","Args":["-c","touch hello; while true; do echo hello world; sleep 1; done"],"Config":{"Hostname":"07695c5170a1","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"PortSpecs":null,"ExposedPorts":null,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":null,"Cmd":["/bin/sh","-c","touch hello; while true; do echo hello world; sleep 1; done"],"Image":"ff8f955d1fed83a6239675b9a767fc9502db9934922a2f69ac6bf4cad8a7d7be","Volumes":null,"VolumeDriver":"","WorkingDir":"","Entrypoint":null,"NetworkDisabled":false,"MacAddress":"","OnBuild":null,"Labels":{},"Init":""},"Image":"ff8f955d1fed83a6239675b9a767fc9502db9934922a2f69ac6bf4cad8a7d7be","NetworkSettings":{"Bridge":"","EndpointID":"78d38a6f4d63966b16ca824a533b70f0a3a00490586688a188f67ec0bb07e68f","Gateway":"172.17.42.1","GlobalIPv6Address":"","GlobalIPv6PrefixLen":0,"HairpinMode":false,"IPAddress":"172.17.0.3","IPPrefixLen":16,"IPv6Gateway":"","LinkLocalIPv6Address":"","LinkLocalIPv6PrefixLen":0,"MacAddress":"02:42:ac:11:00:03","NetworkID":"880f733909fa4ad45c8fbc95b80c83e22a7b46f8cd7fb23dea62b0996ef1c8c6","PortMapping":null,"Ports":{},"SandboxKey":"/var/run/docker/netns/07695c5170a1","SecondaryIPAddresses":null,"SecondaryIPv6Addresses":null},"ResolvConfPath":"/var/lib/docker/containers/07695c5170a1cbf3402552202fd28d2beb81fce5958a244e9ad7c8cb0ded936e/resolv.conf","HostnamePath":"/var/lib/docker/containers/07695c5170a1cbf3402552202fd28d2beb81fce5958a244e9ad7c8cb0ded936e/hostname","HostsPath":"/var/lib/docker/containers/07695c5170a1cbf3402552202fd28d2beb81fce5958a244e9ad7c8cb0ded936e/hosts","LogPath":"/var/lib/docker/containers/07695c5170a1cbf3402552202fd28d2beb81fce5958a244e9ad7c8cb0ded936e/07695c5170a1cbf3402552202fd28d2beb81fce5958a244e9ad7c8cb0ded936e-json.log","Name":"/miscApiTest","Driver":"devicemapper","ExecDriver":"native-0.2","MountLabel":"","ProcessLabel":"","RestartCount":0,"UpdateDns":false,"MountPoints":{},"Volumes":{},"VolumesRW":{},"Init":"","AppArmorProfile":""}} From 8ca3a326c85b817d31749c7933366a0dfe1e53a6 Mon Sep 17 00:00:00 2001 From: googlielmo Date: Mon, 5 Oct 2015 10:10:20 +0200 Subject: [PATCH 48/81] Fix dnsSearch type - dnsSearch declared as String, corrected to List --- .../java/org/jclouds/docker/domain/HostConfig.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/HostConfig.java b/apis/docker/src/main/java/org/jclouds/docker/domain/HostConfig.java index 17a4f380f8..1e655afb23 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/HostConfig.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/HostConfig.java @@ -41,7 +41,7 @@ public abstract class HostConfig { public abstract List dns(); - @Nullable public abstract String dnsSearch(); + @Nullable public abstract List dnsSearch(); public abstract Map>> portBindings(); @@ -62,9 +62,9 @@ public abstract class HostConfig { @SerializedNames({ "ContainerIDFile", "Binds", "LxcConf", "Privileged", "Dns", "DnsSearch", "PortBindings", "Links", "ExtraHosts", "PublishAllPorts", "VolumesFrom", "NetworkMode" }) public static HostConfig create(String containerIDFile, List binds, List> lxcConf, - boolean privileged, List dns, String dnsSearch, Map>> portBindings, + boolean privileged, List dns, List dnsSearch, Map>> portBindings, List links, List extraHosts, boolean publishAllPorts, List volumesFrom, String networkMode) { - return new AutoValue_HostConfig(containerIDFile, copyOf(binds), copyOf(lxcConf), privileged, copyOf(dns), dnsSearch, + return new AutoValue_HostConfig(containerIDFile, copyOf(binds), copyOf(lxcConf), privileged, copyOf(dns), copyOf(dnsSearch), copyOf(portBindings), copyOf(links), copyOf(extraHosts), publishAllPorts, copyOf(volumesFrom), networkMode); } @@ -83,7 +83,7 @@ public abstract class HostConfig { private List> lxcConf = Lists.newArrayList(); private boolean privileged; private List dns = Lists.newArrayList(); - private String dnsSearch; + private List dnsSearch = Lists.newArrayList(); private Map>> portBindings = Maps.newLinkedHashMap(); private List links = Lists.newArrayList(); private List extraHosts = Lists.newArrayList(); @@ -116,8 +116,8 @@ public abstract class HostConfig { return this; } - public Builder dnsSearch(String dnsSearch) { - this.dnsSearch = dnsSearch; + public Builder dnsSearch(List dnsSearch) { + this.dnsSearch.addAll(checkNotNull(dnsSearch, "dnsSearch")); return this; } From 01d43f503b96c5ab225410b4e4e5d3711d18ef4f Mon Sep 17 00:00:00 2001 From: Ignasi Barrera Date: Sat, 10 Oct 2015 00:10:50 +0200 Subject: [PATCH 49/81] JCLOUDS-1014: Make the login port lookup function configurable --- apis/docker/pom.xml | 4 + .../DockerComputeServiceContextModule.java | 9 +- .../compute/config/LoginPortLookupModule.java | 40 +++++++ .../functions/ContainerToNodeMetadata.java | 44 +++----- .../functions/CustomLoginPortFromImage.java | 84 ++++++++++++++ .../functions/LoginPortForContainer.java | 51 +++++++++ .../functions/PublicPortForContainerPort.java | 58 ++++++++++ .../ContainerToNodeMetadataTest.java | 8 +- .../CustomLoginPortFromImageTest.java | 106 ++++++++++++++++++ 9 files changed, 364 insertions(+), 40 deletions(-) create mode 100644 apis/docker/src/main/java/org/jclouds/docker/compute/config/LoginPortLookupModule.java create mode 100644 apis/docker/src/main/java/org/jclouds/docker/compute/functions/CustomLoginPortFromImage.java create mode 100644 apis/docker/src/main/java/org/jclouds/docker/compute/functions/LoginPortForContainer.java create mode 100644 apis/docker/src/main/java/org/jclouds/docker/compute/functions/PublicPortForContainerPort.java create mode 100644 apis/docker/src/test/java/org/jclouds/docker/compute/functions/CustomLoginPortFromImageTest.java diff --git a/apis/docker/pom.xml b/apis/docker/pom.xml index d93d43ba18..a10b263a37 100644 --- a/apis/docker/pom.xml +++ b/apis/docker/pom.xml @@ -84,6 +84,10 @@ jclouds-okhttp ${project.version} + + com.google.inject.extensions + guice-multibindings + org.apache.jclouds jclouds-core diff --git a/apis/docker/src/main/java/org/jclouds/docker/compute/config/DockerComputeServiceContextModule.java b/apis/docker/src/main/java/org/jclouds/docker/compute/config/DockerComputeServiceContextModule.java index 063d6c9e87..f9e03f4e42 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/compute/config/DockerComputeServiceContextModule.java +++ b/apis/docker/src/main/java/org/jclouds/docker/compute/config/DockerComputeServiceContextModule.java @@ -16,8 +16,6 @@ */ package org.jclouds.docker.compute.config; -import com.google.common.base.Function; -import com.google.inject.TypeLiteral; import org.jclouds.compute.ComputeServiceAdapter; import org.jclouds.compute.config.ComputeServiceAdapterContextModule; import org.jclouds.compute.domain.Hardware; @@ -34,8 +32,11 @@ import org.jclouds.docker.domain.State; import org.jclouds.domain.Location; import org.jclouds.functions.IdentityFunction; +import com.google.common.base.Function; +import com.google.inject.TypeLiteral; + public class DockerComputeServiceContextModule extends - ComputeServiceAdapterContextModule { + ComputeServiceAdapterContextModule { @SuppressWarnings("unchecked") @Override @@ -54,6 +55,8 @@ public class DockerComputeServiceContextModule extends bind(new TypeLiteral>() { }).to(StateToStatus.class); bind(TemplateOptions.class).to(DockerTemplateOptions.class); + + install(new LoginPortLookupModule()); } } diff --git a/apis/docker/src/main/java/org/jclouds/docker/compute/config/LoginPortLookupModule.java b/apis/docker/src/main/java/org/jclouds/docker/compute/config/LoginPortLookupModule.java new file mode 100644 index 0000000000..2daf287fd7 --- /dev/null +++ b/apis/docker/src/main/java/org/jclouds/docker/compute/config/LoginPortLookupModule.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.compute.config; + +import org.jclouds.docker.compute.functions.LoginPortForContainer; + +import com.google.inject.AbstractModule; +import com.google.inject.Binder; +import com.google.inject.multibindings.MapBinder; + +public class LoginPortLookupModule extends AbstractModule { + + @Override + protected void configure() { + // Declare it to initialize the binder allowing duplicates. Users may + // provide different functions for the same image in different modules, or + // we could provide predefined functions for known images. This allows + // users to set their own ones too. + loginPortLookupBinder(binder()); + } + + public static MapBinder loginPortLookupBinder(Binder binder) { + return MapBinder.newMapBinder(binder, String.class, LoginPortForContainer.class).permitDuplicates(); + } + +} diff --git a/apis/docker/src/main/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadata.java b/apis/docker/src/main/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadata.java index 20df6a3499..db75e50d42 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadata.java +++ b/apis/docker/src/main/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadata.java @@ -16,9 +16,6 @@ */ package org.jclouds.docker.compute.functions; -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.collect.Iterables.getOnlyElement; - import java.net.URI; import java.util.List; import java.util.Map; @@ -34,7 +31,6 @@ import org.jclouds.compute.domain.NodeMetadataBuilder; import org.jclouds.compute.domain.Processor; import org.jclouds.compute.functions.GroupNamingConvention; import org.jclouds.docker.domain.Container; -import org.jclouds.docker.domain.Port; import org.jclouds.docker.domain.State; import org.jclouds.domain.Location; import org.jclouds.providers.ProviderMetadata; @@ -54,24 +50,26 @@ public class ContainerToNodeMetadata implements Function toPortableStatus; private final GroupNamingConvention nodeNamingConvention; private final Supplier> images; private final Supplier> locations; + private final LoginPortForContainer loginPortForContainer; @Inject - public ContainerToNodeMetadata(ProviderMetadata providerMetadata, Function toPortableStatus, GroupNamingConvention.Factory namingConvention, - Supplier> images, - @Memoized Supplier> locations) { - this.providerMetadata = checkNotNull(providerMetadata, "providerMetadata"); - this.toPortableStatus = checkNotNull(toPortableStatus, "toPortableStatus cannot be null"); - this.nodeNamingConvention = checkNotNull(namingConvention, "namingConvention").createWithoutPrefix(); - this.images = checkNotNull(images, "images cannot be null"); - this.locations = checkNotNull(locations, "locations"); + ContainerToNodeMetadata(ProviderMetadata providerMetadata, + Function toPortableStatus, GroupNamingConvention.Factory namingConvention, + Supplier> images, @Memoized Supplier> locations, + LoginPortForContainer loginPortForContainer) { + this.providerMetadata = providerMetadata; + this.toPortableStatus = toPortableStatus; + this.nodeNamingConvention = namingConvention.createWithoutPrefix(); + this.images = images; + this.locations = locations; + this.loginPortForContainer = loginPortForContainer; } @Override @@ -90,7 +88,7 @@ public class ContainerToNodeMetadata implements Function>> ports = container.networkSettings().ports(); - if (ports != null && ports.containsKey("22/tcp")) { - return Integer.parseInt(getOnlyElement(ports.get("22/tcp")).get("HostPort")); - } - // this is needed in case the container list is coming from listContainers - } else if (container.ports() != null) { - for (Port port : container.ports()) { - if (port.privatePort() == 22) { - return port.publicPort(); - } - } - } - return NO_LOGIN_PORT; - } } diff --git a/apis/docker/src/main/java/org/jclouds/docker/compute/functions/CustomLoginPortFromImage.java b/apis/docker/src/main/java/org/jclouds/docker/compute/functions/CustomLoginPortFromImage.java new file mode 100644 index 0000000000..811cdd4eb1 --- /dev/null +++ b/apis/docker/src/main/java/org/jclouds/docker/compute/functions/CustomLoginPortFromImage.java @@ -0,0 +1,84 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.compute.functions; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.inject.Inject; + +import org.jclouds.docker.domain.Container; + +import com.google.common.annotations.Beta; +import com.google.common.base.Optional; +import com.google.common.base.Predicate; +import com.google.common.collect.Maps; + +@Beta +public class CustomLoginPortFromImage implements LoginPortForContainer { + + private final Map> imageToPortLookup; + + @Inject + CustomLoginPortFromImage(Map> imageToPortLookup) { + this.imageToPortLookup = imageToPortLookup; + } + + @Override + public Optional apply(final Container container) { + Map> matchingFunctions = Maps.filterKeys(imageToPortLookup, + new Predicate() { + @Override + public boolean apply(String input) { + return container.config().image().matches(input); + } + }); + + // We allow to provide several forms in the image-to-function map: + // - redis + // - redis:12 + // - owner/redis:12 + // - registry:5000/owner/redis:12 + // We consider the longest match first, as it is the more accurate one + List sortedImages = new ArrayList(matchingFunctions.keySet()); + Collections.sort(sortedImages, LongestStringFirst); + + for (String currentImage : sortedImages) { + Set functions = matchingFunctions.get(currentImage); + for (LoginPortForContainer function : functions) { + Optional port = function.apply(container); + if (port.isPresent()) { + return port; + } + } + } + + return Optional.absent(); + } + + private static final Comparator LongestStringFirst = new Comparator() { + @Override + public int compare(String s1, String s2) { + return s2.length() - s1.length(); + } + }; + +} diff --git a/apis/docker/src/main/java/org/jclouds/docker/compute/functions/LoginPortForContainer.java b/apis/docker/src/main/java/org/jclouds/docker/compute/functions/LoginPortForContainer.java new file mode 100644 index 0000000000..723f9c0cf3 --- /dev/null +++ b/apis/docker/src/main/java/org/jclouds/docker/compute/functions/LoginPortForContainer.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.compute.functions; + +import javax.inject.Inject; + +import org.jclouds.docker.compute.functions.LoginPortForContainer.LoginPortLookupChain; +import org.jclouds.docker.domain.Container; + +import com.google.common.annotations.Beta; +import com.google.common.base.Function; +import com.google.common.base.Optional; +import com.google.inject.ImplementedBy; + +@Beta +@ImplementedBy(LoginPortLookupChain.class) +public interface LoginPortForContainer extends Function> { + + @Beta + static final class LoginPortLookupChain implements LoginPortForContainer { + private final PublicPortForContainerPort publicPortForContainerPort; + private final CustomLoginPortFromImage customLoginPortFromImage; + + @Inject + LoginPortLookupChain(CustomLoginPortFromImage customLoginPortFromImage) { + this.publicPortForContainerPort = new PublicPortForContainerPort(22); + this.customLoginPortFromImage = customLoginPortFromImage; + } + + @Override + public Optional apply(Container input) { + Optional loginPort = publicPortForContainerPort.apply(input); + return loginPort.isPresent() ? loginPort : customLoginPortFromImage.apply(input); + } + + } +} diff --git a/apis/docker/src/main/java/org/jclouds/docker/compute/functions/PublicPortForContainerPort.java b/apis/docker/src/main/java/org/jclouds/docker/compute/functions/PublicPortForContainerPort.java new file mode 100644 index 0000000000..3b11547178 --- /dev/null +++ b/apis/docker/src/main/java/org/jclouds/docker/compute/functions/PublicPortForContainerPort.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.compute.functions; + +import static com.google.common.collect.Iterables.getOnlyElement; + +import java.util.List; +import java.util.Map; + +import org.jclouds.docker.domain.Container; +import org.jclouds.docker.domain.Port; + +import com.google.common.annotations.Beta; +import com.google.common.base.Optional; + +@Beta +public class PublicPortForContainerPort implements LoginPortForContainer { + + private final int containerPort; + + public PublicPortForContainerPort(int containerPort) { + this.containerPort = containerPort; + } + + @Override + public Optional apply(Container container) { + if (container.networkSettings() != null) { + Map>> ports = container.networkSettings().ports(); + if (ports != null && ports.containsKey(containerPort + "/tcp")) { + return Optional.of(Integer.parseInt(getOnlyElement(ports.get(containerPort + "/tcp")).get("HostPort"))); + } + // this is needed in case the container list is coming from + // listContainers + } else if (container.ports() != null) { + for (Port port : container.ports()) { + if (port.privatePort() == containerPort) { + return Optional.of(port.publicPort()); + } + } + } + return Optional.absent(); + } + +} diff --git a/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadataTest.java b/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadataTest.java index 850863c1af..0f81c948e9 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadataTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadataTest.java @@ -44,7 +44,6 @@ 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; @@ -62,8 +61,6 @@ import com.google.inject.Guice; @Test(groups = "unit", testName = "ContainerToNodeMetadataTest") public class ContainerToNodeMetadataTest { - private LoginCredentials credentials; - private ContainerToNodeMetadata function; private Container container; @@ -170,9 +167,8 @@ public class ContainerToNodeMetadataTest { } }; - credentials = LoginCredentials.builder().user("foo").password("bar").build(); - - function = new ContainerToNodeMetadata(providerMetadata, toPortableStatus(), namingConvention, images, locations); + function = new ContainerToNodeMetadata(providerMetadata, toPortableStatus(), namingConvention, images, locations, + new LoginPortForContainer.LoginPortLookupChain(null)); } private Function toPortableStatus() { diff --git a/apis/docker/src/test/java/org/jclouds/docker/compute/functions/CustomLoginPortFromImageTest.java b/apis/docker/src/test/java/org/jclouds/docker/compute/functions/CustomLoginPortFromImageTest.java new file mode 100644 index 0000000000..3e747d37e7 --- /dev/null +++ b/apis/docker/src/test/java/org/jclouds/docker/compute/functions/CustomLoginPortFromImageTest.java @@ -0,0 +1,106 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.compute.functions; + +import static org.jclouds.docker.compute.config.LoginPortLookupModule.loginPortLookupBinder; +import static org.testng.Assert.assertEquals; + +import org.jclouds.docker.compute.config.LoginPortLookupModule; +import org.jclouds.docker.domain.Config; +import org.jclouds.docker.domain.Container; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import com.google.common.base.Optional; +import com.google.common.base.Predicate; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +import com.google.inject.AbstractModule; +import com.google.inject.Guice; +import com.google.inject.Injector; +import com.google.inject.multibindings.MapBinder; + +@Test(groups = "unit") +public class CustomLoginPortFromImageTest { + + private CustomLoginPortFromImage customLoginPortFromImage; + + @BeforeClass + public void setup() { + Injector i = Guice.createInjector(new LoginPortLookupModule(), new AbstractModule() { + @Override + protected void configure() { + MapBinder imageToFunction = loginPortLookupBinder(binder()); + imageToFunction.addBinding(".*alpine-ext.*").toInstance(LoginPortFromEnvVar); + imageToFunction.addBinding(".*ubuntu.*").toInstance(AlwaysPort22); + imageToFunction.addBinding(".*ubuntu:12\\.04.*").toInstance(AlwaysPort8080); + } + }); + customLoginPortFromImage = i.getInstance(CustomLoginPortFromImage.class); + } + + public void testPortFromEnvironmentVariables() { + Config config = Config.builder().image("alpine-ext:3.2").env(ImmutableList.of("FOO=bar", "SSH_PORT=2345")) + .build(); + Container container = Container.builder().id("id").config(config).build(); + + assertEquals(customLoginPortFromImage.apply(container).get().intValue(), 2345); + } + + public void testMostSpecificImageIsPicked() { + Config config = Config.builder().image("ubuntu:12.04").build(); + Container container = Container.builder().id("id").config(config).build(); + + assertEquals(customLoginPortFromImage.apply(container).get().intValue(), 8080); + } + + public void testNoImageFoundInMap() { + Config config = Config.builder().image("unexisting").build(); + Container container = Container.builder().id("id").config(config).build(); + + assertEquals(customLoginPortFromImage.apply(container), Optional.absent()); + } + + private static final LoginPortForContainer LoginPortFromEnvVar = new LoginPortForContainer() { + @Override + public Optional apply(Container input) { + Optional portVariable = Iterables.tryFind(input.config().env(), new Predicate() { + @Override + public boolean apply(String input) { + String[] var = input.split("="); + return var[0].equals("SSH_PORT"); + } + }); + return portVariable.isPresent() ? Optional.of(Integer.valueOf(portVariable.get().split("=")[1])) : Optional + . absent(); + } + }; + + private static final LoginPortForContainer AlwaysPort22 = new LoginPortForContainer() { + @Override + public Optional apply(Container input) { + return Optional.of(22); + } + }; + + private static final LoginPortForContainer AlwaysPort8080 = new LoginPortForContainer() { + @Override + public Optional apply(Container input) { + return Optional.of(8080); + } + }; +} From 9271c11b6770573d9c8a62249363163baddbb100 Mon Sep 17 00:00:00 2001 From: Josef Cacek Date: Thu, 8 Oct 2015 11:19:09 +0200 Subject: [PATCH 50/81] [JCLOUDS-1010] use a lightweight image as a base for Docker build test --- apis/docker/src/test/resources/Dockerfile | 26 ++++++++++++----------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/apis/docker/src/test/resources/Dockerfile b/apis/docker/src/test/resources/Dockerfile index 3c3b30ea48..f375d33e25 100644 --- a/apis/docker/src/test/resources/Dockerfile +++ b/apis/docker/src/test/resources/Dockerfile @@ -14,19 +14,21 @@ # See the License for the specific language governing permissions and # limitations under the License. # -FROM ubuntu:14.04 -MAINTAINER Sven Dowideit -RUN apt-get update && apt-get install -y openssh-server -RUN mkdir /var/run/sshd + +FROM alpine:3.2 +MAINTAINER JClouds Dev + +ENV DROPBEAR_CONF=/etc/dropbear + +RUN apk add --update dropbear \ + && mkdir -p ${DROPBEAR_CONF} \ + && dropbearkey -t dss -f ${DROPBEAR_CONF}/dropbear_dss_host_key \ + && dropbearkey -t rsa -f ${DROPBEAR_CONF}/dropbear_rsa_host_key -s 2048 \ + && dropbearkey -t ecdsa -f ${DROPBEAR_CONF}/dropbear_ecdsa_host_key + RUN echo 'root:screencast' | chpasswd -RUN sed -i 's/PermitRootLogin without-password/PermitRootLogin yes/' /etc/ssh/sshd_config - -# SSH login fix. Otherwise user is kicked off after login -RUN sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd - -ENV NOTVISIBLE "in users profile" -RUN echo "export VISIBLE=now" >> /etc/profile EXPOSE 22 -CMD ["/usr/sbin/sshd", "-D"] \ No newline at end of file + +CMD ["/usr/sbin/dropbear", "-E", "-F"] From 3342e88f8083922abbfdcd1d60fd2fbb70dc2309 Mon Sep 17 00:00:00 2001 From: Josef Cacek Date: Tue, 20 Oct 2015 15:08:16 +0200 Subject: [PATCH 51/81] [JCLOUDS-1017] add tagImage() method to ImageApi --- .../org/jclouds/docker/features/ImageApi.java | 18 ++++ .../docker/features/ImageApiLiveTest.java | 40 +++++++- .../docker/features/ImageApiMockTest.java | 12 +++ .../jclouds/docker/parse/ImagesParseTest.java | 48 +++++----- apis/docker/src/test/resources/images.json | 91 +++++++++++-------- 5 files changed, 140 insertions(+), 69 deletions(-) diff --git a/apis/docker/src/main/java/org/jclouds/docker/features/ImageApi.java b/apis/docker/src/main/java/org/jclouds/docker/features/ImageApi.java index 9f7cebc0fd..cfca40d471 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/features/ImageApi.java +++ b/apis/docker/src/main/java/org/jclouds/docker/features/ImageApi.java @@ -26,6 +26,7 @@ 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.EmptyListOnNotFoundOr404; @@ -102,4 +103,21 @@ public interface ImageApi { @Path("/images/{name}") InputStream deleteImage(@PathParam("name") String name, DeleteImageOptions options); + /** + * Tag the image name into a repository. + * + * @param name + * the name of the image to be tagged + * @param repoName + * the repository to tag in + * @param tag + * the new tag name + * @param force + * force create if tag already exists + */ + @Named("image:tag") + @POST + @Path("/images/{name}/tag") + void tagImage(@PathParam("name") String name, @QueryParam("repo") String repoName, + @QueryParam("tag") String tag, @QueryParam("force") boolean force); } diff --git a/apis/docker/src/test/java/org/jclouds/docker/features/ImageApiLiveTest.java b/apis/docker/src/test/java/org/jclouds/docker/features/ImageApiLiveTest.java index a145417082..1249f2f875 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/features/ImageApiLiveTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/features/ImageApiLiveTest.java @@ -16,20 +16,30 @@ */ package org.jclouds.docker.features; +import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertNull; + import java.io.InputStream; +import java.util.List; import org.jclouds.docker.compute.BaseDockerApiLiveTest; +import org.jclouds.docker.domain.Image; +import org.jclouds.docker.domain.ImageSummary; import org.jclouds.docker.options.CreateImageOptions; import org.testng.annotations.Test; -@Test(groups = "live", testName = "RemoteApiLiveTest", singleThreaded = true) +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; + +@Test(groups = "live", testName = "ImageApiLiveTest", singleThreaded = true) public class ImageApiLiveTest extends BaseDockerApiLiveTest { private static final String DEFAULT_IMAGE = "busybox"; private static final String DEFAULT_TAG = "ubuntu-14.04"; + private Image image; + @Test public void testCreateImage() { InputStream createImageStream = api().createImage(CreateImageOptions.Builder.fromImage(DEFAULT_IMAGE).tag(DEFAULT_TAG)); @@ -38,18 +48,38 @@ public class ImageApiLiveTest extends BaseDockerApiLiveTest { @Test(dependsOnMethods = "testCreateImage") public void testInspectImage() { - assertNotNull(api.getImageApi().inspectImage(String.format("%s:%s", DEFAULT_IMAGE, DEFAULT_TAG))); + image = api.getImageApi().inspectImage(String.format("%s:%s", DEFAULT_IMAGE, DEFAULT_TAG)); + assertNotNull(image); } @Test(dependsOnMethods = "testInspectImage") - public void testListImages() { - assertNotNull(api().listImages()); + public void testTagImage() { + api.getImageApi().tagImage(image.id(), "jclouds", "testTag", true); + Image taggedImage = api.getImageApi().inspectImage("jclouds:testTag"); + assertEquals(taggedImage.id(), image.id(), "Newly added image tag should point to the same image ID."); } - @Test(dependsOnMethods = "testListImages") + @Test(dependsOnMethods = "testTagImage") + public void testListImages() { + List listImages = api().listImages(); + assertNotNull(listImages); + + Iterables.find(listImages, new Predicate() { + @Override + public boolean apply(ImageSummary input) { + return input.repoTags().contains("jclouds:testTag"); + } + }); + } + + @Test(dependsOnMethods = "testListImages", alwaysRun = true) public void testDeleteImage() { consumeStream(api().deleteImage(String.format("%s:%s", DEFAULT_IMAGE, DEFAULT_TAG))); assertNull(api().inspectImage(String.format("%s:%s", DEFAULT_IMAGE, DEFAULT_TAG))); + + assertNotNull(api().inspectImage(image.id()), "Image should should still exist after removing original tag. There is a newly added tag referencing it."); + consumeStream(api().deleteImage("jclouds:testTag")); + assertNull(api().inspectImage("jclouds:testTag")); } private ImageApi api() { diff --git a/apis/docker/src/test/java/org/jclouds/docker/features/ImageApiMockTest.java b/apis/docker/src/test/java/org/jclouds/docker/features/ImageApiMockTest.java index 5349c39c70..f789b6d3b1 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/features/ImageApiMockTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/features/ImageApiMockTest.java @@ -69,6 +69,18 @@ public class ImageApiMockTest extends BaseDockerMockTest { } } + public void testTagImage() throws Exception { + MockWebServer server = mockWebServer(new MockResponse().setResponseCode(201)); + ImageApi api = api(DockerApi.class, server.getUrl("/").toString()).getImageApi(); + try { + api.tagImage("633fcd11259e8d6bccfbb59a4086b95b0d0fb44edfc3912000ef1f70e8a7bfc6", "jclouds", "testTag", true); + assertSent(server, "POST", + "/images/633fcd11259e8d6bccfbb59a4086b95b0d0fb44edfc3912000ef1f70e8a7bfc6/tag?repo=jclouds&tag=testTag&force=true"); + } finally { + server.shutdown(); + } + } + public void testDeleteImage() throws Exception { MockWebServer server = mockWebServer(new MockResponse().setResponseCode(204)); ImageApi api = api(DockerApi.class, server.getUrl("/").toString()).getImageApi(); diff --git a/apis/docker/src/test/java/org/jclouds/docker/parse/ImagesParseTest.java b/apis/docker/src/test/java/org/jclouds/docker/parse/ImagesParseTest.java index d2ef5b6074..1042da0890 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/parse/ImagesParseTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/parse/ImagesParseTest.java @@ -39,30 +39,30 @@ public class ImagesParseTest extends BaseDockerParseTest> { @Consumes(MediaType.APPLICATION_JSON) public List expected() { return ImmutableList.of( - ImageSummary.create("cbba6639a342646deed70d7ea6162fa2a0acea9300f911f4e014555fe37d3456", - 1416827360, - "05794515afd5724df1cdf0e674ae932455fce7dea3c70a94d77119ad1fa954ba", - 0, - 808709069, - ImmutableList.of("test:latest")), - ImageSummary.create("e1e548b03259ae30ba12232b6c16ef5205cf71b0363848e78b0394e1ecba4f57", - 1416826851, - "6f36bec79c7f184ceebf7000cfb7244c4bc9b397b6659ac7f420a53d114250d9", - 0, - 5609404, - ImmutableList.of(":")), - ImageSummary.create("8201388d2b288539aab6aabf5d3b15ec269eba95c6baa9d6771f16540abf3a3f", - 1414247273, - "4671e2c549c5b60063e349f520c801dc73b53d2226a5a8e5501845ebe94761ca", - 0, - 755313702, - ImmutableList.of("dockerfile/java:openjdk-7-jdk")), - ImageSummary.create("5506de2b643be1e6febbf3b8a240760c6843244c41e12aa2f60ccbb7153d17f5", - 1414108439, - "22093c35d77bb609b9257ffb2640845ec05018e3d96cb939f68d0e19127f1723", - 0, - 199257566, - ImmutableList.of("ubuntu:14.04")) + ImageSummary.create("d7057cb020844f245031d27b76cb18af05db1cc3a96a29fa7777af75f5ac91a3", + 1442866547, + "cfa753dfea5e68a24366dfba16e6edf573daa447abf65bc11619c1a98a3aff54", + 0, + 1095501, + ImmutableList.of("docker.io/busybox:1.23.2", "docker.io/busybox:latest")), + ImageSummary.create("633fcd11259e8d6bccfbb59a4086b95b0d0fb44edfc3912000ef1f70e8a7bfc6", + 1442598293, + "b65c936b5fb601d680ed656b1ccf8ab857c0e5cb521043a005405c194e9a69f3", + 0, + 5607885, + ImmutableList.of("docker.io/busybox:ubuntu-14.04", "jclouds:testTag")), + ImageSummary.create("f4fddc471ec22fc1f7d37768132f1753bc171121e30ac2af7fcb0302588197c0", + 1442260874, + "", + 5244426, + 5244426, + ImmutableList.of("docker.io/alpine:3.2")), + ImageSummary.create("91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c", + 1440102075, + "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82", + 0, + 188333286, + ImmutableList.of("docker.io/ubuntu:14.04", "docker.io/ubuntu:latest")) ); } diff --git a/apis/docker/src/test/resources/images.json b/apis/docker/src/test/resources/images.json index 41d4f12f4b..63bec4f3c2 100644 --- a/apis/docker/src/test/resources/images.json +++ b/apis/docker/src/test/resources/images.json @@ -1,42 +1,53 @@ [ - { - "Created": 1416827360, - "Id": "cbba6639a342646deed70d7ea6162fa2a0acea9300f911f4e014555fe37d3456", - "ParentId": "05794515afd5724df1cdf0e674ae932455fce7dea3c70a94d77119ad1fa954ba", - "RepoTags": [ - "test:latest" - ], - "Size": 0, - "VirtualSize": 808709069 - }, - { - "Created": 1416826851, - "Id": "e1e548b03259ae30ba12232b6c16ef5205cf71b0363848e78b0394e1ecba4f57", - "ParentId": "6f36bec79c7f184ceebf7000cfb7244c4bc9b397b6659ac7f420a53d114250d9", - "RepoTags": [ - ":" - ], - "Size": 0, - "VirtualSize": 5609404 - }, - { - "Created": 1414247273, - "Id": "8201388d2b288539aab6aabf5d3b15ec269eba95c6baa9d6771f16540abf3a3f", - "ParentId": "4671e2c549c5b60063e349f520c801dc73b53d2226a5a8e5501845ebe94761ca", - "RepoTags": [ - "dockerfile/java:openjdk-7-jdk" - ], - "Size": 0, - "VirtualSize": 755313702 - }, - { - "Created": 1414108439, - "Id": "5506de2b643be1e6febbf3b8a240760c6843244c41e12aa2f60ccbb7153d17f5", - "ParentId": "22093c35d77bb609b9257ffb2640845ec05018e3d96cb939f68d0e19127f1723", - "RepoTags": [ - "ubuntu:14.04" - ], - "Size": 0, - "VirtualSize": 199257566 - } + { + "Created": 1442866547, + "Id": "d7057cb020844f245031d27b76cb18af05db1cc3a96a29fa7777af75f5ac91a3", + "Labels": null, + "ParentId": "cfa753dfea5e68a24366dfba16e6edf573daa447abf65bc11619c1a98a3aff54", + "RepoDigests": [], + "RepoTags": [ + "docker.io/busybox:1.23.2", + "docker.io/busybox:latest" + ], + "Size": 0, + "VirtualSize": 1095501 + }, + { + "Created": 1442598293, + "Id": "633fcd11259e8d6bccfbb59a4086b95b0d0fb44edfc3912000ef1f70e8a7bfc6", + "Labels": null, + "ParentId": "b65c936b5fb601d680ed656b1ccf8ab857c0e5cb521043a005405c194e9a69f3", + "RepoDigests": [], + "RepoTags": [ + "docker.io/busybox:ubuntu-14.04", + "jclouds:testTag" + ], + "Size": 0, + "VirtualSize": 5607885 + }, + { + "Created": 1442260874, + "Id": "f4fddc471ec22fc1f7d37768132f1753bc171121e30ac2af7fcb0302588197c0", + "Labels": null, + "ParentId": "", + "RepoDigests": [], + "RepoTags": [ + "docker.io/alpine:3.2" + ], + "Size": 5244426, + "VirtualSize": 5244426 + }, + { + "Created": 1440102075, + "Id": "91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c", + "Labels": {}, + "ParentId": "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82", + "RepoDigests": [], + "RepoTags": [ + "docker.io/ubuntu:14.04", + "docker.io/ubuntu:latest" + ], + "Size": 0, + "VirtualSize": 188333286 + } ] \ No newline at end of file From f0fc31e2998db512171067eb9d18759de80fd1fa Mon Sep 17 00:00:00 2001 From: Josef Cacek Date: Wed, 21 Oct 2015 10:44:30 +0200 Subject: [PATCH 52/81] [JCLOUDS-1020] fix query parameter name in Docker's BuildOptions --- .../src/main/java/org/jclouds/docker/options/BuildOptions.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apis/docker/src/main/java/org/jclouds/docker/options/BuildOptions.java b/apis/docker/src/main/java/org/jclouds/docker/options/BuildOptions.java index 6653a56c3e..15d95e031d 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/options/BuildOptions.java +++ b/apis/docker/src/main/java/org/jclouds/docker/options/BuildOptions.java @@ -21,7 +21,7 @@ import org.jclouds.http.options.BaseHttpRequestOptions; public class BuildOptions extends BaseHttpRequestOptions { public BuildOptions tag(String tag) { - this.queryParameters.put("tag", tag); + this.queryParameters.put("t", tag); return this; } From 3260fa5803048fe189a297cbd031fa1b97473438 Mon Sep 17 00:00:00 2001 From: Josef Cacek Date: Tue, 20 Oct 2015 18:54:58 +0200 Subject: [PATCH 53/81] Add LiveTest to cover loginPort (SSH) customization in Docker. --- .../compute/SshToCustomPortLiveTest.java | 224 ++++++++++++++++++ 1 file changed, 224 insertions(+) create mode 100644 apis/docker/src/test/java/org/jclouds/docker/compute/SshToCustomPortLiveTest.java diff --git a/apis/docker/src/test/java/org/jclouds/docker/compute/SshToCustomPortLiveTest.java b/apis/docker/src/test/java/org/jclouds/docker/compute/SshToCustomPortLiveTest.java new file mode 100644 index 0000000000..62e1e5e848 --- /dev/null +++ b/apis/docker/src/test/java/org/jclouds/docker/compute/SshToCustomPortLiveTest.java @@ -0,0 +1,224 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.compute; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.jclouds.compute.options.TemplateOptions.Builder.runAsRoot; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.List; +import java.util.logging.Level; + +import org.jclouds.compute.RunNodesException; +import org.jclouds.compute.domain.ExecResponse; +import org.jclouds.compute.domain.NodeMetadata; +import org.jclouds.compute.domain.Template; +import org.jclouds.compute.internal.BaseComputeServiceContextLiveTest; +import org.jclouds.docker.DockerApi; +import org.jclouds.docker.compute.functions.LoginPortForContainer; +import org.jclouds.docker.compute.options.DockerTemplateOptions; +import org.jclouds.docker.domain.Container; +import org.jclouds.docker.domain.Image; +import org.jclouds.docker.domain.ImageSummary; +import org.jclouds.docker.options.BuildOptions; +import org.jclouds.sshj.config.SshjSshClientModule; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import com.google.common.base.Optional; +import com.google.common.base.Predicate; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; +import com.google.inject.AbstractModule; +import com.google.inject.Module; + +/** + * This class tests configuring custom SSH port for Docker images. + */ +@Test(groups = "live", testName = "SshToCustomPortLiveTest", singleThreaded = true) +public class SshToCustomPortLiveTest extends BaseComputeServiceContextLiveTest { + + private static final int SSH_PORT = 8822; + private static final String IMAGE_REPOSITORY = "jclouds/testrepo"; + private static final String IMAGE_TAG_1 = "testtag"; + private static final String IMAGE_TAG_2 = "second"; + + private Image image; + + public SshToCustomPortLiveTest() { + provider = "docker"; + } + + /** + * Asserts that the new image exists and tags were created successfully in + * the test preparation phase ({@link #setupContext()} method). + */ + @Test + public void testImageCreated() { + assertNotNull(image); + + final String imageId = image.id(); + assertNotNull(imageId); + + List listImages = api().getImageApi().listImages(); + assertNotNull(listImages); + ImageSummary testImage = Iterables.find(listImages, new Predicate() { + @Override + public boolean apply(ImageSummary input) { + return imageId.equals(input.id()); + } + }); + + assertEquals(testImage.repoTags().size(), 2, "Unexpected number of tags on the image."); + assertThat(testImage.repoTags()).contains(toTag(IMAGE_REPOSITORY, IMAGE_TAG_1), + toTag(IMAGE_REPOSITORY, IMAGE_TAG_2)); + } + + /** + * Start a node from the newly created image. The dropbear SSH server running + * on custom port ( {@value #SSH_PORT}). The Docker networkMode used is + * "host". Execute a command through the SSH connection and check the result. + * Destroy the node when finished. + * + * @throws RunNodesException + */ + @Test(dependsOnMethods = "testImageCreated") + public void testCustomPortSsh() throws RunNodesException { + final DockerTemplateOptions options = DockerTemplateOptions.Builder + .commands("/usr/sbin/dropbear", "-E", "-F", "-p", String.valueOf(SSH_PORT)).overrideLoginUser("root") + .overrideLoginPassword("screencast").blockOnPort(SSH_PORT, 30).networkMode("host"); + + final Template template = view.getComputeService().templateBuilder().imageId(image.id()).options(options).build(); + + String nodeId = null; + try { + NodeMetadata node = Iterables + .getOnlyElement(view.getComputeService().createNodesInGroup("ssh-test", 1, template)); + + nodeId = node.getId(); + ExecResponse response = view.getComputeService().runScriptOnNode(nodeId, "echo hello", + runAsRoot(false).wrapInInitScript(false)); + assertThat(response.getOutput().trim()).endsWith("hello"); + } finally { + if (nodeId != null) + view.getComputeService().destroyNode(nodeId); + } + } + + /** + * Build a new image with 2 tags on it in the test preparation phase. + * + * @see org.jclouds.apis.BaseContextLiveTest#setupContext() + */ + @Override + @BeforeClass(groups = { "integration", "live" }) + public void setupContext() { + super.setupContext(); + + final String tag = toTag(IMAGE_REPOSITORY, IMAGE_TAG_1); + BuildOptions options = BuildOptions.Builder.tag(tag).verbose(false).nocache(false); + InputStream buildImageStream; + try { + buildImageStream = api().getMiscApi().build(BaseDockerApiLiveTest.tarredDockerfile(), options); + consumeStreamSilently(buildImageStream); + } catch (IOException e) { + throw new RuntimeException("Error occured during building Docker image.", e); + } + image = api().getImageApi().inspectImage(tag); + api().getImageApi().tagImage(image.id(), IMAGE_REPOSITORY, IMAGE_TAG_2, true); + } + + /** + * After the test remove created image (with all tags). + * + * @see #setupContext() + */ + @AfterClass(alwaysRun = true) + protected void tearDown() { + consumeStreamSilently(api().getImageApi().deleteImage(toTag(IMAGE_REPOSITORY, IMAGE_TAG_1))); + consumeStreamSilently(api().getImageApi().deleteImage(toTag(IMAGE_REPOSITORY, IMAGE_TAG_2))); + } + + /** + * Configure used modules. A custom {@link LoginPortForContainer} binding is + * added among logging and SSH module. + * + * @see org.jclouds.compute.internal.BaseGenericComputeServiceContextLiveTest#setupModules() + */ + @Override + protected Iterable setupModules() { + return ImmutableSet. of(getLoggingModule(), new SshjSshClientModule(), new AbstractModule() { + @Override + protected void configure() { + bind(LoginPortForContainer.class).toInstance(new LoginPortForContainer() { + @Override + public Optional apply(Container input) { + return Optional.of(SSH_PORT); + } + }); + } + }); + } + + /** + * Return DockerApi for current Context. + * + * @return + */ + private DockerApi api() { + return view.unwrapApi(DockerApi.class); + } + + /** + * Read all data from given InputStream and throw away all the bits. + * + * @param is + */ + private static void consumeStreamSilently(InputStream is) { + char[] tmpBuff = new char[8 * 1024]; + // throw everything away + InputStreamReader isr = new InputStreamReader(is); + + try { + try { + while (isr.read(tmpBuff) > -1) { + // empty + } + } finally { + isr.close(); + } + } catch (IOException e) { + java.util.logging.Logger.getAnonymousLogger().log(Level.WARNING, "Error ocured during reading InputStream.", e); + } + } + + /** + * Concatenate repository and tag name (if provided) in Docker format. + * + * @param repo + * @param tag + * @return + */ + private static String toTag(String repo, String tag) { + return repo + (tag != null ? ":" + tag : ""); + } +} From 746038e8b29e7e52757bd0b10541274c27327816 Mon Sep 17 00:00:00 2001 From: Josef Cacek Date: Wed, 21 Oct 2015 16:57:42 +0200 Subject: [PATCH 54/81] [JCLOUDS-1021] Add JavaDoc to cover behavior of repoTags() method in org.jclouds.docker.domain.Image --- .../strategy/DockerComputeServiceAdapter.java | 6 ++++++ .../main/java/org/jclouds/docker/domain/Image.java | 14 ++++++++++++++ .../java/org/jclouds/docker/features/ImageApi.java | 3 +++ 3 files changed, 23 insertions(+) diff --git a/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java b/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java index b1041d012f..6af757f7bf 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java +++ b/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java @@ -174,6 +174,12 @@ public class DockerComputeServiceAdapter implements return hardware; } + /** + * Method based on {@link org.jclouds.docker.features.ImageApi#listImages()}. It retrieves additional + * information by inspecting each image. + * + * @see org.jclouds.compute.ComputeServiceAdapter#listImages() + */ @Override public Set listImages() { Set images = Sets.newHashSet(); diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/Image.java b/apis/docker/src/main/java/org/jclouds/docker/domain/Image.java index f49a7b01f6..a696c5f863 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/Image.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/Image.java @@ -26,6 +26,9 @@ import org.jclouds.json.SerializedNames; import com.google.auto.value.AutoValue; +/** + * Represents a response from Docker "Inspect an image" call (GET /images/(name)/json). + */ @AutoValue public abstract class Image { @@ -55,6 +58,17 @@ public abstract class Image { public abstract long virtualSize(); + /** + * Tags of the image. The value is null when the instance comes + * from {@link org.jclouds.docker.features.ImageApi#inspectImage(String)}. + * Other methods can populate the content (e.g. + * {@link org.jclouds.docker.compute.strategy.DockerComputeServiceAdapter#listImages()} + * call. + *

+ * The tags are in form "ubuntu:12.10", "docker.io/busybox:1.23.2", ... + *

+ * @return list of tags or null + */ @Nullable public abstract List repoTags(); Image() { diff --git a/apis/docker/src/main/java/org/jclouds/docker/features/ImageApi.java b/apis/docker/src/main/java/org/jclouds/docker/features/ImageApi.java index cfca40d471..441f1c071c 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/features/ImageApi.java +++ b/apis/docker/src/main/java/org/jclouds/docker/features/ImageApi.java @@ -63,6 +63,9 @@ public interface ImageApi { List listImages(ListImageOptions options); /** + * Return low-level information on the image with given name. Not all fields from the returned {@link Image} instance + * are populated by this method (e.g. {@link Image#repoTags()}). + * * @param imageName The id of the image to inspect. * @return low-level information on the image name */ From 33522b8945655d02c6427d841131f8fb0c0c58dc Mon Sep 17 00:00:00 2001 From: Andrea Turli Date: Tue, 10 Nov 2015 16:49:59 +0100 Subject: [PATCH 55/81] add docker NetworkAPI - bump api version to 1.21 - use `alpine/3.2` image for liveTests - use `kwart/alpine-ext:3.2-ssh` image as ssh-able image - assert request bodies created correctly in NetworkApiMockTest --- apis/docker/README.md | 4 +- apis/docker/pom.xml | 2 +- .../java/org/jclouds/docker/DockerApi.java | 8 +- .../org/jclouds/docker/DockerApiMetadata.java | 18 +- .../strategy/DockerComputeServiceAdapter.java | 1 - .../org/jclouds/docker/domain/Network.java | 107 +++++++ .../docker/domain/NetworkSettings.java | 175 +++++++++++- .../java/org/jclouds/docker/domain/State.java | 17 +- .../jclouds/docker/features/NetworkApi.java | 100 +++++++ .../docker/compute/BaseDockerApiLiveTest.java | 5 + .../DockerComputeServiceAdapterLiveTest.java | 7 +- .../ContainerToNodeMetadataTest.java | 6 +- .../docker/config/DockerParserModuleTest.java | 69 ++++- .../docker/features/ContainerApiLiveTest.java | 9 +- .../docker/features/ContainerApiMockTest.java | 2 +- .../docker/features/ImageApiLiveTest.java | 3 - .../docker/features/MiscApiLiveTest.java | 12 +- .../docker/features/NetworkApiLiveTest.java | 125 ++++++++ .../docker/features/NetworkApiMockTest.java | 153 ++++++++++ .../docker/internal/BaseDockerMockTest.java | 15 + .../docker/parse/ContainerParseTest.java | 45 ++- .../docker/parse/NetworkParseTest.java | 71 +++++ .../docker/parse/NetworksParseTest.java | 97 +++++++ apis/docker/src/test/resources/container.json | 270 ++++++++++-------- .../src/test/resources/network-creation.json | 4 + apis/docker/src/test/resources/network.json | 30 ++ apis/docker/src/test/resources/networks.json | 56 ++++ 27 files changed, 1228 insertions(+), 183 deletions(-) create mode 100644 apis/docker/src/main/java/org/jclouds/docker/domain/Network.java create mode 100644 apis/docker/src/main/java/org/jclouds/docker/features/NetworkApi.java create mode 100644 apis/docker/src/test/java/org/jclouds/docker/features/NetworkApiLiveTest.java create mode 100644 apis/docker/src/test/java/org/jclouds/docker/features/NetworkApiMockTest.java create mode 100644 apis/docker/src/test/java/org/jclouds/docker/parse/NetworkParseTest.java create mode 100644 apis/docker/src/test/java/org/jclouds/docker/parse/NetworksParseTest.java create mode 100644 apis/docker/src/test/resources/network-creation.json create mode 100644 apis/docker/src/test/resources/network.json create mode 100644 apis/docker/src/test/resources/networks.json diff --git a/apis/docker/README.md b/apis/docker/README.md index c5080e6ef8..4fdf700750 100644 --- a/apis/docker/README.md +++ b/apis/docker/README.md @@ -8,7 +8,7 @@ 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` then it can also manage certificates and help you setup `DOCKER_CERT_PATH` and `DOCKER_HOST` environment variables. (See `boot2docker shellinit`) +If you are using `docker-machine` then it can also manage certificates and help you setup `DOCKER_CERT_PATH` and `DOCKER_HOST` environment variables. Assuming these environment variables are setup correctly there are no further setups steps are required. @@ -48,7 +48,7 @@ As jclouds docker support is quite new, issues may occasionally arise. Please fo 1. Remove all containers - $ docker rm `docker ps -a` + $ docker rm -f `docker ps -a` 2. remove all the images diff --git a/apis/docker/pom.xml b/apis/docker/pom.xml index a10b263a37..39009fa89d 100644 --- a/apis/docker/pom.xml +++ b/apis/docker/pom.xml @@ -34,7 +34,7 @@ bundle - 1.19 + 1.21 ${env.DOCKER_CERT_PATH}/cert.pem ${env.DOCKER_CERT_PATH}/key.pem ${env.DOCKER_CERT_PATH}/ca.pem diff --git a/apis/docker/src/main/java/org/jclouds/docker/DockerApi.java b/apis/docker/src/main/java/org/jclouds/docker/DockerApi.java index 502f5774fa..4650eac254 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/DockerApi.java +++ b/apis/docker/src/main/java/org/jclouds/docker/DockerApi.java @@ -16,13 +16,14 @@ */ package org.jclouds.docker; +import java.io.Closeable; + import org.jclouds.docker.features.ContainerApi; import org.jclouds.docker.features.ImageApi; import org.jclouds.docker.features.MiscApi; +import org.jclouds.docker.features.NetworkApi; import org.jclouds.rest.annotations.Delegate; -import java.io.Closeable; - public interface DockerApi extends Closeable { @Delegate @@ -34,4 +35,7 @@ public interface DockerApi extends Closeable { @Delegate ImageApi getImageApi(); + @Delegate + NetworkApi getNetworkApi(); + } diff --git a/apis/docker/src/main/java/org/jclouds/docker/DockerApiMetadata.java b/apis/docker/src/main/java/org/jclouds/docker/DockerApiMetadata.java index 65855419f5..9089fd06d8 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/DockerApiMetadata.java +++ b/apis/docker/src/main/java/org/jclouds/docker/DockerApiMetadata.java @@ -16,9 +16,11 @@ */ package org.jclouds.docker; -import com.google.auto.service.AutoService; -import com.google.common.collect.ImmutableSet; -import com.google.inject.Module; +import static org.jclouds.compute.config.ComputeServiceProperties.TEMPLATE; +import static org.jclouds.reflect.Reflection2.typeToken; +import java.net.URI; +import java.util.Properties; + import org.jclouds.Constants; import org.jclouds.apis.ApiMetadata; import org.jclouds.compute.ComputeServiceContext; @@ -28,11 +30,9 @@ import org.jclouds.docker.config.DockerHttpApiModule; import org.jclouds.docker.config.DockerParserModule; import org.jclouds.rest.internal.BaseHttpApiMetadata; -import java.net.URI; -import java.util.Properties; - -import static org.jclouds.compute.config.ComputeServiceProperties.TEMPLATE; -import static org.jclouds.reflect.Reflection2.typeToken; +import com.google.auto.service.AutoService; +import com.google.common.collect.ImmutableSet; +import com.google.inject.Module; @AutoService(ApiMetadata.class) public class DockerApiMetadata extends BaseHttpApiMetadata { @@ -70,7 +70,7 @@ public class DockerApiMetadata extends BaseHttpApiMetadata { .identityName("Path to certificate .pem file") .credentialName("Path to key .pem file") .documentation(URI.create("https://docs.docker.com/reference/api/docker_remote_api/")) - .version("1.16") + .version("1.21") .defaultEndpoint("https://127.0.0.1:2376") .defaultProperties(DockerApiMetadata.defaultProperties()) .view(typeToken(ComputeServiceContext.class)) diff --git a/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java b/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java index 6af757f7bf..d800b7e24c 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java +++ b/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java @@ -18,7 +18,6 @@ package org.jclouds.docker.compute.strategy; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.collect.Iterables.find; - import java.util.List; import java.util.Map; import java.util.Set; diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/Network.java b/apis/docker/src/main/java/org/jclouds/docker/domain/Network.java new file mode 100644 index 0000000000..74f9e816e7 --- /dev/null +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/Network.java @@ -0,0 +1,107 @@ +/* + * 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 java.util.Map; + +import org.jclouds.javax.annotation.Nullable; +import org.jclouds.json.SerializedNames; + +import com.google.auto.value.AutoValue; + +@AutoValue +public abstract class Network { + + @AutoValue + public abstract static class IPAM { + + IPAM() {} // For AutoValue only! + + @Nullable + public abstract String driver(); + + public abstract List config(); + + @SerializedNames({"Driver", "Config"}) + public static IPAM create(String driver, List config) { + return new AutoValue_Network_IPAM(driver, copyOf(config)); + } + + @AutoValue + public abstract static class Config { + + Config() {} // For AutoValue only! + + public abstract String subnet(); + + @Nullable + public abstract String ipRange(); + + @Nullable + public abstract String gateway(); + + @SerializedNames({"Subnet", "IPRange", "Gateway"}) + public static Config create(String subnet, String ipRange, String gateway) { + return new AutoValue_Network_IPAM_Config(subnet, ipRange, gateway); + } + } + } + + @AutoValue + public abstract static class Details { + + Details() {} // For AutoValue only! + + public abstract String endpoint(); + + public abstract String macAddress(); + + public abstract String ipv4address(); + + public abstract String ipv6address(); + + @SerializedNames({ "EndpointID", "MacAddress", "IPv4Address", "IPv6Address" }) + public static Details create(String endpoint, String macAddress, String ipv4address, String ipv6address) { + return new AutoValue_Network_Details(endpoint, macAddress, ipv4address, ipv6address); + } + } + + @Nullable public abstract String name(); + + @Nullable public abstract String id(); + + @Nullable public abstract String scope(); + + @Nullable public abstract String driver(); + + @Nullable public abstract IPAM ipam(); + + public abstract Map containers(); + + public abstract Map options(); + + Network() {} + + @SerializedNames({ "Name", "Id", "Scope", "Driver", "IPAM", "Containers", "Options" }) + public static Network create(String name, String id, String scope, String driver, IPAM ipam, + Map containers, Map options) { + return new AutoValue_Network(name, id, scope, driver, ipam, copyOf(containers), copyOf(options)); + } + +} diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/NetworkSettings.java b/apis/docker/src/main/java/org/jclouds/docker/domain/NetworkSettings.java index fee82f2224..ca2772916c 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/NetworkSettings.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/NetworkSettings.java @@ -18,7 +18,6 @@ package org.jclouds.docker.domain; import static com.google.common.base.Preconditions.checkNotNull; import static org.jclouds.docker.internal.NullSafeCopies.copyOf; - import java.util.List; import java.util.Map; @@ -27,28 +26,98 @@ import org.jclouds.json.SerializedNames; import com.google.auto.value.AutoValue; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; @AutoValue public abstract class NetworkSettings { + + @AutoValue + public abstract static class Details { + + Details() {} // For AutoValue only! + + public abstract String endpoint(); + + public abstract String gateway(); + + public abstract String ipAddress(); + + public abstract int ipPrefixLen(); + + public abstract String ipv6Gateway(); + + public abstract String globalIPv6Address(); + + public abstract int globalIPv6PrefixLen(); + + public abstract String macAddress(); + + @SerializedNames({ "EndpointID", "Gateway", "IPAddress", "IPPrefixLen", "IPv6Gateway", "GlobalIPv6Address", "GlobalIPv6PrefixLen", "MacAddress" }) + public static Details create(String endpointId, String gateway, String ipAddress, int ipPrefixLen, String ipv6Gateway, String globalIPv6Address, + int globalIPv6PrefixLen, String macAddress) { + return new AutoValue_NetworkSettings_Details(endpointId, gateway, ipAddress, ipPrefixLen, ipv6Gateway, globalIPv6Address, globalIPv6PrefixLen, + macAddress); + } + } + + public abstract String bridge(); + + @Nullable public abstract String sandboxId(); + + public abstract boolean hairpinMode(); + + @Nullable public abstract String linkLocalIPv6Address(); + + public abstract int linkLocalIPv6PrefixLen(); + + @Nullable public abstract Map>> ports(); + + @Nullable public abstract String sandboxKey(); + + public abstract List secondaryIPAddresses(); + + public abstract List secondaryIPv6Addresses(); + + @Nullable public abstract String endpointId(); + + public abstract String gateway(); + + @Nullable public abstract String globalIPv6Address(); + + public abstract int globalIPv6PrefixLen(); + public abstract String ipAddress(); public abstract int ipPrefixLen(); - public abstract String gateway(); + @Nullable public abstract String ipv6Gateway(); - public abstract String bridge(); + @Nullable public abstract String macAddress(); + + public abstract Map networks(); @Nullable public abstract String portMapping(); - public abstract Map>> ports(); - NetworkSettings() { } - @SerializedNames({ "IPAddress", "IPPrefixLen", "Gateway", "Bridge", "PortMapping", "Ports" }) - public static NetworkSettings create(String ipAddress, int ipPrefixLen, String gateway, String bridge, - String portMapping, Map>> ports) { - return new AutoValue_NetworkSettings(ipAddress, ipPrefixLen, gateway, bridge, portMapping, copyOf(ports)); + @SerializedNames({ "Bridge", "SandboxID", "HairpinMode", "LinkLocalIPv6Address", + "LinkLocalIPv6PrefixLen", "Ports", "SandboxKey", "SecondaryIPAddresses", + "SecondaryIPv6Addresses", "EndpointID", "Gateway", "GlobalIPv6Address", + "GlobalIPv6PrefixLen", "IPAddress", "IPPrefixLen", "IPv6Gateway", + "MacAddress", "Networks", "PortMapping" }) + public static NetworkSettings create(String bridge, String sandboxId, boolean hairpinMode, String linkLocalIPv6Address, + int linkLocalIPv6PrefixLen, Map>> ports, String sandboxKey, List secondaryIPAddresses, + List secondaryIPv6Addresses, String endpointId, String gateway, String globalIPv6Address, + int globalIPv6PrefixLen, String ipAddress, int ipPrefixLen, String ipv6Gateway, + String macAddress, Map networks, String portMapping) { + return new AutoValue_NetworkSettings( + bridge, sandboxId, hairpinMode, linkLocalIPv6Address, + linkLocalIPv6PrefixLen, ports, sandboxKey, copyOf(secondaryIPAddresses), copyOf(secondaryIPv6Addresses), + endpointId, gateway, globalIPv6Address, globalIPv6PrefixLen, + ipAddress, ipPrefixLen, ipv6Gateway, + macAddress, copyOf(networks), portMapping); } public static Builder builder() { @@ -67,6 +136,19 @@ public abstract class NetworkSettings { private String bridge; private String portMapping; private Map>> ports = ImmutableMap.of(); + private String sandboxId; + private boolean hairpinMode; + private String linkLocalIPv6Address; + private int linkLocalIPv6PrefixLen; + private String sandboxKey; + private List secondaryIPAddresses = Lists.newArrayList(); + private List secondaryIPv6Addresses = Lists.newArrayList(); + private String endpointId; + private String globalIPv6Address; + private int globalIPv6PrefixLen; + private String ipv6Gateway; + private String macAddress; + private Map networks = Maps.newHashMap(); public Builder ipAddress(String ipAddress) { this.ipAddress = ipAddress; @@ -98,13 +180,84 @@ public abstract class NetworkSettings { return this; } + public Builder sandboxId(String sandboxId) { + this.sandboxId = sandboxId; + return this; + } + + public Builder hairpinMode(boolean hairpinMode) { + this.hairpinMode = hairpinMode; + return this; + } + + public Builder linkLocalIPv6Address(String linkLocalIPv6Address) { + this.linkLocalIPv6Address = linkLocalIPv6Address; + return this; + } + + public Builder linkLocalIPv6PrefixLen(int linkLocalIPv6PrefixLen) { + this.linkLocalIPv6PrefixLen = linkLocalIPv6PrefixLen; + return this; + } + + public Builder sandboxKey(String sandboxKey) { + this.sandboxKey = sandboxKey; + return this; + } + + public Builder secondaryIPAddresses(List secondaryIPAddresses) { + this.secondaryIPAddresses = secondaryIPAddresses; + return this; + } + + public Builder secondaryIPv6Addresses(List secondaryIPv6Addresses) { + this.secondaryIPv6Addresses = secondaryIPv6Addresses; + return this; + } + + public Builder endpointId(String endpointId) { + this.endpointId = endpointId; + return this; + } + + public Builder globalIPv6Address(String globalIPv6Address) { + this.globalIPv6Address = globalIPv6Address; + return this; + } + + public Builder globalIPv6PrefixLen(int globalIPv6PrefixLen) { + this.globalIPv6PrefixLen = globalIPv6PrefixLen; + return this; + } + + public Builder ipv6Gateway(String ipv6Gateway) { + this.ipv6Gateway = ipv6Gateway; + return this; + } + + public Builder macAddress(String macAddress) { + this.macAddress = macAddress; + return this; + } + + public Builder networks(Map networks) { + this.networks.putAll(networks); + return this; + } + public NetworkSettings build() { - return NetworkSettings.create(ipAddress, ipPrefixLen, gateway, bridge, portMapping, ports); + return NetworkSettings.create(bridge, sandboxId, hairpinMode, linkLocalIPv6Address, linkLocalIPv6PrefixLen, ports, + sandboxKey, secondaryIPAddresses, secondaryIPv6Addresses, endpointId, gateway, + globalIPv6Address, globalIPv6PrefixLen, ipAddress, ipPrefixLen, ipv6Gateway, macAddress, networks, portMapping); } public Builder fromNetworkSettings(NetworkSettings in) { return this.ipAddress(in.ipAddress()).ipPrefixLen(in.ipPrefixLen()).gateway(in.gateway()).bridge(in.bridge()) - .portMapping(in.portMapping()).ports(in.ports()); + .portMapping(in.portMapping()).ports(in.ports()).sandboxId(in.sandboxId()).hairpinMode(in.hairpinMode()).linkLocalIPv6Address(in + .linkLocalIPv6Address()).linkLocalIPv6PrefixLen(in.linkLocalIPv6PrefixLen()).sandboxKey(in.sandboxKey()).secondaryIPAddresses(in + .secondaryIPAddresses()).secondaryIPv6Addresses(in.secondaryIPv6Addresses()).endpointId(in.endpointId()).globalIPv6Address(in + .globalIPv6Address()).globalIPv6PrefixLen(in.globalIPv6PrefixLen()).ipv6Gateway(in.ipv6Gateway()).macAddress(in.macAddress()) + .networks(in.networks()); } } diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/State.java b/apis/docker/src/main/java/org/jclouds/docker/domain/State.java index 1b3c809af3..54d96cff04 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/State.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/State.java @@ -16,12 +16,15 @@ */ package org.jclouds.docker.domain; +import org.jclouds.javax.annotation.Nullable; import org.jclouds.json.SerializedNames; import com.google.auto.value.AutoValue; @AutoValue public abstract class State { + + public abstract int pid(); public abstract boolean running(); @@ -36,12 +39,20 @@ public abstract class State { public abstract boolean restarting(); + @Nullable public abstract String status(); + + @Nullable public abstract boolean oomKilled(); + + @Nullable public abstract boolean dead(); + + @Nullable public abstract String error(); + State() { } - @SerializedNames({ "Pid", "Running", "ExitCode", "StartedAt", "FinishedAt", "Paused", "Restarting" }) + @SerializedNames({ "Pid", "Running", "ExitCode", "StartedAt", "FinishedAt", "Paused", "Restarting", "Status", "OOMKilled", "Dead", "Error" }) public static State create(int pid, boolean running, int exitCode, String startedAt, String finishedAt, - boolean paused, boolean restarting) { - return new AutoValue_State(pid, running, exitCode, startedAt, finishedAt, paused, restarting); + boolean paused, boolean restarting, String status, boolean oomKilled, boolean dead, String error) { + return new AutoValue_State(pid, running, exitCode, startedAt, finishedAt, paused, restarting, status, oomKilled, dead, error); } } diff --git a/apis/docker/src/main/java/org/jclouds/docker/features/NetworkApi.java b/apis/docker/src/main/java/org/jclouds/docker/features/NetworkApi.java new file mode 100644 index 0000000000..79671cc61e --- /dev/null +++ b/apis/docker/src/main/java/org/jclouds/docker/features/NetworkApi.java @@ -0,0 +1,100 @@ +/* + * 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.core.MediaType; + +import org.jclouds.Fallbacks.EmptyListOnNotFoundOr404; +import org.jclouds.Fallbacks.NullOnNotFoundOr404; +import org.jclouds.docker.domain.Network; +import org.jclouds.rest.annotations.BinderParam; +import org.jclouds.rest.annotations.Fallback; +import org.jclouds.rest.annotations.Headers; +import org.jclouds.rest.annotations.Payload; +import org.jclouds.rest.annotations.PayloadParam; +import org.jclouds.rest.binders.BindToJsonPayload; + +@Consumes(MediaType.APPLICATION_JSON) +@Path("/v{jclouds.api-version}/networks") +public interface NetworkApi { + + /** + * @return a set of networks + */ + @Named("networks:list") + @GET + @Fallback(EmptyListOnNotFoundOr404.class) + List listNetworks(); + + /** + * @param network the network’s configuration (@see BindToJsonPayload) + * @return a new network + */ + @Named("network:create") + @POST + @Path("/create") + Network createNetwork(@BinderParam(BindToJsonPayload.class) Network network); + + /** + * Return low-level information on the network id + * @param networkIdOrName The id or name of the network to get. + * @return The details of the network or null if the network with the given id doesn't exist. + */ + @Named("network:inspect") + @GET + @Path("/{idOrName}") + @Fallback(NullOnNotFoundOr404.class) + Network inspectNetwork(@PathParam("idOrName") String networkIdOrName); + + /** + * @param networkIdOrName The id or name of the network to be removed. + */ + @Named("network:delete") + @DELETE + @Path("/{idOrName}") + void removeNetwork(@PathParam("idOrName") String networkIdOrName); + + /** + * @param networkIdOrName The id or name of the network where the container will be attached. + */ + @Named("network:connectContainer") + @POST + @Path("/{idOrName}/connect") + @Payload("%7B\"Container\":\"{containerIdOrName}\"%7D") + @Headers(keys = "Content-Type", values = "application/json") + void connectContainerToNetwork(@PathParam("idOrName") String networkIdOrName, @PayloadParam("containerIdOrName") String containerIdOrName); + + /** + * @param networkIdOrName The id or name of the network where the container was attached. + */ + @Named("network:disconnectContainer") + @POST + @Path("/{idOrName}/disconnect") + @Payload("%7B\"Container\":\"{containerIdOrName}\"%7D") + @Headers(keys = "Content-Type", values = "application/json") + void disconnectContainerFromNetwork(@PathParam("idOrName") String networkIdOrName, @PayloadParam("containerIdOrName") String containerIdOrName); + +} diff --git a/apis/docker/src/test/java/org/jclouds/docker/compute/BaseDockerApiLiveTest.java b/apis/docker/src/test/java/org/jclouds/docker/compute/BaseDockerApiLiveTest.java index 5778d9af79..eab76ba8ec 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/compute/BaseDockerApiLiveTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/compute/BaseDockerApiLiveTest.java @@ -45,6 +45,11 @@ import com.google.inject.Module; @Test(groups = "live") public class BaseDockerApiLiveTest extends BaseApiLiveTest { + protected static final String DEFAULT_IMAGE = "alpine"; + protected static final String DEFAULT_TAG = "3.2"; + protected static final String ALPINE_IMAGE_TAG = String.format("%s:%s", DEFAULT_IMAGE, DEFAULT_TAG); + + public BaseDockerApiLiveTest() { provider = "docker"; } diff --git a/apis/docker/src/test/java/org/jclouds/docker/compute/DockerComputeServiceAdapterLiveTest.java b/apis/docker/src/test/java/org/jclouds/docker/compute/DockerComputeServiceAdapterLiveTest.java index 9aae9bffa4..faad0ac51e 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/compute/DockerComputeServiceAdapterLiveTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/compute/DockerComputeServiceAdapterLiveTest.java @@ -22,7 +22,6 @@ 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; @@ -48,13 +47,12 @@ 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 static final String SSHABLE_IMAGE = "kwart/alpine-ext"; + private static final String SSHABLE_IMAGE_TAG = "3.2-ssh"; private Image defaultImage; private DockerComputeServiceAdapter adapter; private TemplateBuilder templateBuilder; - private ComputeService computeService; private NodeAndInitialCredentials guest; @BeforeClass @@ -75,7 +73,6 @@ public class DockerComputeServiceAdapterLiveTest extends BaseDockerApiLiveTest { 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); } diff --git a/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadataTest.java b/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadataTest.java index 0f81c948e9..00cd83657b 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadataTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadataTest.java @@ -96,7 +96,11 @@ public class ContainerToNodeMetadataTest { "2014-03-24T20:28:37.537659054Z", // startedAt "0001-01-01T00:00:00Z", // finishedAt false, // paused - false // restarting + false, // restarting + "running", // Status + false, // OOMKilled + false, // Dead + "" // Error ); container = Container.builder() .id("6d35806c1bd2b25cd92bba2d2c2c5169dc2156f53ab45c2b62d76e2d2fee14a9") diff --git a/apis/docker/src/test/java/org/jclouds/docker/config/DockerParserModuleTest.java b/apis/docker/src/test/java/org/jclouds/docker/config/DockerParserModuleTest.java index 57659c5970..33564eaeb1 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/config/DockerParserModuleTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/config/DockerParserModuleTest.java @@ -19,6 +19,9 @@ package org.jclouds.docker.config; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNotNull; +import java.util.List; +import java.util.Map; + import org.jclouds.docker.domain.Container; import org.jclouds.docker.domain.NetworkSettings; import org.jclouds.docker.domain.Port; @@ -53,8 +56,70 @@ public class DockerParserModuleTest { } public void networkSettings() { - String text = "{\"IPAddress\":\"XX.XX.206.98\",\"IPPrefixLen\":27,\"Gateway\":\"XX.XX.206.105\",\"Bridge\":\"public\",\"Ports\":{}}"; - NetworkSettings settings = NetworkSettings.create("XX.XX.206.98", 27, "XX.XX.206.105", "public", null, null); + String text = "{" + + "\"Bridge\":\"\"," + + "\"SandboxID\":\"3ef128b055eb9ef62a6a2c281d97a2dfde5f47947d490f1dd2a81612611d961f\"," + + "\"HairpinMode\":false," + + "\"LinkLocalIPv6Address\":\"\"," + + "\"LinkLocalIPv6PrefixLen\":0," + + "\"Ports\":{}," + + "\"SandboxKey\":\"/var/run/docker/netns/3ef128b055eb\"," + + "\"SecondaryIPAddresses\":[]," + + "\"SecondaryIPv6Addresses\":[]," + + "\"EndpointID\":\"9e8dcc0c8288938a923018fee0728cee8e6de7c01a5150738ee6e51c1caf8cf6\"," + + "\"Gateway\":\"172.17.0.1\"," + + "\"GlobalIPv6Address\":\"\"," + + "\"GlobalIPv6PrefixLen\":0," + + "\"IPAddress\":\"172.17.0.2\"," + + "\"IPPrefixLen\":16," + + "\"IPv6Gateway\":\"\"," + + "\"MacAddress\":\"02:42:ac:11:00:02\"," + + "\"Networks\":{" + + "\"bridge\":{" + + "\"EndpointID\":\"9e8dcc0c8288938a923018fee0728cee8e6de7c01a5150738ee6e51c1caf8cf6\"," + + "\"Gateway\":\"172.17.0.1\"," + + "\"IPAddress\":\"172.17.0.2\"," + + "\"IPPrefixLen\":16," + + "\"IPv6Gateway\":\"\"," + + "\"GlobalIPv6Address\":\"\"," + + "\"GlobalIPv6PrefixLen\":0," + + "\"MacAddress\":\"02:42:ac:11:00:02\"" + + "}" + + "}" + + "}"; + NetworkSettings settings = NetworkSettings.create( + "", // Bridge + "3ef128b055eb9ef62a6a2c281d97a2dfde5f47947d490f1dd2a81612611d961f", // SandboxID + false, // HairpinMode + "", // LinkLocalIPv6Address + 0, // LinkLocalIPv6PrefixLen + ImmutableMap.>> of(), // Ports + "/var/run/docker/netns/3ef128b055eb", // SandboxKey + null, // SecondaryIPAddresses + null, // SecondaryIPv6Addresses + "9e8dcc0c8288938a923018fee0728cee8e6de7c01a5150738ee6e51c1caf8cf6", // EndpointID + "172.17.0.1", // Gateway + "", // GlobalIPv6Address + 0, // GlobalIPv6PrefixLen + "172.17.0.2", // IPAddress + 16, // IPPrefixLen + "", // IPv6Gateway + "02:42:ac:11:00:02", // MacAddress + ImmutableMap.of( + "bridge", NetworkSettings.Details.create( + "9e8dcc0c8288938a923018fee0728cee8e6de7c01a5150738ee6e51c1caf8cf6", // EndpointID + "172.17.0.1", // Gateway + "172.17.0.2", // IPAddress + 16, // IPPrefixLen + "", // IPv6Gateway + "", // GlobalIPv6Address + 0, // GlobalIPv6PrefixLen + "02:42:ac:11:00:02" // MacAddress + ) + ), + null // PortMapping + ); + assertEquals(json.fromJson(text, NetworkSettings.class), settings); assertEquals(json.toJson(settings), text); } diff --git a/apis/docker/src/test/java/org/jclouds/docker/features/ContainerApiLiveTest.java b/apis/docker/src/test/java/org/jclouds/docker/features/ContainerApiLiveTest.java index 23e7e7b78e..295eb5b915 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/features/ContainerApiLiveTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/features/ContainerApiLiveTest.java @@ -47,17 +47,16 @@ import com.google.common.collect.ImmutableList; 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); + if (api.getImageApi().inspectImage(ALPINE_IMAGE_TAG) == null) { + CreateImageOptions options = CreateImageOptions.Builder.fromImage(ALPINE_IMAGE_TAG); InputStream createImageStream = api.getImageApi().createImage(options); consumeStream(createImageStream); } - image = api.getImageApi().inspectImage(BUSYBOX_IMAGE_TAG); + image = api.getImageApi().inspectImage(ALPINE_IMAGE_TAG); assertNotNull(image); } @@ -69,7 +68,7 @@ public class ContainerApiLiveTest extends BaseDockerApiLiveTest { } } if (image != null) { - api.getImageApi().deleteImage(BUSYBOX_IMAGE_TAG); + api.getImageApi().deleteImage(ALPINE_IMAGE_TAG); } } diff --git a/apis/docker/src/test/java/org/jclouds/docker/features/ContainerApiMockTest.java b/apis/docker/src/test/java/org/jclouds/docker/features/ContainerApiMockTest.java index ab3657b107..a9d32b5f58 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/features/ContainerApiMockTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/features/ContainerApiMockTest.java @@ -78,7 +78,7 @@ public class ContainerApiMockTest extends BaseDockerMockTest { public void testGetContainer() throws Exception { MockWebServer server = mockWebServer(new MockResponse().setBody(payloadFromResource("/container.json"))); ContainerApi api = api(DockerApi.class, server.getUrl("/").toString(), new DockerParserModule()).getContainerApi(); - String containerId = "b03d4cd15b76f8876110615cdeed15eadf77c9beb408d62f1687dcc69192cd6d"; + String containerId = "e475abdf3e139a5e1e158b38b6cb290a1bec856d39d5a951f015dfb8fcba7331"; try { assertEquals(api.inspectContainer(containerId), new ContainerParseTest().expected()); assertSent(server, "GET", "/containers/" + containerId + "/json"); diff --git a/apis/docker/src/test/java/org/jclouds/docker/features/ImageApiLiveTest.java b/apis/docker/src/test/java/org/jclouds/docker/features/ImageApiLiveTest.java index 1249f2f875..051ea013e1 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/features/ImageApiLiveTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/features/ImageApiLiveTest.java @@ -35,9 +35,6 @@ import com.google.common.collect.Iterables; @Test(groups = "live", testName = "ImageApiLiveTest", singleThreaded = true) public class ImageApiLiveTest extends BaseDockerApiLiveTest { - private static final String DEFAULT_IMAGE = "busybox"; - private static final String DEFAULT_TAG = "ubuntu-14.04"; - private Image image; @Test diff --git a/apis/docker/src/test/java/org/jclouds/docker/features/MiscApiLiveTest.java b/apis/docker/src/test/java/org/jclouds/docker/features/MiscApiLiveTest.java index 8cdd76bf5e..95b749bd7d 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/features/MiscApiLiveTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/features/MiscApiLiveTest.java @@ -51,8 +51,6 @@ import com.google.common.collect.Iterables; @Test(groups = "live", testName = "MiscApiLiveTest", singleThreaded = true) public class MiscApiLiveTest extends BaseDockerApiLiveTest { - protected static final String BUSYBOX_IMAGE_TAG = "busybox:ubuntu-12.04"; - private static String imageId; private Container container = null; @@ -61,12 +59,12 @@ public class MiscApiLiveTest extends BaseDockerApiLiveTest { @BeforeClass protected void init() { - if (api.getImageApi().inspectImage(BUSYBOX_IMAGE_TAG) == null) { - CreateImageOptions options = CreateImageOptions.Builder.fromImage(BUSYBOX_IMAGE_TAG); + if (api.getImageApi().inspectImage(ALPINE_IMAGE_TAG) == null) { + CreateImageOptions options = CreateImageOptions.Builder.fromImage(ALPINE_IMAGE_TAG); InputStream createImageStream = api.getImageApi().createImage(options); consumeStream(createImageStream); } - image = api.getImageApi().inspectImage(BUSYBOX_IMAGE_TAG); + image = api.getImageApi().inspectImage(ALPINE_IMAGE_TAG); assertNotNull(image); Config containerConfig = Config.builder().image(image.id()) .cmd(ImmutableList.of("/bin/sh", "-c", "touch hello; while true; do echo hello world; sleep 1; done")) @@ -85,7 +83,7 @@ public class MiscApiLiveTest extends BaseDockerApiLiveTest { } } if (image != null) { - api.getImageApi().deleteImage(BUSYBOX_IMAGE_TAG); + api.getImageApi().deleteImage(ALPINE_IMAGE_TAG); } } @@ -107,7 +105,7 @@ public class MiscApiLiveTest extends BaseDockerApiLiveTest { @Test public void testBuildImageFromDockerfile() throws IOException, InterruptedException, URISyntaxException { - BuildOptions options = BuildOptions.Builder.tag("testBuildImage").verbose(false).nocache(false); + BuildOptions options = BuildOptions.Builder.tag("jclouds-test-test-build-image").verbose(false).nocache(false); InputStream buildImageStream = api().build(tarredDockerfile(), options); String buildStream = consumeStream(buildImageStream); Iterable splitted = Splitter.on("\n").split(buildStream.replace("\r", "").trim()); diff --git a/apis/docker/src/test/java/org/jclouds/docker/features/NetworkApiLiveTest.java b/apis/docker/src/test/java/org/jclouds/docker/features/NetworkApiLiveTest.java new file mode 100644 index 0000000000..8edafa3ce4 --- /dev/null +++ b/apis/docker/src/test/java/org/jclouds/docker/features/NetworkApiLiveTest.java @@ -0,0 +1,125 @@ +/* + * 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.assertNull; +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.Image; +import org.jclouds.docker.domain.Network; +import org.jclouds.docker.options.CreateImageOptions; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import com.google.common.base.Predicates; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Iterables; + +@Test(groups = "live", testName = "NetworkApiLiveTest", singleThreaded = true) +public class NetworkApiLiveTest extends BaseDockerApiLiveTest { + + private static final String NETWORK_NAME = "JCLOUDS_NETWORK"; + + private Network network = null; + protected Image image = null; + private Container container; + + @BeforeClass + protected void init() { + + if (api.getImageApi().inspectImage(ALPINE_IMAGE_TAG) == null) { + CreateImageOptions options = CreateImageOptions.Builder.fromImage(ALPINE_IMAGE_TAG); + InputStream createImageStream = api.getImageApi().createImage(options); + consumeStream(createImageStream); + } + image = api.getImageApi().inspectImage(ALPINE_IMAGE_TAG); + assertNotNull(image); + + Config containerConfig = Config.builder().image(image.id()) + .cmd(ImmutableList.of("sh", "-c", "touch hello; while true; do echo hello world; sleep 1; done")) + .build(); + container = api.getContainerApi().createContainer("jclouds-test-network", containerConfig); + api.getContainerApi().startContainer(container.id()); + container = api.getContainerApi().inspectContainer(container.id()); + } + + @AfterClass(alwaysRun = true) + protected void tearDown() { + if (container != null) { + api.getContainerApi().stopContainer(container.id()); + api.getContainerApi().removeContainer(container.id()); + } + if (network != null) { + api().removeNetwork(network.id()); + } + } + + public void testCreateNetwork() throws IOException, InterruptedException { + network = api().createNetwork(Network.create(NETWORK_NAME, null, null, null, null, ImmutableMap. of(), ImmutableMap. of())); + assertNotNull(network); + assertNotNull(network.id()); + } + + @Test(dependsOnMethods = "testCreateNetwork") + public void testGetNetwork() { + network = api().inspectNetwork(network.id()); + assertNotNull(network); + } + + @Test(dependsOnMethods = "testGetNetwork") + public void testAttachContainerToNetwork() { + api().connectContainerToNetwork(network.id(), container.id()); + container = api.getContainerApi().inspectContainer(container.id()); + assertTrue(Iterables.any(container.networkSettings().networks().keySet(), Predicates.equalTo(network.name()))); + } + + @Test(dependsOnMethods = "testAttachContainerToNetwork") + public void testDisconnectContainerFromNetwork() { + api().disconnectContainerFromNetwork(network.id(), container.id()); + container = api.getContainerApi().inspectContainer(container.id()); + assertFalse(Iterables.any(container.networkSettings().networks().keySet(), Predicates.equalTo(network.name()))); + } + + @Test(dependsOnMethods = "testCreateNetwork") + public void testListNetworks() { + List networks = api().listNetworks(); + for (Network network : networks) { + assertNotNull(network.id()); + } + } + + @Test(dependsOnMethods = "testDisconnectContainerFromNetwork") + public void testRemoveNetwork() { + api().removeNetwork(network.id()); + assertNull(api().inspectNetwork(network.id())); + network = null; + } + + private NetworkApi api() { + return api.getNetworkApi(); + } +} diff --git a/apis/docker/src/test/java/org/jclouds/docker/features/NetworkApiMockTest.java b/apis/docker/src/test/java/org/jclouds/docker/features/NetworkApiMockTest.java new file mode 100644 index 0000000000..f35e2572c4 --- /dev/null +++ b/apis/docker/src/test/java/org/jclouds/docker/features/NetworkApiMockTest.java @@ -0,0 +1,153 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.features; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; +import java.util.Map; + +import org.jclouds.docker.DockerApi; +import org.jclouds.docker.config.DockerParserModule; +import org.jclouds.docker.domain.Network; +import org.jclouds.docker.internal.BaseDockerMockTest; +import org.jclouds.docker.parse.NetworkParseTest; +import org.jclouds.docker.parse.NetworksParseTest; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.squareup.okhttp.mockwebserver.MockResponse; +import com.squareup.okhttp.mockwebserver.MockWebServer; + +/** + * Mock tests for the {@link NetworkApi} class. + */ +@Test(groups = "unit", testName = "NetworkApiMockTest") +public class NetworkApiMockTest extends BaseDockerMockTest { + + public void testListNetworks() throws Exception { + MockWebServer server = mockWebServer(new MockResponse().setBody(payloadFromResource("/networks.json"))); + NetworkApi api = api(DockerApi.class, server.getUrl("/").toString()).getNetworkApi(); + try { + assertEquals(api.listNetworks(), new NetworksParseTest().expected()); + assertSent(server, "GET", "/networks"); + } finally { + server.shutdown(); + } + } + + public void testListNonexistentNetworks() throws Exception { + MockWebServer server = mockWebServer(new MockResponse().setResponseCode(404)); + NetworkApi api = api(DockerApi.class, server.getUrl("/").toString()).getNetworkApi(); + try { + assertEquals(api.listNetworks(), ImmutableList.of()); + assertSent(server, "GET", "/networks"); + } finally { + server.shutdown(); + } + } + + public void testGetNetwork() throws Exception { + MockWebServer server = mockWebServer(new MockResponse().setBody(payloadFromResource("/network.json"))); + NetworkApi api = api(DockerApi.class, server.getUrl("/").toString(), new DockerParserModule()).getNetworkApi(); + String networkId = "b03d4cd15b76f8876110615cdeed15eadf77c9beb408d62f1687dcc69192cd6d"; + try { + assertEquals(api.inspectNetwork(networkId), new NetworkParseTest().expected()); + assertSent(server, "GET", "/networks/" + networkId); + } finally { + server.shutdown(); + } + } + + public void testCreateNetwork() throws Exception { + MockWebServer server = mockWebServer(new MockResponse().setBody(payloadFromResource("/network-creation.json"))); + NetworkApi api = api(DockerApi.class, server.getUrl("/").toString()).getNetworkApi(); + + Map options = ImmutableMap. builder() + .put("com.docker.network.bridge.default_bridge", "true") + .put("com.docker.network.bridge.enable_icc", "true") + .put("com.docker.network.bridge.enable_ip_masquerade", "true") + .put("com.docker.network.bridge.host_binding_ipv4", "0.0.0.0") + .put("com.docker.network.bridge.name", "docker0") + .put("com.docker.network.driver.mtu", "1500") + .build(); + + Network network = Network.create( + "isolated_nw", // Name + null, // Id + "bridge", // Driver + null, // Scope + Network.IPAM.create( + "default", // driver + ImmutableList.of(Network.IPAM.Config.create("172.17.0.0/16", null, null)) // config + ), + ImmutableMap.of("39b69226f9d79f5634485fb236a23b2fe4e96a0a94128390a7fbbcc167065867", + Network.Details.create( + "ed2419a97c1d9954d05b46e462e7002ea552f216e9b136b80a7db8d98b442eda", //endpointId + "02:42:ac:11:00:02", // MAC + "172.17.0.2/16", // ipv4address + "" // ipv6address + ) + ), + options); + + try { + Network created = api.createNetwork(network); + assertNotNull(created); + assertThat(created.id()).isEqualTo("22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30"); + assertSent(server, "POST", "/networks/create", "{\"Name\":\"isolated_nw\",\"Scope\":\"bridge\",\"IPAM\":{\"Driver\":\"default\",\"Config\":[{\"Subnet\":\"172.17.0.0/16\"}]},\"Containers\":{\"39b69226f9d79f5634485fb236a23b2fe4e96a0a94128390a7fbbcc167065867\":{\"EndpointID\":\"ed2419a97c1d9954d05b46e462e7002ea552f216e9b136b80a7db8d98b442eda\",\"MacAddress\":\"02:42:ac:11:00:02\",\"IPv4Address\":\"172.17.0.2/16\",\"IPv6Address\":\"\"}},\"Options\":{\"com.docker.network.bridge.default_bridge\":\"true\",\"com.docker.network.bridge.enable_icc\":\"true\",\"com.docker.network.bridge.enable_ip_masquerade\":\"true\",\"com.docker.network.bridge.host_binding_ipv4\":\"0.0.0.0\",\"com.docker.network.bridge.name\":\"docker0\",\"com.docker.network.driver.mtu\":\"1500\"}}"); + } finally { + server.shutdown(); + } + } + + public void testRemoveNetwork() throws Exception { + MockWebServer server = mockWebServer(new MockResponse().setResponseCode(204)); + NetworkApi api = api(DockerApi.class, server.getUrl("/").toString()).getNetworkApi(); + String networkId = "6d35806c1bd2b25cd92bba2d2c2c5169dc2156f53ab45c2b62d76e2d2fee14a9"; + try { + api.removeNetwork(networkId); + assertSent(server, "DELETE", "/networks/" + networkId); + } finally { + server.shutdown(); + } + } + + public void testConnectContainerToNetwork() throws Exception { + MockWebServer server = mockWebServer(new MockResponse().setResponseCode(200)); + NetworkApi api = api(DockerApi.class, server.getUrl("/").toString()).getNetworkApi(); + try { + api.connectContainerToNetwork("123456789", "containerName"); + assertSent(server, "POST", "/networks/123456789/connect", "{ \"Container\": \"containerName\" }"); + } finally { + server.shutdown(); + } + } + + public void testDisconnectContainerFromNetwork() throws Exception { + MockWebServer server = mockWebServer(new MockResponse().setResponseCode(200)); + NetworkApi api = api(DockerApi.class, server.getUrl("/").toString()).getNetworkApi(); + try { + api.disconnectContainerFromNetwork("123456789", "containerName"); + assertSent(server, "POST", "/networks/123456789/disconnect", "{ \"Container\": \"containerName\" }"); + } finally { + server.shutdown(); + } + } + +} diff --git a/apis/docker/src/test/java/org/jclouds/docker/internal/BaseDockerMockTest.java b/apis/docker/src/test/java/org/jclouds/docker/internal/BaseDockerMockTest.java index 994519ece3..f04e94f035 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/internal/BaseDockerMockTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/internal/BaseDockerMockTest.java @@ -16,8 +16,11 @@ */ package org.jclouds.docker.internal; +import static com.google.common.base.Charsets.UTF_8; +import static javax.ws.rs.core.MediaType.APPLICATION_JSON; import static org.assertj.core.api.Assertions.assertThat; import static org.jclouds.util.Strings2.toStringAndClose; +import static org.testng.Assert.assertEquals; import java.io.IOException; import java.util.Properties; @@ -29,6 +32,7 @@ import org.jclouds.http.okhttp.config.OkHttpCommandExecutorServiceModule; import com.google.common.base.Charsets; import com.google.common.base.Throwables; +import com.google.gson.JsonParser; import com.google.inject.Module; import com.squareup.okhttp.mockwebserver.MockWebServer; import com.squareup.okhttp.mockwebserver.RecordedRequest; @@ -66,4 +70,15 @@ public class BaseDockerMockTest extends BaseMockWebServerTest { return request; } + protected RecordedRequest assertSent(MockWebServer server, String method, String path, String json) + throws InterruptedException { + RecordedRequest request = assertSent(server, method, path); + assertEquals(request.getHeader("Content-Type"), APPLICATION_JSON); + assertEquals(parser.parse(new String(request.getBody(), UTF_8)), parser.parse(json)); + return request; + } + + /** So that we can ignore formatting. */ + private final JsonParser parser = new JsonParser(); + } diff --git a/apis/docker/src/test/java/org/jclouds/docker/parse/ContainerParseTest.java b/apis/docker/src/test/java/org/jclouds/docker/parse/ContainerParseTest.java index ff2e2fe058..42e94be7a6 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/parse/ContainerParseTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/parse/ContainerParseTest.java @@ -63,17 +63,46 @@ public class ContainerParseTest extends BaseDockerParseTest { .workingDir("/home/weave") .exposedPorts(ImmutableMap.of("6783/tcp", ImmutableMap.of(), "6783/udp", ImmutableMap.of())) .build()) - .state(State.create(3939, true, 0, "2014-10-31T17:00:21.802008706Z", "0001-01-01T00:00:00Z", false, false)) + .state(State.create(10357, true, 0, "2015-11-10T09:33:21.68146124Z", "0001-01-01T00:00:00Z", false, false, "running", false, false, "")) .image("57e570db16baba1e8c0d6f3c15868ddb400f64ff76ec948e65c3ca3f15fb3587") .networkSettings(NetworkSettings.builder() - .ipAddress("172.17.0.7") + .sandboxId("3ef128b055eb9ef62a6a2c281d97a2dfde5f47947d490f1dd2a81612611d961f") + .hairpinMode(false) + .linkLocalIPv6Address("") + .linkLocalIPv6PrefixLen(0) + .globalIPv6Address("") + .globalIPv6PrefixLen(0) + .ipv6Gateway("") + .sandboxKey("/var/run/docker/netns/3ef128b055eb") + .endpointId("9e8dcc0c8288938a923018fee0728cee8e6de7c01a5150738ee6e51c1caf8cf6") + .ipAddress("172.17.0.2") .ipPrefixLen(16) - .gateway("172.17.42.1") - .bridge("docker0") - .ports(ImmutableMap.>>of( - "6783/tcp", ImmutableList.>of(ImmutableMap.of("HostIp", "0.0.0.0", "HostPort", "6783")), - "6783/udp", ImmutableList.>of(ImmutableMap.of("HostIp", "0.0.0.0", "HostPort", "6783"))) - ) + .gateway("172.17.0.1") + .bridge("") + .ports(ImmutableMap.>>of()) + .macAddress("02:42:ac:11:00:02") + .networks(ImmutableMap.of( + "JCLOUDS_NETWORK", NetworkSettings.Details.create( + "04268fbb4dc368b5a53bb1c3f89294a4f0c72095deb944db3c4efc6d6a439304", + "172.19.0.1", + "172.19.0.2", + 16, + "", + "", + 0, + "02:42:ac:13:00:02" + ), + "bridge", NetworkSettings.Details.create( + "9e8dcc0c8288938a923018fee0728cee8e6de7c01a5150738ee6e51c1caf8cf6", + "172.17.0.1", + "172.17.0.2", + 16, + "", + "", + 0, + "02:42:ac:11:00:02" + ) + )) .build()) .resolvConfPath("/var/lib/docker/containers/6c9932f478bd761f32ddb54ed28ab42ab6fac6f2a279f561ea31503ee9d39524/resolv.conf") .hostConfig(HostConfig.builder() diff --git a/apis/docker/src/test/java/org/jclouds/docker/parse/NetworkParseTest.java b/apis/docker/src/test/java/org/jclouds/docker/parse/NetworkParseTest.java new file mode 100644 index 0000000000..9b4815c6e5 --- /dev/null +++ b/apis/docker/src/test/java/org/jclouds/docker/parse/NetworkParseTest.java @@ -0,0 +1,71 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.parse; + +import java.util.Map; + +import javax.ws.rs.Consumes; +import javax.ws.rs.core.MediaType; + +import org.jclouds.docker.domain.Network; +import org.jclouds.docker.internal.BaseDockerParseTest; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; + +@Test(groups = "unit") +public class NetworkParseTest extends BaseDockerParseTest { + + @Override + public String resource() { + return "/network.json"; + } + + @Override + @Consumes(MediaType.APPLICATION_JSON) + public Network expected() { + + Map options = ImmutableMap. builder() + .put("com.docker.network.bridge.default_bridge", "true") + .put("com.docker.network.bridge.enable_icc", "true") + .put("com.docker.network.bridge.enable_ip_masquerade", "true") + .put("com.docker.network.bridge.host_binding_ipv4", "0.0.0.0") + .put("com.docker.network.bridge.name", "docker0") + .put("com.docker.network.driver.mtu", "1500") + .build(); + + return Network.create( + "bridge", // Name + "f2de39df4171b0dc801e8002d1d999b77256983dfc63041c0f34030aa3977566", // Id + "local", // Scope + "bridge", // Driver + Network.IPAM.create( + "default", // driver + ImmutableList.of(Network.IPAM.Config.create("172.17.0.0/16", null, null)) // config + ), + ImmutableMap.of("39b69226f9d79f5634485fb236a23b2fe4e96a0a94128390a7fbbcc167065867", + Network.Details.create( + "ed2419a97c1d9954d05b46e462e7002ea552f216e9b136b80a7db8d98b442eda", //endpointId + "02:42:ac:11:00:02", // MAC + "172.17.0.2/16", // ipv4address + "" // ipv6address + ) + ), + options); + } +} diff --git a/apis/docker/src/test/java/org/jclouds/docker/parse/NetworksParseTest.java b/apis/docker/src/test/java/org/jclouds/docker/parse/NetworksParseTest.java new file mode 100644 index 0000000000..d9053f8789 --- /dev/null +++ b/apis/docker/src/test/java/org/jclouds/docker/parse/NetworksParseTest.java @@ -0,0 +1,97 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.parse; + +import java.util.List; + +import javax.ws.rs.Consumes; +import javax.ws.rs.core.MediaType; + +import org.jclouds.docker.domain.Network; +import org.jclouds.docker.internal.BaseDockerParseTest; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; + +@Test(groups = "unit") +public class NetworksParseTest extends BaseDockerParseTest> { + + @Override + public String resource() { + return "/networks.json"; + } + + @Override + @Consumes(MediaType.APPLICATION_JSON) + public List expected() { + return ImmutableList.of( + + Network.create( + "bridge", // Name + "f2de39df4171b0dc801e8002d1d999b77256983dfc63041c0f34030aa3977566", // Id + "local", // Scope + "bridge", // Driver + Network.IPAM.create( + "default", // driver + ImmutableList.of(Network.IPAM.Config.create("172.17.0.0/16", null, null)) // config + ), + ImmutableMap.of("39b69226f9d79f5634485fb236a23b2fe4e96a0a94128390a7fbbcc167065867", + Network.Details.create( + "ed2419a97c1d9954d05b46e462e7002ea552f216e9b136b80a7db8d98b442eda", //endpointId + "02:42:ac:11:00:02", // MAC + "172.17.0.2/16", // ipv4address + "" // ipv6address + ) + ), + ImmutableMap. builder() + .put("com.docker.network.bridge.default_bridge", "true") + .put("com.docker.network.bridge.enable_icc", "true") + .put("com.docker.network.bridge.enable_ip_masquerade", "true") + .put("com.docker.network.bridge.host_binding_ipv4", "0.0.0.0") + .put("com.docker.network.bridge.name", "docker0") + .put("com.docker.network.driver.mtu", "1500") + .build() + ), + Network.create( + "none", // Name + "e086a3893b05ab69242d3c44e49483a3bbbd3a26b46baa8f61ab797c1088d794", // Id + "local", // Scope + "null", // Driver + Network.IPAM.create( + "default", // driver + ImmutableList.of() // config + ), + ImmutableMap. of(), + ImmutableMap. of() + ), + Network.create( + "host", // Name + "13e871235c677f196c4e1ecebb9dc733b9b2d2ab589e30c539efeda84a24215e", // Id + "local", // Scope + "host", // Driver + Network.IPAM.create( + "default", // driver + ImmutableList.of() // config + ), + ImmutableMap. of(), + ImmutableMap. of() + ) + ); + } + +} diff --git a/apis/docker/src/test/resources/container.json b/apis/docker/src/test/resources/container.json index 5a13e13fda..fa1b61fd86 100644 --- a/apis/docker/src/test/resources/container.json +++ b/apis/docker/src/test/resources/container.json @@ -1,129 +1,155 @@ { - "Args": [ - "-iface", - "ethwe", - "-wait", - "5", - "-name", - "7a:63:a2:39:7b:0f" + "Args": [ + "-iface", + "ethwe", + "-wait", + "5", + "-name", + "7a:63:a2:39:7b:0f" + ], + "Config": { + "AttachStderr": false, + "AttachStdin": false, + "AttachStdout": false, + "Cmd": [ + "-name", + "7a:63:a2:39:7b:0f" ], - "Config": { - "AttachStderr": false, - "AttachStdin": false, - "AttachStdout": false, - "Cmd": [ - "-name", - "7a:63:a2:39:7b:0f" - ], - "CpuShares": 0, - "Cpuset": "", - "Domainname": "", - "Entrypoint": [ - "/home/weave/weaver", - "-iface", - "ethwe", - "-wait", - "5" - ], - "Env": [ - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" - ], - "ExposedPorts": { - "6783/tcp": {}, - "6783/udp": {} - }, - "Hostname": "6c9932f478bd", - "Image": "zettio/weave", - "Memory": 0, - "MemorySwap": 0, - "NetworkDisabled": false, - "OnBuild": null, - "OpenStdin": false, - "PortSpecs": null, - "SecurityOpt": null, - "StdinOnce": false, - "Tty": false, - "User": "", - "Volumes": null, - "WorkingDir": "/home/weave" + "CpuShares": 0, + "Cpuset": "", + "Domainname": "", + "Entrypoint": [ + "/home/weave/weaver", + "-iface", + "ethwe", + "-wait", + "5" + ], + "Env": [ + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + ], + "ExposedPorts": { + "6783/tcp": {}, + "6783/udp": {} }, - "Created": "2014-10-31T17:00:21.544197943Z", - "Driver": "aufs", - "ExecDriver": "native-0.2", - "HostConfig": { - "Binds": null, - "CapAdd": null, - "CapDrop": null, - "ContainerIDFile": "", - "Devices": [], - "Dns": [ "8.8.8.8", "8.8.4.4" ], - "DnsSearch": null, - "ExtraHosts": [ "extra:169.254.0.1" ], - "Links": null, - "LxcConf": [], - "NetworkMode": "bridge", - "PortBindings": { - "6783/tcp": [ - { - "HostIp": "", - "HostPort": "6783" - } - ], - "6783/udp": [ - { - "HostIp": "", - "HostPort": "6783" - } - ] - }, - "Privileged": true, - "PublishAllPorts": false, - "RestartPolicy": { - "MaximumRetryCount": 0, - "Name": "" - }, - "VolumesFrom": null - }, - "HostnamePath": "/var/lib/docker/containers/6c9932f478bd761f32ddb54ed28ab42ab6fac6f2a279f561ea31503ee9d39524/hostname", - "HostsPath": "/var/lib/docker/containers/6c9932f478bd761f32ddb54ed28ab42ab6fac6f2a279f561ea31503ee9d39524/hosts", - "Id": "6c9932f478bd761f32ddb54ed28ab42ab6fac6f2a279f561ea31503ee9d39524", - "Image": "57e570db16baba1e8c0d6f3c15868ddb400f64ff76ec948e65c3ca3f15fb3587", - "MountLabel": "", - "Name": "/weave", - "NetworkSettings": { - "Bridge": "docker0", - "Gateway": "172.17.42.1", - "IPAddress": "172.17.0.7", - "IPPrefixLen": 16, - "MacAddress": "02:42:ac:11:00:07", - "PortMapping": null, - "Ports": { - "6783/tcp": [ - { - "HostIp": "0.0.0.0", - "HostPort": "6783" - } - ], - "6783/udp": [ - { - "HostIp": "0.0.0.0", - "HostPort": "6783" - } - ] + "Hostname": "6c9932f478bd", + "Image": "zettio/weave", + "Memory": 0, + "MemorySwap": 0, + "NetworkDisabled": false, + "OnBuild": null, + "OpenStdin": false, + "PortSpecs": null, + "SecurityOpt": null, + "StdinOnce": false, + "Tty": false, + "User": "", + "Volumes": null, + "WorkingDir": "/home/weave" + }, + "Created": "2014-10-31T17:00:21.544197943Z", + "Driver": "aufs", + "ExecDriver": "native-0.2", + "HostConfig": { + "Binds": null, + "CapAdd": null, + "CapDrop": null, + "ContainerIDFile": "", + "Devices": [], + "Dns": [ + "8.8.8.8", + "8.8.4.4" + ], + "DnsSearch": null, + "ExtraHosts": ["extra:169.254.0.1"], + "Links": null, + "LxcConf": [], + "NetworkMode": "bridge", + "PortBindings": { + "6783/tcp": [ + { + "HostIp": "", + "HostPort": "6783" } + ], + "6783/udp": [ + { + "HostIp": "", + "HostPort": "6783" + } + ] }, - "Path": "/home/weave/weaver", - "ProcessLabel": "", - "ResolvConfPath": "/var/lib/docker/containers/6c9932f478bd761f32ddb54ed28ab42ab6fac6f2a279f561ea31503ee9d39524/resolv.conf", - "State": { - "ExitCode": 0, - "FinishedAt": "0001-01-01T00:00:00Z", - "Paused": false, - "Pid": 3939, - "Restarting": false, - "Running": true, - "StartedAt": "2014-10-31T17:00:21.802008706Z" + "Privileged": true, + "PublishAllPorts": false, + "RestartPolicy": { + "MaximumRetryCount": 0, + "Name": "" }, - "Volumes": {}, - "VolumesRW": {} + "VolumesFrom": null + }, + "HostnamePath": "/var/lib/docker/containers/6c9932f478bd761f32ddb54ed28ab42ab6fac6f2a279f561ea31503ee9d39524/hostname", + "HostsPath": "/var/lib/docker/containers/6c9932f478bd761f32ddb54ed28ab42ab6fac6f2a279f561ea31503ee9d39524/hosts", + "Id": "6c9932f478bd761f32ddb54ed28ab42ab6fac6f2a279f561ea31503ee9d39524", + "Image": "57e570db16baba1e8c0d6f3c15868ddb400f64ff76ec948e65c3ca3f15fb3587", + "MountLabel": "", + "Name": "/weave", + "NetworkSettings": { + "Bridge": "", + "SandboxID": "3ef128b055eb9ef62a6a2c281d97a2dfde5f47947d490f1dd2a81612611d961f", + "HairpinMode": false, + "LinkLocalIPv6Address": "", + "LinkLocalIPv6PrefixLen": 0, + "Ports": {}, + "SandboxKey": "/var/run/docker/netns/3ef128b055eb", + "SecondaryIPAddresses": null, + "SecondaryIPv6Addresses": null, + "EndpointID": "9e8dcc0c8288938a923018fee0728cee8e6de7c01a5150738ee6e51c1caf8cf6", + "Gateway": "172.17.0.1", + "GlobalIPv6Address": "", + "GlobalIPv6PrefixLen": 0, + "IPAddress": "172.17.0.2", + "IPPrefixLen": 16, + "IPv6Gateway": "", + "MacAddress": "02:42:ac:11:00:02", + "Networks": { + "JCLOUDS_NETWORK": { + "EndpointID": "04268fbb4dc368b5a53bb1c3f89294a4f0c72095deb944db3c4efc6d6a439304", + "Gateway": "172.19.0.1", + "IPAddress": "172.19.0.2", + "IPPrefixLen": 16, + "IPv6Gateway": "", + "GlobalIPv6Address": "", + "GlobalIPv6PrefixLen": 0, + "MacAddress": "02:42:ac:13:00:02" + }, + "bridge": { + "EndpointID": "9e8dcc0c8288938a923018fee0728cee8e6de7c01a5150738ee6e51c1caf8cf6", + "Gateway": "172.17.0.1", + "IPAddress": "172.17.0.2", + "IPPrefixLen": 16, + "IPv6Gateway": "", + "GlobalIPv6Address": "", + "GlobalIPv6PrefixLen": 0, + "MacAddress": "02:42:ac:11:00:02" + } + } + }, + "Path": "/home/weave/weaver", + "ProcessLabel": "", + "ResolvConfPath": "/var/lib/docker/containers/6c9932f478bd761f32ddb54ed28ab42ab6fac6f2a279f561ea31503ee9d39524/resolv.conf", + "State": { + "Status": "running", + "Running": true, + "Paused": false, + "Restarting": false, + "OOMKilled": false, + "Dead": false, + "Pid": 10357, + "ExitCode": 0, + "Error": "", + "StartedAt": "2015-11-10T09:33:21.68146124Z", + "FinishedAt": "0001-01-01T00:00:00Z" + }, + "Volumes": {}, + "VolumesRW": {} } \ No newline at end of file diff --git a/apis/docker/src/test/resources/network-creation.json b/apis/docker/src/test/resources/network-creation.json new file mode 100644 index 0000000000..63b70e4eee --- /dev/null +++ b/apis/docker/src/test/resources/network-creation.json @@ -0,0 +1,4 @@ +{ + "Id": "22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30", + "Warning": "" +} \ No newline at end of file diff --git a/apis/docker/src/test/resources/network.json b/apis/docker/src/test/resources/network.json new file mode 100644 index 0000000000..1e6c4228d9 --- /dev/null +++ b/apis/docker/src/test/resources/network.json @@ -0,0 +1,30 @@ +{ + "Name": "bridge", + "Id": "f2de39df4171b0dc801e8002d1d999b77256983dfc63041c0f34030aa3977566", + "Scope": "local", + "Driver": "bridge", + "IPAM": { + "Driver": "default", + "Config": [ + { + "Subnet": "172.17.0.0/16" + } + ] + }, + "Containers": { + "39b69226f9d79f5634485fb236a23b2fe4e96a0a94128390a7fbbcc167065867": { + "EndpointID": "ed2419a97c1d9954d05b46e462e7002ea552f216e9b136b80a7db8d98b442eda", + "MacAddress": "02:42:ac:11:00:02", + "IPv4Address": "172.17.0.2/16", + "IPv6Address": "" + } + }, + "Options": { + "com.docker.network.bridge.default_bridge": "true", + "com.docker.network.bridge.enable_icc": "true", + "com.docker.network.bridge.enable_ip_masquerade": "true", + "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0", + "com.docker.network.bridge.name": "docker0", + "com.docker.network.driver.mtu": "1500" + } +} diff --git a/apis/docker/src/test/resources/networks.json b/apis/docker/src/test/resources/networks.json new file mode 100644 index 0000000000..af81f0380a --- /dev/null +++ b/apis/docker/src/test/resources/networks.json @@ -0,0 +1,56 @@ +[ + { + "Name": "bridge", + "Id": "f2de39df4171b0dc801e8002d1d999b77256983dfc63041c0f34030aa3977566", + "Scope": "local", + "Driver": "bridge", + "IPAM": { + "Driver": "default", + "Config": [ + { + "Subnet": "172.17.0.0/16" + } + ] + }, + "Containers": { + "39b69226f9d79f5634485fb236a23b2fe4e96a0a94128390a7fbbcc167065867": { + "EndpointID": "ed2419a97c1d9954d05b46e462e7002ea552f216e9b136b80a7db8d98b442eda", + "MacAddress": "02:42:ac:11:00:02", + "IPv4Address": "172.17.0.2/16", + "IPv6Address": "" + } + }, + "Options": { + "com.docker.network.bridge.default_bridge": "true", + "com.docker.network.bridge.enable_icc": "true", + "com.docker.network.bridge.enable_ip_masquerade": "true", + "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0", + "com.docker.network.bridge.name": "docker0", + "com.docker.network.driver.mtu": "1500" + } + }, + { + "Name": "none", + "Id": "e086a3893b05ab69242d3c44e49483a3bbbd3a26b46baa8f61ab797c1088d794", + "Scope": "local", + "Driver": "null", + "IPAM": { + "Driver": "default", + "Config": [] + }, + "Containers": {}, + "Options": {} + }, + { + "Name": "host", + "Id": "13e871235c677f196c4e1ecebb9dc733b9b2d2ab589e30c539efeda84a24215e", + "Scope": "local", + "Driver": "host", + "IPAM": { + "Driver": "default", + "Config": [] + }, + "Containers": {}, + "Options": {} + } +] \ No newline at end of file From 29a8474ba03b420b462566acede250e98fdb4789 Mon Sep 17 00:00:00 2001 From: Ignasi Barrera Date: Mon, 23 Nov 2015 22:30:20 +0100 Subject: [PATCH 56/81] JCLOUDS-1041: Fix NPE when deserializing Port in Docker 1.9.1 --- .../java/org/jclouds/docker/domain/Port.java | 7 ++++--- .../docker/parse/ContainersParseTest.java | 16 +++++++--------- apis/docker/src/test/resources/containers.json | 16 ++++++++++++++++ 3 files changed, 27 insertions(+), 12 deletions(-) diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/Port.java b/apis/docker/src/main/java/org/jclouds/docker/domain/Port.java index b4bc93e1e1..45fa96602d 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/Port.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/Port.java @@ -16,17 +16,18 @@ */ package org.jclouds.docker.domain; +import org.jclouds.javax.annotation.Nullable; import org.jclouds.json.SerializedNames; import com.google.auto.value.AutoValue; @AutoValue public abstract class Port { - public abstract String ip(); + @Nullable public abstract String ip(); public abstract int privatePort(); - public abstract int publicPort(); + @Nullable public abstract Integer publicPort(); public abstract String type(); @@ -34,7 +35,7 @@ public abstract class Port { } @SerializedNames({ "IP", "PrivatePort", "PublicPort", "Type" }) - public static Port create(String ip, int privatePort, int publicPort, String type) { + public static Port create(String ip, int privatePort, Integer publicPort, String type) { return new AutoValue_Port(ip, privatePort, publicPort, type); } } diff --git a/apis/docker/src/test/java/org/jclouds/docker/parse/ContainersParseTest.java b/apis/docker/src/test/java/org/jclouds/docker/parse/ContainersParseTest.java index bb3bd623be..39565bf63b 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/parse/ContainersParseTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/parse/ContainersParseTest.java @@ -39,15 +39,13 @@ public class ContainersParseTest extends BaseDockerParseTest expected() { - return ImmutableList.of( - ContainerSummary.create("6d35806c1bd2b25cd92bba2d2c2c5169dc2156f53ab45c2b62d76e2d2fee14a9", - ImmutableList.of("/hopeful_mclean"), - "1395472605", - "jclouds/ubuntu:latest", - "/usr/sbin/sshd -D", - ImmutableList.of(Port.create("0.0.0.0", 22, 49231, "tcp")), - "Up 55 seconds") - ); + return ImmutableList.of(ContainerSummary.create( + "6d35806c1bd2b25cd92bba2d2c2c5169dc2156f53ab45c2b62d76e2d2fee14a9", ImmutableList.of("/hopeful_mclean"), + "1395472605", "jclouds/ubuntu:latest", "/usr/sbin/sshd -D", + ImmutableList.of(Port.create("0.0.0.0", 22, 49231, "tcp")), "Up 55 seconds"), ContainerSummary.create( + "6d35806c1bd2b25cd92bba2d2c2c5169dc2156f53ab45c2b62d76e2d2fee14a2", ImmutableList.of("/hopeful_mclean"), + "1395472605", "jclouds/ubuntu:latest", "/usr/sbin/sshd -D", + ImmutableList.of(Port.create(null, 22, null, "tcp")), "Up 55 seconds")); } } diff --git a/apis/docker/src/test/resources/containers.json b/apis/docker/src/test/resources/containers.json index 8f789b7100..e7e7e3be1e 100644 --- a/apis/docker/src/test/resources/containers.json +++ b/apis/docker/src/test/resources/containers.json @@ -16,5 +16,21 @@ } ], "Status": "Up 55 seconds" + }, + { + "Command": "/usr/sbin/sshd -D", + "Created": 1395472605, + "Id": "6d35806c1bd2b25cd92bba2d2c2c5169dc2156f53ab45c2b62d76e2d2fee14a2", + "Image": "jclouds/ubuntu:latest", + "Names": [ + "/hopeful_mclean" + ], + "Ports": [ + { + "PrivatePort": 22, + "Type": "tcp" + } + ], + "Status": "Up 55 seconds" } ] \ No newline at end of file From b6e20822d803294b3403683793df5702443f83f2 Mon Sep 17 00:00:00 2001 From: Andrew Gaul Date: Sat, 12 Dec 2015 10:20:34 +0800 Subject: [PATCH 57/81] Remove @Nullable from methods returning primitives Found via error-prone. --- .../docker/src/main/java/org/jclouds/docker/domain/State.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/State.java b/apis/docker/src/main/java/org/jclouds/docker/domain/State.java index 54d96cff04..30ec8c136b 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/State.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/State.java @@ -41,9 +41,9 @@ public abstract class State { @Nullable public abstract String status(); - @Nullable public abstract boolean oomKilled(); + public abstract boolean oomKilled(); - @Nullable public abstract boolean dead(); + public abstract boolean dead(); @Nullable public abstract String error(); From 3f1fe271ed8c59eb136d94bc4cd199f2da60befd Mon Sep 17 00:00:00 2001 From: Josef Cacek Date: Fri, 19 Feb 2016 12:33:24 +0100 Subject: [PATCH 58/81] [JCLOUDS-1084] Docker live tests fixed and made more robust --- apis/docker/pom.xml | 3 +- .../docker/compute/BaseDockerApiLiveTest.java | 14 +++- .../DockerComputeServiceAdapterLiveTest.java | 27 +++---- .../compute/SshToCustomPortLiveTest.java | 47 ++++------- .../docker/features/ContainerApiLiveTest.java | 3 - .../docker/features/MiscApiLiveTest.java | 20 +++-- .../docker/internal/DockerTestUtils.java | 78 +++++++++++++++++++ apis/docker/src/test/resources/Dockerfile | 16 +--- 8 files changed, 133 insertions(+), 75 deletions(-) create mode 100644 apis/docker/src/test/java/org/jclouds/docker/internal/DockerTestUtils.java diff --git a/apis/docker/pom.xml b/apis/docker/pom.xml index 39009fa89d..8bbb98c037 100644 --- a/apis/docker/pom.xml +++ b/apis/docker/pom.xml @@ -38,6 +38,7 @@ ${env.DOCKER_CERT_PATH}/cert.pem ${env.DOCKER_CERT_PATH}/key.pem ${env.DOCKER_CERT_PATH}/ca.pem + ${env.DOCKER_HOST} false org.jclouds.docker*;version="${project.version}" @@ -151,7 +152,7 @@ test.docker.endpoint - ${env.DOCKER_HOST} + ${test.docker.endpoint} tcp https false diff --git a/apis/docker/src/test/java/org/jclouds/docker/compute/BaseDockerApiLiveTest.java b/apis/docker/src/test/java/org/jclouds/docker/compute/BaseDockerApiLiveTest.java index eab76ba8ec..81324ef8bb 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/compute/BaseDockerApiLiveTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/compute/BaseDockerApiLiveTest.java @@ -33,6 +33,7 @@ import org.jclouds.Constants; import org.jclouds.apis.BaseApiLiveTest; import org.jclouds.compute.config.ComputeServiceProperties; import org.jclouds.docker.DockerApi; +import org.jclouds.docker.internal.DockerTestUtils; import org.jclouds.io.Payload; import org.jclouds.io.Payloads; import org.jclouds.sshj.config.SshjSshClientModule; @@ -46,7 +47,7 @@ import com.google.inject.Module; public class BaseDockerApiLiveTest extends BaseApiLiveTest { protected static final String DEFAULT_IMAGE = "alpine"; - protected static final String DEFAULT_TAG = "3.2"; + protected static final String DEFAULT_TAG = "3.3"; protected static final String ALPINE_IMAGE_TAG = String.format("%s:%s", DEFAULT_IMAGE, DEFAULT_TAG); @@ -54,6 +55,17 @@ public class BaseDockerApiLiveTest extends BaseApiLiveTest { provider = "docker"; } + /** + * Removes Docker image if it's present on the Docker host. + * + * @param imageName + * image to be deleted (must be not-null) + * @see DockerTestUtils#removeImageIfExists(DockerApi, String) + */ + protected void removeImageIfExists(String imageName) { + DockerTestUtils.removeImageIfExists(api, imageName); + } + @Override protected Iterable setupModules() { return ImmutableSet.of(getLoggingModule(), new SshjSshClientModule()); diff --git a/apis/docker/src/test/java/org/jclouds/docker/compute/DockerComputeServiceAdapterLiveTest.java b/apis/docker/src/test/java/org/jclouds/docker/compute/DockerComputeServiceAdapterLiveTest.java index faad0ac51e..022177691f 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/compute/DockerComputeServiceAdapterLiveTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/compute/DockerComputeServiceAdapterLiveTest.java @@ -19,6 +19,7 @@ package org.jclouds.docker.compute; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertNotNull; + import java.util.Properties; import java.util.Random; @@ -32,9 +33,8 @@ 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.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; @@ -48,7 +48,7 @@ import com.google.inject.Module; public class DockerComputeServiceAdapterLiveTest extends BaseDockerApiLiveTest { private static final String SSHABLE_IMAGE = "kwart/alpine-ext"; - private static final String SSHABLE_IMAGE_TAG = "3.2-ssh"; + private static final String SSHABLE_IMAGE_TAG = "3.3-ssh"; private Image defaultImage; private DockerComputeServiceAdapter adapter; @@ -68,6 +68,14 @@ public class DockerComputeServiceAdapterLiveTest extends BaseDockerApiLiveTest { assertNotNull(defaultImage); } + @AfterClass(alwaysRun = true) + protected void tearDown() { + if (guest != null) { + adapter.destroyNode(guest.getNode().id() + ""); + } + super.tearDown(); + } + @Override protected DockerApi create(Properties props, Iterable modules) { Injector injector = newBuilder().modules(modules).overrides(props).buildInjector(); @@ -83,7 +91,7 @@ public class DockerComputeServiceAdapterLiveTest extends BaseDockerApiLiveTest { Template template = templateBuilder.imageId(defaultImage.id()).build(); DockerTemplateOptions options = template.getOptions().as(DockerTemplateOptions.class); - options.env(ImmutableList.of("ROOT_PASS=password")); + options.env(ImmutableList.of("ROOT_PASSWORD=password")); guest = adapter.createNodeWithGroupEncodedIntoName(group, name, template); assertEquals(guest.getNodeId(), guest.getNode().id()); } @@ -97,17 +105,6 @@ public class DockerComputeServiceAdapterLiveTest extends BaseDockerApiLiveTest { } } - @AfterGroups(groups = "live") - protected void tearDown() { - if (guest != null) { - adapter.destroyNode(guest.getNode().id() + ""); - } - if (defaultImage != null) { - api.getImageApi().deleteImage(defaultImage.id(), DeleteImageOptions.Builder.force(true)); - } - super.tearDown(); - } - @Override protected Iterable setupModules() { return ImmutableSet.of(getLoggingModule(), new SshjSshClientModule()); diff --git a/apis/docker/src/test/java/org/jclouds/docker/compute/SshToCustomPortLiveTest.java b/apis/docker/src/test/java/org/jclouds/docker/compute/SshToCustomPortLiveTest.java index 62e1e5e848..cb35173c87 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/compute/SshToCustomPortLiveTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/compute/SshToCustomPortLiveTest.java @@ -17,15 +17,15 @@ package org.jclouds.docker.compute; import static org.assertj.core.api.Assertions.assertThat; -import static org.jclouds.compute.options.TemplateOptions.Builder.runAsRoot; +import static org.jclouds.compute.options.RunScriptOptions.Builder.wrapInInitScript; +import static org.jclouds.docker.internal.DockerTestUtils.consumeStreamSilently; +import static org.jclouds.docker.internal.DockerTestUtils.removeImageIfExists; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNotNull; import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; import java.util.List; -import java.util.logging.Level; import org.jclouds.compute.RunNodesException; import org.jclouds.compute.domain.ExecResponse; @@ -104,8 +104,9 @@ public class SshToCustomPortLiveTest extends BaseComputeServiceContextLiveTest { @Test(dependsOnMethods = "testImageCreated") public void testCustomPortSsh() throws RunNodesException { final DockerTemplateOptions options = DockerTemplateOptions.Builder - .commands("/usr/sbin/dropbear", "-E", "-F", "-p", String.valueOf(SSH_PORT)).overrideLoginUser("root") - .overrideLoginPassword("screencast").blockOnPort(SSH_PORT, 30).networkMode("host"); + .env("SSH_PORT=" + SSH_PORT, "ROOT_PASSWORD=screencast") + .overrideLoginUser("root").overrideLoginPassword("screencast") + .blockOnPort(SSH_PORT, 30).networkMode("host"); final Template template = view.getComputeService().templateBuilder().imageId(image.id()).options(options).build(); @@ -115,8 +116,7 @@ public class SshToCustomPortLiveTest extends BaseComputeServiceContextLiveTest { .getOnlyElement(view.getComputeService().createNodesInGroup("ssh-test", 1, template)); nodeId = node.getId(); - ExecResponse response = view.getComputeService().runScriptOnNode(nodeId, "echo hello", - runAsRoot(false).wrapInInitScript(false)); + ExecResponse response = view.getComputeService().runScriptOnNode(nodeId, "sh -c 'echo hello && sleep 0.2'", wrapInInitScript(false)); assertThat(response.getOutput().trim()).endsWith("hello"); } finally { if (nodeId != null) @@ -133,8 +133,11 @@ public class SshToCustomPortLiveTest extends BaseComputeServiceContextLiveTest { @BeforeClass(groups = { "integration", "live" }) public void setupContext() { super.setupContext(); - final String tag = toTag(IMAGE_REPOSITORY, IMAGE_TAG_1); + + removeImageIfExists(api(), tag); + removeImageIfExists(api(), toTag(IMAGE_REPOSITORY, IMAGE_TAG_2)); + BuildOptions options = BuildOptions.Builder.tag(tag).verbose(false).nocache(false); InputStream buildImageStream; try { @@ -154,8 +157,8 @@ public class SshToCustomPortLiveTest extends BaseComputeServiceContextLiveTest { */ @AfterClass(alwaysRun = true) protected void tearDown() { - consumeStreamSilently(api().getImageApi().deleteImage(toTag(IMAGE_REPOSITORY, IMAGE_TAG_1))); - consumeStreamSilently(api().getImageApi().deleteImage(toTag(IMAGE_REPOSITORY, IMAGE_TAG_2))); + removeImageIfExists(api(), toTag(IMAGE_REPOSITORY, IMAGE_TAG_1)); + removeImageIfExists(api(), toTag(IMAGE_REPOSITORY, IMAGE_TAG_2)); } /** @@ -188,29 +191,6 @@ public class SshToCustomPortLiveTest extends BaseComputeServiceContextLiveTest { return view.unwrapApi(DockerApi.class); } - /** - * Read all data from given InputStream and throw away all the bits. - * - * @param is - */ - private static void consumeStreamSilently(InputStream is) { - char[] tmpBuff = new char[8 * 1024]; - // throw everything away - InputStreamReader isr = new InputStreamReader(is); - - try { - try { - while (isr.read(tmpBuff) > -1) { - // empty - } - } finally { - isr.close(); - } - } catch (IOException e) { - java.util.logging.Logger.getAnonymousLogger().log(Level.WARNING, "Error ocured during reading InputStream.", e); - } - } - /** * Concatenate repository and tag name (if provided) in Docker format. * @@ -221,4 +201,5 @@ public class SshToCustomPortLiveTest extends BaseComputeServiceContextLiveTest { private static String toTag(String repo, String tag) { return repo + (tag != null ? ":" + tag : ""); } + } diff --git a/apis/docker/src/test/java/org/jclouds/docker/features/ContainerApiLiveTest.java b/apis/docker/src/test/java/org/jclouds/docker/features/ContainerApiLiveTest.java index 295eb5b915..1b23b07b83 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/features/ContainerApiLiveTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/features/ContainerApiLiveTest.java @@ -67,9 +67,6 @@ public class ContainerApiLiveTest extends BaseDockerApiLiveTest { api.getContainerApi().removeContainer(container.id(), RemoveContainerOptions.Builder.force(true)); } } - if (image != null) { - api.getImageApi().deleteImage(ALPINE_IMAGE_TAG); - } } public void testCreateContainer() throws IOException, InterruptedException { diff --git a/apis/docker/src/test/java/org/jclouds/docker/features/MiscApiLiveTest.java b/apis/docker/src/test/java/org/jclouds/docker/features/MiscApiLiveTest.java index 95b749bd7d..c9c69dc7ef 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/features/MiscApiLiveTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/features/MiscApiLiveTest.java @@ -51,6 +51,8 @@ import com.google.common.collect.Iterables; @Test(groups = "live", testName = "MiscApiLiveTest", singleThreaded = true) public class MiscApiLiveTest extends BaseDockerApiLiveTest { + private static final String IMAGE_TEST_TAG = "jclouds-test-test-build-image"; + private static String imageId; private Container container = null; @@ -105,14 +107,19 @@ public class MiscApiLiveTest extends BaseDockerApiLiveTest { @Test public void testBuildImageFromDockerfile() throws IOException, InterruptedException, URISyntaxException { - BuildOptions options = BuildOptions.Builder.tag("jclouds-test-test-build-image").verbose(false).nocache(false); + removeImageIfExists(IMAGE_TEST_TAG); + BuildOptions options = BuildOptions.Builder.tag(IMAGE_TEST_TAG).verbose(false).nocache(true); InputStream buildImageStream = api().build(tarredDockerfile(), options); String buildStream = consumeStream(buildImageStream); - Iterable splitted = Splitter.on("\n").split(buildStream.replace("\r", "").trim()); - String lastStreamedLine = Iterables.getLast(splitted).trim(); - String rawImageId = Iterables.getLast(Splitter.on("Successfully built ").split(lastStreamedLine)); - imageId = rawImageId.substring(0, 11); - assertNotNull(imageId); + try { + Iterable splitted = Splitter.on("\n").split(buildStream.replace("\r", "").trim()); + String lastStreamedLine = Iterables.getLast(splitted).trim(); + String rawImageId = Iterables.getLast(Splitter.on("Successfully built ").split(lastStreamedLine)); + imageId = rawImageId.substring(0, 11); + assertNotNull(imageId); + } finally { + removeImageIfExists(IMAGE_TEST_TAG); + } } @Test @@ -168,7 +175,6 @@ public class MiscApiLiveTest extends BaseDockerApiLiveTest { assertEquals(execInspect.exitCode(), 2); } - private MiscApi api() { return api.getMiscApi(); } diff --git a/apis/docker/src/test/java/org/jclouds/docker/internal/DockerTestUtils.java b/apis/docker/src/test/java/org/jclouds/docker/internal/DockerTestUtils.java new file mode 100644 index 0000000000..102684af93 --- /dev/null +++ b/apis/docker/src/test/java/org/jclouds/docker/internal/DockerTestUtils.java @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.internal; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.logging.Level; + +import org.jclouds.docker.DockerApi; +import org.jclouds.docker.features.ImageApi; + +import com.google.common.base.Preconditions; + +/** + * Utility methods shared by Docker tests. + */ +public class DockerTestUtils { + + /** + * Read all data from given {@link InputStream} and throw away all the bits. + * If an {@link IOException} occurs, it's not propagated to user. The given InputStream is closed after the read. + * + * @param is InputStream instance (may be null) + */ + public static void consumeStreamSilently(InputStream is) { + if (is == null) { + return; + } + char[] tmpBuff = new char[8 * 1024]; + // throw everything away + InputStreamReader isr = new InputStreamReader(is); + try { + try { + while (isr.read(tmpBuff) > -1) { + // empty + } + } finally { + isr.close(); + } + } catch (IOException e) { + java.util.logging.Logger.getAnonymousLogger().log(Level.WARNING, "Error ocured during reading InputStream.", e); + } + } + + /** + * Removes Docker image if it's present on the Docker host. Docker Image API + * is used to inspect and remove image (({@link ImageApi#deleteImage(String)} + * method). + * + * @param dockerApi + * DockerApi instance (must be not-null) + * @param imageName + * image to be deleted (must be not-null) + */ + public static void removeImageIfExists(DockerApi dockerApi, String imageName) { + Preconditions.checkNotNull(dockerApi, "DockerApi instance has to be provided"); + Preconditions.checkNotNull(imageName, "Docker image name has to be provided"); + final ImageApi imageApi = dockerApi.getImageApi(); + if (null != imageApi.inspectImage(imageName)) { + consumeStreamSilently(imageApi.deleteImage(imageName)); + } + } +} diff --git a/apis/docker/src/test/resources/Dockerfile b/apis/docker/src/test/resources/Dockerfile index f375d33e25..0cedc5e20a 100644 --- a/apis/docker/src/test/resources/Dockerfile +++ b/apis/docker/src/test/resources/Dockerfile @@ -16,19 +16,5 @@ # -FROM alpine:3.2 +FROM kwart/alpine-ext:3.3-ssh MAINTAINER JClouds Dev - -ENV DROPBEAR_CONF=/etc/dropbear - -RUN apk add --update dropbear \ - && mkdir -p ${DROPBEAR_CONF} \ - && dropbearkey -t dss -f ${DROPBEAR_CONF}/dropbear_dss_host_key \ - && dropbearkey -t rsa -f ${DROPBEAR_CONF}/dropbear_rsa_host_key -s 2048 \ - && dropbearkey -t ecdsa -f ${DROPBEAR_CONF}/dropbear_ecdsa_host_key - -RUN echo 'root:screencast' | chpasswd - -EXPOSE 22 - -CMD ["/usr/sbin/dropbear", "-E", "-F"] From 831cdc67c3977e834bac3c838df0ce2b970a7c9d Mon Sep 17 00:00:00 2001 From: Josef Cacek Date: Thu, 18 Feb 2016 16:02:37 +0100 Subject: [PATCH 59/81] [JCLOUDS-1002] provide access to Docker container Config object in the Node template options --- .../options/DockerTemplateOptions.java | 92 ++++++++++-- .../strategy/DockerComputeServiceAdapter.java | 131 ++++++++++-------- .../compute/SshToCustomPortLiveTest.java | 30 ++++ .../options/DockerTemplateOptionsTest.java | 18 +++ 4 files changed, 202 insertions(+), 69 deletions(-) diff --git a/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java b/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java index 4088a547c9..4239661e8d 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java +++ b/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java @@ -22,8 +22,8 @@ import static com.google.common.base.Preconditions.checkNotNull; import java.util.List; import java.util.Map; -import org.jclouds.compute.ComputeService; import org.jclouds.compute.options.TemplateOptions; +import org.jclouds.docker.domain.Config; import org.jclouds.docker.internal.NullSafeCopies; import org.jclouds.domain.LoginCredentials; import org.jclouds.javax.annotation.Nullable; @@ -34,23 +34,55 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; /** - * Contains options supported by the {@link ComputeService#createNodesInGroup(String, int, TemplateOptions) createNodes} - * operation on the docker provider. - * + * Contains options supported by the + * {@link org.jclouds.compute.ComputeService#createNodesInGroup(String, int, TemplateOptions) + * createNodes} operation on the docker provider. + * *

Usage

* - * The recommended way to instantiate a - * DockerTemplateOptions object is to statically import {@code DockerTemplateOptions.Builder.*} - * and invoke one of the static creation methods, followed by an instance mutator if needed. + * The recommended way to instantiate a DockerTemplateOptions object is to + * statically import {@code DockerTemplateOptions.Builder.*} and invoke one of + * the static creation methods, followed by an instance mutator if needed. * - *
{@code import static org.jclouds.docker.compute.options.DockerTemplateOptions.Builder.*;
+ * 
+ * {@code import static org.jclouds.docker.compute.options.DockerTemplateOptions.Builder.*;
  *
  * ComputeService api = // get connection
  * templateBuilder.options(inboundPorts(22, 80, 8080, 443));
- * Set set = api.createNodesInGroup(tag, 2, templateBuilder.build());}
+ * Set set = api.createNodesInGroup(tag, 2, templateBuilder.build());} + *
+ * + *

Advanced Usage

+ *

+ * In addition to basic configuration through its methods, this class also + * provides possibility to work directly with Docker API configuration object ( + * {@link Config.Builder}). When the + * {@link #configBuilder(org.jclouds.docker.domain.Config.Builder)} is used to + * configure not-null configBuilder, then this configuration object + * takes precedence over the other configuration in this class (i.e. the other + * config entries are not used) + *

+ *

+ * Note: The {@code image} property in the provided {@link Config.Builder} is rewritten by a placeholder value. + * The real value is configured by ComputeServiceAdapter. + *

+ * + *
+ * {@code import static org.jclouds.docker.compute.options.DockerTemplateOptions.Builder.*;
+ *
+ * ComputeService api = // get connection
+ * DockerTemplateOptions options = DockerTemplateOptions.Builder
+ *       .configBuilder(
+ *                Config.builder().env(ImmutableList. of("SSH_PORT=8822"))
+ *                      .hostConfig(HostConfig.builder().networkMode("host").build()));
+ * templateBuilder.options(options);
+ * Set set = api.createNodesInGroup("sample-group", 1, templateBuilder.build());}
+ * 
*/ public class DockerTemplateOptions extends TemplateOptions implements Cloneable { + private static final String NO_IMAGE = "jclouds-placeholder-for-image"; + protected List dns = ImmutableList.of(); protected String hostname; protected Integer memory; @@ -63,6 +95,8 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable protected String networkMode; protected Map extraHosts = ImmutableMap.of(); + protected Config.Builder configBuilder; + @Override public DockerTemplateOptions clone() { DockerTemplateOptions options = new DockerTemplateOptions(); @@ -94,6 +128,7 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable if (!extraHosts.isEmpty()) { eTo.extraHosts(extraHosts); } + eTo.configBuilder(configBuilder); } } @@ -113,12 +148,22 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable equal(this.cpuShares, that.cpuShares) && equal(this.env, that.env) && equal(this.portBindings, that.portBindings) && - equal(this.extraHosts, that.extraHosts); + equal(this.extraHosts, that.extraHosts) && + buildersEqual(this.configBuilder, that.configBuilder); } + + /** + * Compares two Config.Builder instances. + */ + private boolean buildersEqual(Config.Builder b1, Config.Builder b2) { + return b1 == b2 || (b1 != null && b2 != null && b1.build().equals(b2.build())); + } + @Override public int hashCode() { - return Objects.hashCode(super.hashCode(), volumes, hostname, dns, memory, entrypoint, commands, cpuShares, env, portBindings, extraHosts); + return Objects.hashCode(super.hashCode(), volumes, hostname, dns, memory, entrypoint, commands, cpuShares, env, + portBindings, extraHosts, configBuilder); } @Override @@ -134,6 +179,7 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable .add("env", env) .add("portBindings", portBindings) .add("extraHosts", extraHosts) + .add("configBuilder", configBuilder) .toString(); } @@ -235,6 +281,23 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable return this; } + /** + * This method sets Config.Builder configuration object, which can be used as + * a replacement for all the other settings from this class. Some values in + * the provided Config.Builder instance (the image name for instance) can be + * ignored or their value can be changed. + * + * @param configBuilder + * Config.Builder instance. This instance can be changed in this + * method! + */ + public DockerTemplateOptions configBuilder(Config.Builder configBuilder) { + this.configBuilder = configBuilder != null + ? Config.builder().fromConfig(configBuilder.image(NO_IMAGE).build()) + : null; + return this; + } + public Map getVolumes() { return volumes; } public List getDns() { return dns; } @@ -257,6 +320,8 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable public Map getExtraHosts() { return extraHosts; } + public Config.Builder getConfigBuilder() { return configBuilder; } + public static class Builder { /** @@ -379,6 +444,11 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable return options.extraHosts(extraHosts); } + public static DockerTemplateOptions configBuilder(Config.Builder configBuilder) { + DockerTemplateOptions options = new DockerTemplateOptions(); + return options.configBuilder(configBuilder); + } + /** * @see TemplateOptions#inboundPorts(int...) */ diff --git a/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java b/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java index d800b7e24c..64e9e679ea 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java +++ b/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java @@ -32,6 +32,7 @@ import org.jclouds.compute.domain.Hardware; import org.jclouds.compute.domain.HardwareBuilder; import org.jclouds.compute.domain.Processor; import org.jclouds.compute.domain.Template; +import org.jclouds.compute.options.TemplateOptions; import org.jclouds.compute.reference.ComputeServiceConstants; import org.jclouds.docker.DockerApi; import org.jclouds.docker.compute.options.DockerTemplateOptions; @@ -74,83 +75,97 @@ public class DockerComputeServiceAdapter implements this.api = checkNotNull(api, "api"); } + @SuppressWarnings({ "rawtypes", "unchecked" }) @Override public NodeAndInitialCredentials createNodeWithGroupEncodedIntoName(String group, String name, Template template) { checkNotNull(template, "template was null"); - checkNotNull(template.getOptions(), "template options was null"); + TemplateOptions options = template.getOptions(); + checkNotNull(options, "template options was null"); String imageId = checkNotNull(template.getImage().getId(), "template image id must not be null"); String loginUser = template.getImage().getDefaultCredentials().getUser(); String loginUserPassword = template.getImage().getDefaultCredentials().getOptionalPassword().or("password"); - DockerTemplateOptions templateOptions = DockerTemplateOptions.class.cast(template.getOptions()); - int[] inboundPorts = templateOptions.getInboundPorts(); + DockerTemplateOptions templateOptions = DockerTemplateOptions.class.cast(options); + Config.Builder containerConfigBuilder = templateOptions.getConfigBuilder(); + if (containerConfigBuilder == null) { + containerConfigBuilder = Config.builder(); - Map exposedPorts = Maps.newHashMap(); - for (int inboundPort : inboundPorts) { - exposedPorts.put(inboundPort + "/tcp", Maps.newHashMap()); - } + containerConfigBuilder.entrypoint(templateOptions.getEntrypoint()); + containerConfigBuilder.cmd(templateOptions.getCommands()); + containerConfigBuilder.memory(templateOptions.getMemory()); + containerConfigBuilder.hostname(templateOptions.getHostname()); + containerConfigBuilder.cpuShares(templateOptions.getCpuShares()); + containerConfigBuilder.env(templateOptions.getEnv()); - Config.Builder containerConfigBuilder = Config.builder() - .image(imageId) - .exposedPorts(exposedPorts); - - containerConfigBuilder.entrypoint(templateOptions.getEntrypoint()); - containerConfigBuilder.cmd(templateOptions.getCommands()); - containerConfigBuilder.memory(templateOptions.getMemory()); - containerConfigBuilder.hostname(templateOptions.getHostname()); - containerConfigBuilder.cpuShares(templateOptions.getCpuShares()); - containerConfigBuilder.env(templateOptions.getEnv()); - - if (!templateOptions.getVolumes().isEmpty()) { - Map volumes = Maps.newLinkedHashMap(); - for (String containerDir : templateOptions.getVolumes().values()) { - volumes.put(containerDir, Maps.newHashMap()); + if (!templateOptions.getVolumes().isEmpty()) { + Map volumes = Maps.newLinkedHashMap(); + for (String containerDir : templateOptions.getVolumes().values()) { + volumes.put(containerDir, Maps.newHashMap()); + } + containerConfigBuilder.volumes(volumes); } - containerConfigBuilder.volumes(volumes); + + HostConfig.Builder hostConfigBuilder = HostConfig.builder() + .publishAllPorts(true) + .privileged(true); + + if (!templateOptions.getPortBindings().isEmpty()) { + Map>> portBindings = Maps.newHashMap(); + for (Map.Entry entry : templateOptions.getPortBindings().entrySet()) { + portBindings.put(entry.getValue() + "/tcp", + Lists.>newArrayList(ImmutableMap.of("HostPort", Integer.toString(entry.getKey())))); + } + hostConfigBuilder.portBindings(portBindings); + } + + if (!templateOptions.getDns().isEmpty()) { + hostConfigBuilder.dns(templateOptions.getDns()); + } + + if (!templateOptions.getExtraHosts().isEmpty()) { + List extraHosts = Lists.newArrayList(); + for (Map.Entry entry : templateOptions.getExtraHosts().entrySet()) { + extraHosts.add(entry.getKey() + ":" + entry.getValue()); + } + hostConfigBuilder.extraHosts(extraHosts); + } + + if (!templateOptions.getVolumes().isEmpty()) { + for (Map.Entry entry : templateOptions.getVolumes().entrySet()) { + hostConfigBuilder.binds(ImmutableList.of(entry.getKey() + ":" + entry.getValue())); + } + } + + hostConfigBuilder.networkMode(templateOptions.getNetworkMode()); + containerConfigBuilder.hostConfig(hostConfigBuilder.build()); } - Config containerConfig = containerConfigBuilder.build(); + containerConfigBuilder.image(imageId); + // add the inbound ports into exposed ports map + Config containerConfig = containerConfigBuilder.build(); + Map exposedPorts = Maps.newHashMap(); + if (containerConfig.exposedPorts() == null) { + exposedPorts.putAll(containerConfig.exposedPorts()); + } + for (int inboundPort : templateOptions.getInboundPorts()) { + String portKey = inboundPort + "/tcp"; + if (!exposedPorts.containsKey(portKey)) { + exposedPorts.put(portKey, Maps.newHashMap()); + } + } + containerConfigBuilder.exposedPorts(exposedPorts); + + // build once more after setting inboundPorts + containerConfig = containerConfigBuilder.build(); + logger.debug(">> creating new container with containerConfig(%s)", containerConfig); Container container = api.getContainerApi().createContainer(name, containerConfig); logger.trace("<< container(%s)", container.id()); - HostConfig.Builder hostConfigBuilder = HostConfig.builder() - .publishAllPorts(true) - .privileged(true); - - if (!templateOptions.getPortBindings().isEmpty()) { - Map>> portBindings = Maps.newHashMap(); - for (Map.Entry entry : templateOptions.getPortBindings().entrySet()) { - portBindings.put(entry.getValue() + "/tcp", - Lists.>newArrayList(ImmutableMap.of("HostPort", Integer.toString(entry.getKey())))); - } - hostConfigBuilder.portBindings(portBindings); - } - - if (!templateOptions.getDns().isEmpty()) { - hostConfigBuilder.dns(templateOptions.getDns()); - } - - if (!templateOptions.getExtraHosts().isEmpty()) { - List extraHosts = Lists.newArrayList(); - for (Map.Entry entry : templateOptions.getExtraHosts().entrySet()) { - extraHosts.add(entry.getKey() + ":" + entry.getValue()); - } - hostConfigBuilder.extraHosts(extraHosts); - } - - if (!templateOptions.getVolumes().isEmpty()) { - for (Map.Entry entry : templateOptions.getVolumes().entrySet()) { - hostConfigBuilder.binds(ImmutableList.of(entry.getKey() + ":" + entry.getValue())); - } - } - - hostConfigBuilder.networkMode(templateOptions.getNetworkMode()); - - HostConfig hostConfig = hostConfigBuilder.build(); + HostConfig hostConfig = containerConfig.hostConfig(); api.getContainerApi().startContainer(container.id(), hostConfig); container = api.getContainerApi().inspectContainer(container.id()); diff --git a/apis/docker/src/test/java/org/jclouds/docker/compute/SshToCustomPortLiveTest.java b/apis/docker/src/test/java/org/jclouds/docker/compute/SshToCustomPortLiveTest.java index cb35173c87..ba11feb089 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/compute/SshToCustomPortLiveTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/compute/SshToCustomPortLiveTest.java @@ -35,7 +35,9 @@ import org.jclouds.compute.internal.BaseComputeServiceContextLiveTest; import org.jclouds.docker.DockerApi; import org.jclouds.docker.compute.functions.LoginPortForContainer; import org.jclouds.docker.compute.options.DockerTemplateOptions; +import org.jclouds.docker.domain.Config; import org.jclouds.docker.domain.Container; +import org.jclouds.docker.domain.HostConfig; import org.jclouds.docker.domain.Image; import org.jclouds.docker.domain.ImageSummary; import org.jclouds.docker.options.BuildOptions; @@ -46,6 +48,7 @@ import org.testng.annotations.Test; import com.google.common.base.Optional; import com.google.common.base.Predicate; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.inject.AbstractModule; @@ -124,6 +127,33 @@ public class SshToCustomPortLiveTest extends BaseComputeServiceContextLiveTest { } } + + @Test(dependsOnMethods = "testImageCreated") + public void testAdvancedConfig() throws RunNodesException { + final DockerTemplateOptions options = DockerTemplateOptions.Builder + .configBuilder( + Config.builder().env(ImmutableList. of("SSH_PORT=" + SSH_PORT, "ROOT_PASSWORD=jcloudsRulez")) + .hostConfig(HostConfig.builder().networkMode("host").build()) + .image("test-if-this-value-is-correctly-overriden")) + .overrideLoginUser("root").overrideLoginPassword("jcloudsRulez").blockOnPort(SSH_PORT, 30); + + final Template template = view.getComputeService().templateBuilder().imageId(image.id()).options(options).build(); + + String nodeId = null; + try { + NodeMetadata node = Iterables + .getOnlyElement(view.getComputeService().createNodesInGroup("ssh-test-advanced", 1, template)); + + nodeId = node.getId(); + ExecResponse response = view.getComputeService().runScriptOnNode(nodeId, "sh -c 'true'", + wrapInInitScript(false)); + assertEquals(response.getExitStatus(), 0); + } finally { + if (nodeId != null) + view.getComputeService().destroyNode(nodeId); + } + } + /** * Build a new image with 2 tags on it in the test preparation phase. * diff --git a/apis/docker/src/test/java/org/jclouds/docker/compute/options/DockerTemplateOptionsTest.java b/apis/docker/src/test/java/org/jclouds/docker/compute/options/DockerTemplateOptionsTest.java index a1145f5a8d..a1bb321816 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/compute/options/DockerTemplateOptionsTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/compute/options/DockerTemplateOptionsTest.java @@ -17,8 +17,11 @@ package org.jclouds.docker.compute.options; import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; import org.jclouds.compute.options.TemplateOptions; +import org.jclouds.docker.domain.Config; +import org.jclouds.docker.domain.Config.Builder; import org.testng.annotations.Test; import com.google.common.collect.ImmutableList; @@ -91,6 +94,21 @@ public class DockerTemplateOptionsTest { assertEquals(options.as(DockerTemplateOptions.class).getNetworkMode(), "host"); } + @Test + public void testConfigBuilder() { + Builder builder = Config.builder().memory(1024) + .cpuShares(100).cmd(ImmutableList. of("/opt/jboss/wildfly/bin/standalone.sh", "-b", "0.0.0.0")) + .env(ImmutableList. of("JAVA_HOME=/opt/jdk-1.8", "MGMT_USER=admin", + "MGMT_PASSWORD=Schroedinger's Cat")); + TemplateOptions options = DockerTemplateOptions.Builder.configBuilder(builder); + Builder builderInOpts = options.as(DockerTemplateOptions.class).getConfigBuilder(); + assertNotNull(builderInOpts); + Config configFromOptions = builderInOpts.build(); + assertEquals(configFromOptions, builder.build()); + assertEquals(configFromOptions.env(), ImmutableList. of("JAVA_HOME=/opt/jdk-1.8", "MGMT_USER=admin", + "MGMT_PASSWORD=Schroedinger's Cat")); + } + @Test public void testNonDockerOptions() { TemplateOptions options = DockerTemplateOptions.Builder.userMetadata(ImmutableMap.of("key", "value")).cpuShares(1); From f3ee898c1399791a5874c208e3b1ff9eb22a7cf7 Mon Sep 17 00:00:00 2001 From: Josef Cacek Date: Tue, 8 Mar 2016 15:03:40 +0100 Subject: [PATCH 60/81] [JCLOUDS-1089] Add image history support to Docker ImageApi --- .../jclouds/docker/domain/ImageHistory.java | 50 +++++++++++++++ .../org/jclouds/docker/features/ImageApi.java | 13 ++++ .../docker/features/ImageApiMockTest.java | 49 +++++++++++++++ .../docker/parse/HistoryParseTest.java | 63 +++++++++++++++++++ .../src/test/resources/history-apiver22.json | 28 +++++++++ apis/docker/src/test/resources/history.json | 32 ++++++++++ 6 files changed, 235 insertions(+) create mode 100644 apis/docker/src/main/java/org/jclouds/docker/domain/ImageHistory.java create mode 100644 apis/docker/src/test/java/org/jclouds/docker/parse/HistoryParseTest.java create mode 100644 apis/docker/src/test/resources/history-apiver22.json create mode 100644 apis/docker/src/test/resources/history.json diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/ImageHistory.java b/apis/docker/src/main/java/org/jclouds/docker/domain/ImageHistory.java new file mode 100644 index 0000000000..5916a00eca --- /dev/null +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/ImageHistory.java @@ -0,0 +1,50 @@ +/* + * 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.javax.annotation.Nullable; +import org.jclouds.json.SerializedNames; + +import com.google.auto.value.AutoValue; + +@AutoValue +public abstract class ImageHistory { + + public abstract String id(); + + public abstract long created(); + + public abstract String createdBy(); + + @Nullable public abstract List tags(); + + public abstract long size(); + + public abstract String comment(); + + + ImageHistory() { + } + + @SerializedNames({"Id", "Created", "CreatedBy", "Tags", "Size", "Comment"}) + public static ImageHistory create(String id, long created, String createdBy, List tags, long size, String comment) { + return new AutoValue_ImageHistory(id, created, createdBy, copyOf(tags), size, comment); + } +} diff --git a/apis/docker/src/main/java/org/jclouds/docker/features/ImageApi.java b/apis/docker/src/main/java/org/jclouds/docker/features/ImageApi.java index 441f1c071c..c93c322ca5 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/features/ImageApi.java +++ b/apis/docker/src/main/java/org/jclouds/docker/features/ImageApi.java @@ -32,6 +32,7 @@ import javax.ws.rs.core.MediaType; import org.jclouds.Fallbacks.EmptyListOnNotFoundOr404; import org.jclouds.Fallbacks.NullOnNotFoundOr404; import org.jclouds.docker.domain.Image; +import org.jclouds.docker.domain.ImageHistory; import org.jclouds.docker.domain.ImageSummary; import org.jclouds.docker.options.CreateImageOptions; import org.jclouds.docker.options.DeleteImageOptions; @@ -123,4 +124,16 @@ public interface ImageApi { @Path("/images/{name}/tag") void tagImage(@PathParam("name") String name, @QueryParam("repo") String repoName, @QueryParam("tag") String tag, @QueryParam("force") boolean force); + + /** + * Return the history of the image with given {@code name}. + * + * @param name + * the name of the image for which the history is retrieved + */ + @Named("image:history") + @GET + @Path("/images/{name}/history") + @Fallback(EmptyListOnNotFoundOr404.class) + List getHistory(@PathParam("name") String name); } diff --git a/apis/docker/src/test/java/org/jclouds/docker/features/ImageApiMockTest.java b/apis/docker/src/test/java/org/jclouds/docker/features/ImageApiMockTest.java index f789b6d3b1..77277ab8c2 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/features/ImageApiMockTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/features/ImageApiMockTest.java @@ -17,15 +17,22 @@ package org.jclouds.docker.features; import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; + +import java.util.List; import org.jclouds.docker.DockerApi; import org.jclouds.docker.config.DockerParserModule; +import org.jclouds.docker.domain.ImageHistory; import org.jclouds.docker.internal.BaseDockerMockTest; import org.jclouds.docker.options.CreateImageOptions; +import org.jclouds.docker.parse.HistoryParseTest; import org.jclouds.docker.parse.ImageParseTest; import org.jclouds.docker.parse.ImagesParseTest; import org.testng.annotations.Test; +import com.google.common.collect.ImmutableList; import com.squareup.okhttp.mockwebserver.MockResponse; import com.squareup.okhttp.mockwebserver.MockWebServer; @@ -93,4 +100,46 @@ public class ImageApiMockTest extends BaseDockerMockTest { } } + public void testGetHistory() throws Exception { + MockWebServer server = mockWebServer( + new MockResponse().setBody(payloadFromResource("/history.json")), + new MockResponse().setBody(payloadFromResource("/history-apiver22.json")), + new MockResponse().setResponseCode(404)); + ImageApi api = api(DockerApi.class, server.getUrl("/").toString()).getImageApi(); + try { + assertEquals(api.getHistory("ubuntu"), new HistoryParseTest().expected()); + assertSent(server, "GET", "/images/ubuntu/history"); + + // Docker Engine 1.10 (REST API ver 22) doesn't return parent layer IDs + assertEquals(api.getHistory("fcf9d588ee9ab46c5a888e67f892fac66e6396eb195a743e50c0c5f9a4710e66"), + ImmutableList.of( + ImageHistory.create("sha256:fcf9d588ee9ab46c5a888e67f892fac66e6396eb195a743e50c0c5f9a4710e66", + 1456304238, + "", + ImmutableList.of("registry.acme.com:8888/jboss-eap-test/eap-test:1.0-3"), + 188605160, + ""), + ImageHistory.create("", + 1455838658, + "", + null, + 195019519, + ""), + ImageHistory.create("", + 1455812978, + "", + null, + 203250948, + "Imported from -") + )); + assertSent(server, "GET", "/images/fcf9d588ee9ab46c5a888e67f892fac66e6396eb195a743e50c0c5f9a4710e66/history"); + + // check also if empty list is returned if the image is not found + List historyList = api.getHistory("missing-image"); + assertNotNull(historyList); + assertTrue(historyList.isEmpty()); + } finally { + server.shutdown(); + } + } } diff --git a/apis/docker/src/test/java/org/jclouds/docker/parse/HistoryParseTest.java b/apis/docker/src/test/java/org/jclouds/docker/parse/HistoryParseTest.java new file mode 100644 index 0000000000..a92a69b348 --- /dev/null +++ b/apis/docker/src/test/java/org/jclouds/docker/parse/HistoryParseTest.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.parse; + +import java.util.List; + +import javax.ws.rs.Consumes; +import javax.ws.rs.core.MediaType; + +import org.jclouds.docker.domain.ImageHistory; +import org.jclouds.docker.internal.BaseDockerParseTest; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableList; + +@Test(groups = "unit") +public class HistoryParseTest extends BaseDockerParseTest> { + + @Override + public String resource() { + return "/history.json"; + } + + @Override + @Consumes(MediaType.APPLICATION_JSON) + public List expected() { + return ImmutableList.of( + ImageHistory.create("3db9c44f45209632d6050b35958829c3a2aa256d81b9a7be45b362ff85c54710", + 1398108230, + "/bin/sh -c #(nop) ADD file:eb15dbd63394e063b805a3c32ca7bf0266ef64676d5a6fab4801f2e81e2a5148 in /", + ImmutableList.of("ubuntu:lucid", "ubuntu:10.04"), + 182964289, + ""), + ImageHistory.create("6cfa4d1f33fb861d4d114f43b25abd0ac737509268065cdfd69d544a59c85ab8", + 1398108222, + "/bin/sh -c #(nop) MAINTAINER Tianon Gravi - mkimage-debootstrap.sh -i iproute,iputils-ping,ubuntu-minimal -t lucid.tar.xz lucid http://archive.ubuntu.com/ubuntu/", + null, + 0, + ""), + ImageHistory.create("511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158", + 1371157430, + "", + ImmutableList.of("scratch12:latest", "scratch:latest"), + 0, + "Imported from -") + ); + } + +} diff --git a/apis/docker/src/test/resources/history-apiver22.json b/apis/docker/src/test/resources/history-apiver22.json new file mode 100644 index 0000000000..87e1ddd394 --- /dev/null +++ b/apis/docker/src/test/resources/history-apiver22.json @@ -0,0 +1,28 @@ +[ + { + "Comment": "", + "Created": 1456304238, + "CreatedBy": "", + "Id": "sha256:fcf9d588ee9ab46c5a888e67f892fac66e6396eb195a743e50c0c5f9a4710e66", + "Size": 188605160, + "Tags": [ + "registry.acme.com:8888/jboss-eap-test/eap-test:1.0-3" + ] + }, + { + "Comment": "", + "Created": 1455838658, + "CreatedBy": "", + "Id": "", + "Size": 195019519, + "Tags": null + }, + { + "Comment": "Imported from -", + "Created": 1455812978, + "CreatedBy": "", + "Id": "", + "Size": 203250948, + "Tags": null + } +] \ No newline at end of file diff --git a/apis/docker/src/test/resources/history.json b/apis/docker/src/test/resources/history.json new file mode 100644 index 0000000000..d1c40a89fc --- /dev/null +++ b/apis/docker/src/test/resources/history.json @@ -0,0 +1,32 @@ +[ + { + "Id": "3db9c44f45209632d6050b35958829c3a2aa256d81b9a7be45b362ff85c54710", + "Created": 1398108230, + "CreatedBy": "/bin/sh -c #(nop) ADD file:eb15dbd63394e063b805a3c32ca7bf0266ef64676d5a6fab4801f2e81e2a5148 in /", + "Tags": [ + "ubuntu:lucid", + "ubuntu:10.04" + ], + "Size": 182964289, + "Comment": "" + }, + { + "Id": "6cfa4d1f33fb861d4d114f43b25abd0ac737509268065cdfd69d544a59c85ab8", + "Created": 1398108222, + "CreatedBy": "/bin/sh -c #(nop) MAINTAINER Tianon Gravi - mkimage-debootstrap.sh -i iproute,iputils-ping,ubuntu-minimal -t lucid.tar.xz lucid http://archive.ubuntu.com/ubuntu/", + "Tags": null, + "Size": 0, + "Comment": "" + }, + { + "Id": "511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158", + "Created": 1371157430, + "CreatedBy": "", + "Tags": [ + "scratch12:latest", + "scratch:latest" + ], + "Size": 0, + "Comment": "Imported from -" + } +] \ No newline at end of file From 760995a46e9bc899e949dae0070b7a2421cf8759 Mon Sep 17 00:00:00 2001 From: Zack Shoylev Date: Fri, 18 Mar 2016 16:10:55 -0500 Subject: [PATCH 61/81] Use non-privileged mode for compute --- .../options/DockerTemplateOptions.java | 40 +++++++++++++++---- .../strategy/DockerComputeServiceAdapter.java | 4 +- .../options/DockerTemplateOptionsTest.java | 12 ++++++ 3 files changed, 47 insertions(+), 9 deletions(-) diff --git a/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java b/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java index 4239661e8d..8149a15409 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java +++ b/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java @@ -37,7 +37,7 @@ import com.google.common.collect.ImmutableMap; * Contains options supported by the * {@link org.jclouds.compute.ComputeService#createNodesInGroup(String, int, TemplateOptions) * createNodes} operation on the docker provider. - * + * *

Usage

* * The recommended way to instantiate a DockerTemplateOptions object is to @@ -51,7 +51,7 @@ import com.google.common.collect.ImmutableMap; * templateBuilder.options(inboundPorts(22, 80, 8080, 443)); * Set set = api.createNodesInGroup(tag, 2, templateBuilder.build());} * - * + * *

Advanced Usage

*

* In addition to basic configuration through its methods, this class also @@ -66,7 +66,7 @@ import com.google.common.collect.ImmutableMap; * Note: The {@code image} property in the provided {@link Config.Builder} is rewritten by a placeholder value. * The real value is configured by ComputeServiceAdapter. *

- * + * *
  * {@code import static org.jclouds.docker.compute.options.DockerTemplateOptions.Builder.*;
  *
@@ -94,9 +94,10 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable
    protected Map portBindings = ImmutableMap.of();
    protected String networkMode;
    protected Map extraHosts = ImmutableMap.of();
+   protected boolean privileged;
 
    protected Config.Builder configBuilder;
-   
+
    @Override
    public DockerTemplateOptions clone() {
       DockerTemplateOptions options = new DockerTemplateOptions();
@@ -128,6 +129,8 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable
          if (!extraHosts.isEmpty()) {
             eTo.extraHosts(extraHosts);
          }
+         eTo.privileged(privileged);
+
          eTo.configBuilder(configBuilder);
       }
    }
@@ -149,6 +152,7 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable
               equal(this.env, that.env) &&
               equal(this.portBindings, that.portBindings) &&
               equal(this.extraHosts, that.extraHosts) &&
+              equal(this.privileged, that.privileged) &&
               buildersEqual(this.configBuilder, that.configBuilder);
    }
 
@@ -159,7 +163,7 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable
    private boolean buildersEqual(Config.Builder b1, Config.Builder b2) {
       return b1 == b2 || (b1 != null && b2 != null && b1.build().equals(b2.build()));
    }
-   
+
    @Override
    public int hashCode() {
       return Objects.hashCode(super.hashCode(), volumes, hostname, dns, memory, entrypoint, commands, cpuShares, env,
@@ -281,6 +285,18 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable
       return this;
    }
 
+   /**
+    * By default, Docker containers are unprivileged and cannot execute privileged operations or access certain
+    * host devices.
+    *
+    * @param privileged Whether the container should run in privileged mode or not
+    * @return this instance
+    */
+   public DockerTemplateOptions privileged(boolean privileged) {
+      this.privileged = privileged;
+      return this;
+   }
+
    /**
     * This method sets Config.Builder configuration object, which can be used as
     * a replacement for all the other settings from this class. Some values in
@@ -292,7 +308,7 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable
     *           method!
     */
    public DockerTemplateOptions configBuilder(Config.Builder configBuilder) {
-      this.configBuilder = configBuilder != null 
+      this.configBuilder = configBuilder != null
             ? Config.builder().fromConfig(configBuilder.image(NO_IMAGE).build())
             : null;
       return this;
@@ -320,6 +336,8 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable
 
    public Map getExtraHosts() { return extraHosts; }
 
+   public boolean getPrivileged() { return privileged; }
+
    public Config.Builder getConfigBuilder() { return configBuilder; }
 
    public static class Builder {
@@ -444,11 +462,19 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable
          return options.extraHosts(extraHosts);
       }
 
+      /**
+       * @see DockerTemplateOptions#privileged(boolean)
+       */
+      public static DockerTemplateOptions privileged(boolean privileged) {
+         DockerTemplateOptions options = new DockerTemplateOptions();
+         return options.privileged(privileged);
+      }
+
       public static DockerTemplateOptions configBuilder(Config.Builder configBuilder) {
          DockerTemplateOptions options = new DockerTemplateOptions();
          return options.configBuilder(configBuilder);
       }
-      
+
       /**
        * @see TemplateOptions#inboundPorts(int...)
        */
diff --git a/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java b/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java
index 64e9e679ea..20567cdd46 100644
--- a/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java
+++ b/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java
@@ -109,7 +109,7 @@ public class DockerComputeServiceAdapter implements
 
          HostConfig.Builder hostConfigBuilder = HostConfig.builder()
                  .publishAllPorts(true)
-                 .privileged(true);
+                 .privileged( templateOptions.getPrivileged() );
 
          if (!templateOptions.getPortBindings().isEmpty()) {
             Map>> portBindings = Maps.newHashMap();
@@ -160,7 +160,7 @@ public class DockerComputeServiceAdapter implements
 
       // build once more after setting inboundPorts
       containerConfig = containerConfigBuilder.build();
-      
+
       logger.debug(">> creating new container with containerConfig(%s)", containerConfig);
       Container container = api.getContainerApi().createContainer(name, containerConfig);
       logger.trace("<< container(%s)", container.id());
diff --git a/apis/docker/src/test/java/org/jclouds/docker/compute/options/DockerTemplateOptionsTest.java b/apis/docker/src/test/java/org/jclouds/docker/compute/options/DockerTemplateOptionsTest.java
index a1bb321816..73ba44ee1b 100644
--- a/apis/docker/src/test/java/org/jclouds/docker/compute/options/DockerTemplateOptionsTest.java
+++ b/apis/docker/src/test/java/org/jclouds/docker/compute/options/DockerTemplateOptionsTest.java
@@ -94,6 +94,18 @@ public class DockerTemplateOptionsTest {
       assertEquals(options.as(DockerTemplateOptions.class).getNetworkMode(), "host");
    }
 
+   @Test
+   public void testPrivilegedDefaultFalse() {
+      TemplateOptions options = DockerTemplateOptions.Builder.memory(2);
+      assertEquals(options.as(DockerTemplateOptions.class).getPrivileged(), false);
+   }
+
+   @Test
+   public void testPrivileged() {
+      TemplateOptions options = DockerTemplateOptions.Builder.privileged(true);
+      assertEquals(options.as(DockerTemplateOptions.class).getPrivileged(), true);
+   }
+
    @Test
    public void testConfigBuilder() {
       Builder builder = Config.builder().memory(1024)

From 047595c7bb4f7c756d8de7027348b957e6df5d60 Mon Sep 17 00:00:00 2001
From: Andrew Donald Kennedy 
Date: Fri, 1 Apr 2016 21:53:52 +0100
Subject: [PATCH 62/81] Added volumesFrom to Docker template options

---
 .../options/DockerTemplateOptions.java        | 84 ++++++++++++-------
 .../strategy/DockerComputeServiceAdapter.java | 22 +++--
 .../org/jclouds/docker/domain/Config.java     | 40 ++++-----
 .../org/jclouds/docker/domain/HostConfig.java | 53 ++++++------
 .../docker/internal/NullSafeCopies.java       | 12 +++
 .../ContainerToNodeMetadataTest.java          |  1 -
 6 files changed, 124 insertions(+), 88 deletions(-)

diff --git a/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java b/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java
index 8149a15409..bff0d9c428 100644
--- a/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java
+++ b/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java
@@ -17,11 +17,14 @@
 package org.jclouds.docker.compute.options;
 
 import static com.google.common.base.Objects.equal;
-import static com.google.common.base.Preconditions.checkNotNull;
 
 import java.util.List;
 import java.util.Map;
 
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
 import org.jclouds.compute.options.TemplateOptions;
 import org.jclouds.docker.domain.Config;
 import org.jclouds.docker.internal.NullSafeCopies;
@@ -29,10 +32,6 @@ import org.jclouds.domain.LoginCredentials;
 import org.jclouds.javax.annotation.Nullable;
 import org.jclouds.scriptbuilder.domain.Statement;
 
-import com.google.common.base.Objects;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-
 /**
  * Contains options supported by the
  * {@link org.jclouds.compute.ComputeService#createNodesInGroup(String, int, TemplateOptions)
@@ -87,15 +86,15 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable
    protected String hostname;
    protected Integer memory;
    protected Integer cpuShares;
-   protected List entrypoint;
-   protected List commands;
+   protected List entrypoint = ImmutableList.of();
+   protected List commands = ImmutableList.of();
    protected Map volumes = ImmutableMap.of();
-   protected List env;
+   protected List env = ImmutableList.of();
    protected Map portBindings = ImmutableMap.of();
    protected String networkMode;
    protected Map extraHosts = ImmutableMap.of();
+   protected List volumesFrom = ImmutableList.of();
    protected boolean privileged;
-
    protected Config.Builder configBuilder;
 
    @Override
@@ -110,27 +109,19 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable
       super.copyTo(to);
       if (to instanceof DockerTemplateOptions) {
          DockerTemplateOptions eTo = DockerTemplateOptions.class.cast(to);
-         if (!volumes.isEmpty()) {
-            eTo.volumes(volumes);
-         }
+         eTo.volumes(volumes);
          eTo.hostname(hostname);
-         if (!dns.isEmpty()) {
-            eTo.dns(dns);
-         }
+         eTo.dns(dns);
          eTo.memory(memory);
          eTo.cpuShares(cpuShares);
          eTo.entrypoint(entrypoint);
          eTo.commands(commands);
          eTo.env(env);
-         if (!portBindings.isEmpty()) {
-            eTo.portBindings(portBindings);
-         }
+         eTo.portBindings(portBindings);
          eTo.networkMode(networkMode);
-         if (!extraHosts.isEmpty()) {
-            eTo.extraHosts(extraHosts);
-         }
+         eTo.extraHosts(extraHosts);
+         eTo.volumesFrom(volumesFrom);
          eTo.privileged(privileged);
-
          eTo.configBuilder(configBuilder);
       }
    }
@@ -142,16 +133,19 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable
       if (o == null || getClass() != o.getClass())
          return false;
       DockerTemplateOptions that = DockerTemplateOptions.class.cast(o);
-      return super.equals(that) && equal(this.volumes, that.volumes) &&
+      return super.equals(that) &&
+              equal(this.volumes, that.volumes) &&
               equal(this.hostname, that.hostname) &&
               equal(this.dns, that.dns) &&
               equal(this.memory, that.memory) &&
+              equal(this.cpuShares, that.cpuShares) &&
               equal(this.entrypoint, that.entrypoint) &&
               equal(this.commands, that.commands) &&
-              equal(this.cpuShares, that.cpuShares) &&
               equal(this.env, that.env) &&
               equal(this.portBindings, that.portBindings) &&
+              equal(this.networkMode, that.networkMode) &&
               equal(this.extraHosts, that.extraHosts) &&
+              equal(this.volumesFrom, that.volumesFrom) &&
               equal(this.privileged, that.privileged) &&
               buildersEqual(this.configBuilder, that.configBuilder);
    }
@@ -173,32 +167,35 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable
    @Override
    public String toString() {
       return Objects.toStringHelper(this)
-              .add("dns", dns)
+              .add("volumes", volumes)
               .add("hostname", hostname)
+              .add("dns", dns)
               .add("memory", memory)
               .add("cpuShares", cpuShares)
               .add("entrypoint", entrypoint)
               .add("commands", commands)
-              .add("volumes", volumes)
               .add("env", env)
               .add("portBindings", portBindings)
+              .add("networkMode", networkMode)
               .add("extraHosts", extraHosts)
+              .add("volumesFrom", volumesFrom)
               .add("configBuilder", configBuilder)
               .toString();
    }
 
    public DockerTemplateOptions volumes(Map volumes) {
-      this.volumes = ImmutableMap.copyOf(checkNotNull(volumes, "volumes"));
+      this.volumes = NullSafeCopies.copyOf(volumes);
       return this;
    }
 
    public DockerTemplateOptions dns(Iterable dns) {
-      this.dns = ImmutableList.copyOf(checkNotNull(dns, "dns"));
+      this.dns = NullSafeCopies.copyWithNullOf(dns);
       return this;
    }
 
    public DockerTemplateOptions dns(String...dns) {
-      return dns(ImmutableList.copyOf(checkNotNull(dns, "dns")));
+      this.dns = NullSafeCopies.copyWithNullOf(dns);
+      return this;
    }
 
    public DockerTemplateOptions hostname(@Nullable String hostname) {
@@ -257,7 +254,7 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable
     * @param portBindings the map of host to container port bindings
     */
    public DockerTemplateOptions portBindings(Map portBindings) {
-      this.portBindings = ImmutableMap.copyOf(checkNotNull(portBindings, "portBindings"));
+      this.portBindings = NullSafeCopies.copyOf(portBindings);
       return this;
    }
 
@@ -281,7 +278,17 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable
     * @param extraHosts the map of host names to IP addresses
     */
    public DockerTemplateOptions extraHosts(Map extraHosts) {
-      this.extraHosts = ImmutableMap.copyOf(checkNotNull(extraHosts, "extraHosts"));
+      this.extraHosts = NullSafeCopies.copyWithNullOf(extraHosts);
+      return this;
+   }
+
+   /**
+    * Set list of containers to mount volumes from onto this container.
+    *
+    * @param volumesFrom the list of container names
+    */
+   public DockerTemplateOptions volumesFrom(Iterable volumesFrom) {
+      this.volumesFrom = NullSafeCopies.copyWithNullOf(volumesFrom);
       return this;
    }
 
@@ -318,6 +325,8 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable
 
    public List getDns() { return dns; }
 
+   public List getVolumesFrom() { return volumesFrom; }
+
    public String getHostname() { return hostname; }
 
    public Integer getMemory() { return memory; }
@@ -359,7 +368,7 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable
       }
 
       /**
-       * @see DockerTemplateOptions#dns(Iterable)
+       * @see DockerTemplateOptions#dns(List)
        */
       public static DockerTemplateOptions dns(Iterable dns) {
          DockerTemplateOptions options = new DockerTemplateOptions();
@@ -470,11 +479,22 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable
          return options.privileged(privileged);
       }
 
+      /**
+       * @see DockerTemplateOptions#configBuilder(Config.Builder)
+       */
       public static DockerTemplateOptions configBuilder(Config.Builder configBuilder) {
          DockerTemplateOptions options = new DockerTemplateOptions();
          return options.configBuilder(configBuilder);
       }
 
+      /**
+       * @see DockerTemplateOptions#volumesFrom(Iterable)
+       */
+      public static DockerTemplateOptions volumesFrom(Iterable volumesFrom) {
+         DockerTemplateOptions options = new DockerTemplateOptions();
+         return options.volumesFrom(volumesFrom);
+      }
+
       /**
        * @see TemplateOptions#inboundPorts(int...)
        */
diff --git a/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java b/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java
index 20567cdd46..ebd7f5b57b 100644
--- a/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java
+++ b/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java
@@ -18,6 +18,7 @@ package org.jclouds.docker.compute.strategy;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.collect.Iterables.find;
+
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -27,6 +28,14 @@ import javax.inject.Inject;
 import javax.inject.Named;
 import javax.inject.Singleton;
 
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+
 import org.jclouds.compute.ComputeServiceAdapter;
 import org.jclouds.compute.domain.Hardware;
 import org.jclouds.compute.domain.HardwareBuilder;
@@ -48,14 +57,6 @@ import org.jclouds.domain.Location;
 import org.jclouds.domain.LoginCredentials;
 import org.jclouds.logging.Logger;
 
-import com.google.common.base.Predicate;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-
 /**
  * defines the connection between the {@link org.jclouds.docker.DockerApi} implementation and
  * the jclouds {@link org.jclouds.compute.ComputeService}
@@ -138,7 +139,12 @@ public class DockerComputeServiceAdapter implements
             }
          }
 
+         if (!templateOptions.getVolumesFrom().isEmpty()) {
+            hostConfigBuilder.volumesFrom(templateOptions.getVolumesFrom());
+         }
+
          hostConfigBuilder.networkMode(templateOptions.getNetworkMode());
+
          containerConfigBuilder.hostConfig(hostConfigBuilder.build());
       }
 
diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/Config.java b/apis/docker/src/main/java/org/jclouds/docker/domain/Config.java
index 2203a85ffb..fbe07ca2ff 100644
--- a/apis/docker/src/main/java/org/jclouds/docker/domain/Config.java
+++ b/apis/docker/src/main/java/org/jclouds/docker/domain/Config.java
@@ -64,7 +64,7 @@ public abstract class Config {
 
    public abstract String image();
 
-   public abstract Map volumes();
+   @Nullable public abstract Map volumes();
 
    @Nullable public abstract String workingDir();
 
@@ -76,9 +76,9 @@ public abstract class Config {
 
    @Nullable public abstract HostConfig hostConfig();
 
-   public abstract List binds();
+   @Nullable public abstract List binds();
 
-   public abstract List links();
+   @Nullable public abstract List links();
 
    public abstract List> lxcConf();
 
@@ -90,13 +90,13 @@ public abstract class Config {
 
    @Nullable public abstract List dns();
 
-   @Nullable public abstract String dnsSearch();
+   @Nullable public abstract List dnsSearch();
 
-   @Nullable public abstract String volumesFrom();
+   @Nullable public abstract List volumesFrom();
 
-   public abstract List capAdd();
+   @Nullable public abstract List capAdd();
 
-   public abstract List capDrop();
+   @Nullable public abstract List capDrop();
 
    public abstract Map restartPolicy();
 
@@ -117,14 +117,14 @@ public abstract class Config {
          String image, Map volumes, String workingDir, boolean networkDisabled,
          Map exposedPorts, List securityOpts, HostConfig hostConfig, List binds,
          List links, List> lxcConf, Map>> portBindings,
-         boolean publishAllPorts, boolean privileged, List dns, String dnsSearch, String volumesFrom,
+         boolean publishAllPorts, boolean privileged, List dns, List dnsSearch, List volumesFrom,
          List capAdd, List capDrop, Map restartPolicy) {
       return new AutoValue_Config(hostname, domainname, user, memory, memorySwap, cpuShares, attachStdin,
               attachStdout, attachStderr, tty, openStdin, stdinOnce, copyWithNullOf(env), copyWithNullOf(cmd),
-              copyWithNullOf(entrypoint), image, copyOf(volumes), workingDir, networkDisabled,
+              copyWithNullOf(entrypoint), image, copyWithNullOf(volumes), workingDir, networkDisabled,
               copyOf(exposedPorts), copyOf(securityOpts), hostConfig,
-              copyOf(binds), copyOf(links), copyOf(lxcConf), copyOf(portBindings), publishAllPorts, privileged,
-              copyWithNullOf(dns), dnsSearch, volumesFrom, copyOf(capAdd), copyOf(capDrop), copyOf(restartPolicy));
+              copyWithNullOf(binds), copyWithNullOf(links), copyOf(lxcConf), copyOf(portBindings), publishAllPorts, privileged,
+              copyWithNullOf(dns), copyWithNullOf(dnsSearch), copyWithNullOf(volumesFrom), copyWithNullOf(capAdd), copyWithNullOf(capDrop), copyOf(restartPolicy));
    }
 
    public static Builder builder() {
@@ -152,23 +152,23 @@ public abstract class Config {
       private List cmd;
       private List entrypoint;
       private String image;
-      private Map volumes = Maps.newHashMap();
+      private Map volumes;
       private String workingDir;
       private boolean networkDisabled;
       private Map exposedPorts = Maps.newHashMap();
       private List securityOpts = Lists.newArrayList();
       private HostConfig hostConfig;
-      private List binds = Lists.newArrayList();
-      private List links = Lists.newArrayList();
+      private List binds;
+      private List links;
       private List> lxcConf = Lists.newArrayList();
       private Map>> portBindings = Maps.newHashMap();
       private boolean publishAllPorts;
       private boolean privileged;
       private List dns;
-      private String dnsSearch;
-      private String volumesFrom;
-      private List capAdd = Lists.newArrayList();
-      private List capDrop = Lists.newArrayList();
+      private List dnsSearch;
+      private List volumesFrom;
+      private List capAdd;
+      private List capDrop;
       private Map restartPolicy = Maps.newHashMap();
 
       public Builder hostname(String hostname) {
@@ -322,12 +322,12 @@ public abstract class Config {
          return this;
       }
 
-      public Builder dnsSearch(String dnsSearch) {
+      public Builder dnsSearch(List dnsSearch) {
          this.dnsSearch = dnsSearch;
          return this;
       }
 
-      public Builder volumesFrom(String volumesFrom) {
+      public Builder volumesFrom(List volumesFrom) {
          this.volumesFrom = volumesFrom;
          return this;
       }
diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/HostConfig.java b/apis/docker/src/main/java/org/jclouds/docker/domain/HostConfig.java
index 1e655afb23..1ebdb2cbe0 100644
--- a/apis/docker/src/main/java/org/jclouds/docker/domain/HostConfig.java
+++ b/apis/docker/src/main/java/org/jclouds/docker/domain/HostConfig.java
@@ -16,45 +16,44 @@
  */
 package org.jclouds.docker.domain;
 
-import static com.google.common.base.Preconditions.checkNotNull;
 import static org.jclouds.docker.internal.NullSafeCopies.copyOf;
+import static org.jclouds.docker.internal.NullSafeCopies.copyWithNullOf;
 
 import java.util.List;
 import java.util.Map;
 
-import org.jclouds.javax.annotation.Nullable;
-import org.jclouds.json.SerializedNames;
-
 import com.google.auto.value.AutoValue;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 
+import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.json.SerializedNames;
+
 @AutoValue
 public abstract class HostConfig {
    @Nullable public abstract String containerIDFile();
 
-   public abstract List binds();
+   @Nullable public abstract List binds();
 
    public abstract List> lxcConf();
 
    public abstract boolean privileged();
 
-   public abstract List dns();
+   @Nullable public abstract List dns();
 
    @Nullable public abstract List dnsSearch();
 
    public abstract Map>> portBindings();
 
-   public abstract List links();
+   @Nullable public abstract List links();
 
-   public abstract List extraHosts();
+   @Nullable public abstract List extraHosts();
 
    public abstract boolean publishAllPorts();
 
-   public abstract List volumesFrom();
+   @Nullable public abstract List volumesFrom();
 
-   @Nullable
-   public abstract String networkMode();
+   @Nullable public abstract String networkMode();
 
    HostConfig() {
    }
@@ -64,8 +63,8 @@ public abstract class HostConfig {
    public static HostConfig create(String containerIDFile, List binds, List> lxcConf,
          boolean privileged, List dns, List dnsSearch, Map>> portBindings,
          List links, List extraHosts, boolean publishAllPorts, List volumesFrom, String networkMode) {
-      return new AutoValue_HostConfig(containerIDFile, copyOf(binds), copyOf(lxcConf), privileged, copyOf(dns), copyOf(dnsSearch),
-            copyOf(portBindings), copyOf(links), copyOf(extraHosts), publishAllPorts, copyOf(volumesFrom), networkMode);
+      return new AutoValue_HostConfig(containerIDFile, copyWithNullOf(binds), copyOf(lxcConf), privileged, copyWithNullOf(dns), copyWithNullOf(dnsSearch),
+            copyOf(portBindings), copyWithNullOf(links), copyWithNullOf(extraHosts), publishAllPorts, copyWithNullOf(volumesFrom), networkMode);
    }
 
    public static Builder builder() {
@@ -79,16 +78,16 @@ public abstract class HostConfig {
    public static final class Builder {
 
       private String containerIDFile;
-      private List binds = Lists.newArrayList();
+      private List binds;
       private List> lxcConf = Lists.newArrayList();
       private boolean privileged;
-      private List dns = Lists.newArrayList();
-      private List dnsSearch = Lists.newArrayList();
+      private List dns;
+      private List dnsSearch;
       private Map>> portBindings = Maps.newLinkedHashMap();
-      private List links = Lists.newArrayList();
-      private List extraHosts = Lists.newArrayList();
+      private List links;
+      private List extraHosts;
       private boolean publishAllPorts;
-      private List volumesFrom = Lists.newArrayList();
+      private List volumesFrom;
       private String networkMode;
 
       public Builder containerIDFile(String containerIDFile) {
@@ -97,12 +96,12 @@ public abstract class HostConfig {
       }
 
       public Builder binds(List binds) {
-         this.binds.addAll(checkNotNull(binds, "binds"));
+         this.binds = binds;
          return this;
       }
 
       public Builder lxcConf(List> lxcConf) {
-         this.lxcConf.addAll(checkNotNull(lxcConf, "lxcConf"));
+         this.lxcConf = lxcConf;
          return this;
       }
 
@@ -112,27 +111,27 @@ public abstract class HostConfig {
       }
 
       public Builder dns(List dns) {
-         this.dns.addAll(checkNotNull(dns, "dns"));
+         this.dns = dns;
          return this;
       }
 
       public Builder dnsSearch(List dnsSearch) {
-         this.dnsSearch.addAll(checkNotNull(dnsSearch, "dnsSearch"));
+         this.dnsSearch = dnsSearch;
          return this;
       }
 
       public Builder links(List links) {
-         this.links.addAll(checkNotNull(links, "links"));
+         this.links = links;
          return this;
       }
 
       public Builder extraHosts(List extraHosts) {
-         this.extraHosts.addAll(checkNotNull(extraHosts, "extraHosts"));
+         this.extraHosts = extraHosts;
          return this;
       }
 
       public Builder portBindings(Map>> portBindings) {
-         this.portBindings.putAll(portBindings);
+         this.portBindings = portBindings;
          return this;
       }
 
@@ -142,7 +141,7 @@ public abstract class HostConfig {
       }
 
       public Builder volumesFrom(List volumesFrom) {
-         this.volumesFrom.addAll(checkNotNull(volumesFrom, "volumesFrom"));
+         this.volumesFrom = volumesFrom;
          return this;
       }
 
diff --git a/apis/docker/src/main/java/org/jclouds/docker/internal/NullSafeCopies.java b/apis/docker/src/main/java/org/jclouds/docker/internal/NullSafeCopies.java
index ad86bbbf71..b6e190ce24 100644
--- a/apis/docker/src/main/java/org/jclouds/docker/internal/NullSafeCopies.java
+++ b/apis/docker/src/main/java/org/jclouds/docker/internal/NullSafeCopies.java
@@ -46,6 +46,18 @@ public class NullSafeCopies {
       return list != null ? ImmutableList.copyOf(list) : null;
    }
 
+   /**
+    * Copies given Map with keeping null value if provided.
+    *
+    * @param map
+    *           instance to copy (maybe null)
+    * @return if the parameter is not-null then immutable copy;
+    *         null otherwise
+    */
+   public static  Map copyWithNullOf(@Nullable Map map) {
+      return map != null ? ImmutableMap.copyOf(map) : null;
+   }
+
    /**
     * Copies given {@link Iterable} into immutable {@link List} with keeping null value if provided.
     *
diff --git a/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadataTest.java b/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadataTest.java
index 00cd83657b..d9fc464e29 100644
--- a/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadataTest.java
+++ b/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadataTest.java
@@ -84,7 +84,6 @@ public class ContainerToNodeMetadataTest {
               .env(null)
               .cmd(ImmutableList.of("/usr/sbin/sshd", "-D"))
               .image("jclouds/ubuntu")
-              .volumesFrom("")
               .workingDir("")
               .entrypoint(null)
               .networkDisabled(false)

From 49f7bc37afef7ebd1e2cf74b0d893dd4089cd799 Mon Sep 17 00:00:00 2001
From: Andrew Donald Kennedy 
Date: Thu, 14 Apr 2016 21:05:34 +0100
Subject: [PATCH 63/81] Make DockerTemplateOptions values null safe

---
 .../options/DockerTemplateOptions.java        | 22 +++++++++----------
 .../docker/internal/NullSafeCopies.java       |  8 +++++++
 2 files changed, 19 insertions(+), 11 deletions(-)

diff --git a/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java b/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java
index bff0d9c428..8a42253bad 100644
--- a/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java
+++ b/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java
@@ -83,15 +83,15 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable
    private static final String NO_IMAGE = "jclouds-placeholder-for-image";
 
    protected List dns = ImmutableList.of();
-   protected String hostname;
-   protected Integer memory;
-   protected Integer cpuShares;
-   protected List entrypoint = ImmutableList.of();
-   protected List commands = ImmutableList.of();
+   @Nullable protected String hostname;
+   @Nullable protected Integer memory;
+   @Nullable protected Integer cpuShares;
+   @Nullable List entrypoint;
+   @Nullable List commands;
    protected Map volumes = ImmutableMap.of();
-   protected List env = ImmutableList.of();
+   @Nullable protected List env;
    protected Map portBindings = ImmutableMap.of();
-   protected String networkMode;
+   @Nullable protected String networkMode;
    protected Map extraHosts = ImmutableMap.of();
    protected List volumesFrom = ImmutableList.of();
    protected boolean privileged;
@@ -189,12 +189,12 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable
    }
 
    public DockerTemplateOptions dns(Iterable dns) {
-      this.dns = NullSafeCopies.copyWithNullOf(dns);
+      this.dns = NullSafeCopies.copyOf(dns);
       return this;
    }
 
    public DockerTemplateOptions dns(String...dns) {
-      this.dns = NullSafeCopies.copyWithNullOf(dns);
+      this.dns = NullSafeCopies.copyOf(dns);
       return this;
    }
 
@@ -278,7 +278,7 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable
     * @param extraHosts the map of host names to IP addresses
     */
    public DockerTemplateOptions extraHosts(Map extraHosts) {
-      this.extraHosts = NullSafeCopies.copyWithNullOf(extraHosts);
+      this.extraHosts = NullSafeCopies.copyOf(extraHosts);
       return this;
    }
 
@@ -288,7 +288,7 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable
     * @param volumesFrom the list of container names
     */
    public DockerTemplateOptions volumesFrom(Iterable volumesFrom) {
-      this.volumesFrom = NullSafeCopies.copyWithNullOf(volumesFrom);
+      this.volumesFrom = NullSafeCopies.copyOf(volumesFrom);
       return this;
    }
 
diff --git a/apis/docker/src/main/java/org/jclouds/docker/internal/NullSafeCopies.java b/apis/docker/src/main/java/org/jclouds/docker/internal/NullSafeCopies.java
index b6e190ce24..f3d9eb860c 100644
--- a/apis/docker/src/main/java/org/jclouds/docker/internal/NullSafeCopies.java
+++ b/apis/docker/src/main/java/org/jclouds/docker/internal/NullSafeCopies.java
@@ -34,6 +34,14 @@ public class NullSafeCopies {
       return list != null ? ImmutableList.copyOf(list) : ImmutableList. of();
    }
 
+   public static  List copyOf(@Nullable Iterable list) {
+      return list != null ? ImmutableList.copyOf(list) : ImmutableList. of();
+   }
+
+   public static  List copyOf(@Nullable E[] array) {
+      return array != null ? ImmutableList.copyOf(array) : ImmutableList. of();
+   }
+
    /**
     * Copies given List with keeping null value if provided.
     *

From 42d0576aa938c3da0ed94618b3fb9b7155b41b22 Mon Sep 17 00:00:00 2001
From: Andrew Donald Kennedy 
Date: Mon, 25 Apr 2016 22:25:40 +0100
Subject: [PATCH 64/81] Add openStdin option to Docker template options and set
 all port bindings explicitly

---
 .../options/DockerTemplateOptions.java        | 28 ++++++++++++++++++-
 .../strategy/DockerComputeServiceAdapter.java | 21 ++++++++++++--
 2 files changed, 46 insertions(+), 3 deletions(-)

diff --git a/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java b/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java
index 8a42253bad..1ff4b44b51 100644
--- a/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java
+++ b/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java
@@ -95,6 +95,7 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable
    protected Map extraHosts = ImmutableMap.of();
    protected List volumesFrom = ImmutableList.of();
    protected boolean privileged;
+   protected boolean openStdin;
    protected Config.Builder configBuilder;
 
    @Override
@@ -122,6 +123,7 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable
          eTo.extraHosts(extraHosts);
          eTo.volumesFrom(volumesFrom);
          eTo.privileged(privileged);
+         eTo.openStdin(openStdin);
          eTo.configBuilder(configBuilder);
       }
    }
@@ -147,6 +149,7 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable
               equal(this.extraHosts, that.extraHosts) &&
               equal(this.volumesFrom, that.volumesFrom) &&
               equal(this.privileged, that.privileged) &&
+              equal(this.openStdin, that.openStdin) &&
               buildersEqual(this.configBuilder, that.configBuilder);
    }
 
@@ -161,7 +164,7 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable
    @Override
    public int hashCode() {
       return Objects.hashCode(super.hashCode(), volumes, hostname, dns, memory, entrypoint, commands, cpuShares, env,
-            portBindings, extraHosts, configBuilder);
+            portBindings, extraHosts, volumesFrom, privileged, openStdin, configBuilder);
    }
 
    @Override
@@ -179,6 +182,8 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable
               .add("networkMode", networkMode)
               .add("extraHosts", extraHosts)
               .add("volumesFrom", volumesFrom)
+              .add("privileged", privileged)
+              .add("openStdin", openStdin)
               .add("configBuilder", configBuilder)
               .toString();
    }
@@ -304,6 +309,17 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable
       return this;
    }
 
+   /**
+    * Keep {@code STDIN} open when running interactive workloads in the container.
+    *
+    * @param openStdin Whether the container should keep STDIN open
+    * @return this instance
+    */
+   public DockerTemplateOptions openStdin(boolean openStdin) {
+      this.openStdin = openStdin;
+      return this;
+   }
+
    /**
     * This method sets Config.Builder configuration object, which can be used as
     * a replacement for all the other settings from this class. Some values in
@@ -347,6 +363,8 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable
 
    public boolean getPrivileged() { return privileged; }
 
+   public boolean getOpenStdin() { return openStdin; }
+
    public Config.Builder getConfigBuilder() { return configBuilder; }
 
    public static class Builder {
@@ -479,6 +497,14 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable
          return options.privileged(privileged);
       }
 
+      /**
+       * @see DockerTemplateOptions#openStdin(boolean)
+       */
+      public static DockerTemplateOptions openStdin(boolean openStdin) {
+         DockerTemplateOptions options = new DockerTemplateOptions();
+         return options.openStdin(openStdin);
+      }
+
       /**
        * @see DockerTemplateOptions#configBuilder(Config.Builder)
        */
diff --git a/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java b/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java
index ebd7f5b57b..a1c9caea60 100644
--- a/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java
+++ b/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java
@@ -98,6 +98,7 @@ public class DockerComputeServiceAdapter implements
          containerConfigBuilder.memory(templateOptions.getMemory());
          containerConfigBuilder.hostname(templateOptions.getHostname());
          containerConfigBuilder.cpuShares(templateOptions.getCpuShares());
+         containerConfigBuilder.openStdin(templateOptions.getOpenStdin());
          containerConfigBuilder.env(templateOptions.getEnv());
 
          if (!templateOptions.getVolumes().isEmpty()) {
@@ -110,13 +111,13 @@ public class DockerComputeServiceAdapter implements
 
          HostConfig.Builder hostConfigBuilder = HostConfig.builder()
                  .publishAllPorts(true)
-                 .privileged( templateOptions.getPrivileged() );
+                 .privileged(templateOptions.getPrivileged());
 
          if (!templateOptions.getPortBindings().isEmpty()) {
             Map>> portBindings = Maps.newHashMap();
             for (Map.Entry entry : templateOptions.getPortBindings().entrySet()) {
                portBindings.put(entry.getValue() + "/tcp",
-                       Lists.>newArrayList(ImmutableMap.of("HostPort", Integer.toString(entry.getKey()))));
+                       Lists.>newArrayList(ImmutableMap.of("HostIp", "0.0.0.0", "HostPort", Integer.toString(entry.getKey()))));
             }
             hostConfigBuilder.portBindings(portBindings);
          }
@@ -167,6 +168,22 @@ public class DockerComputeServiceAdapter implements
       // build once more after setting inboundPorts
       containerConfig = containerConfigBuilder.build();
 
+      // finally update port bindings
+      Map>> portBindings = Maps.newHashMap();
+      Map>> existingBindings = containerConfig.hostConfig().portBindings();
+      if (existingBindings != null) {
+          portBindings.putAll(existingBindings);
+      }
+      for (String exposedPort : containerConfig.exposedPorts().keySet()) {
+         if (!portBindings.containsKey(exposedPort)) {
+            portBindings.put(exposedPort, Lists.>newArrayList(ImmutableMap.of("HostIp", "0.0.0.0")));
+         }
+      }
+      HostConfig.Builder hostConfigBuilder = HostConfig.builder().fromHostConfig(containerConfig.hostConfig());
+      hostConfigBuilder.portBindings(portBindings);
+      containerConfigBuilder.hostConfig(hostConfigBuilder.build());
+      containerConfig = containerConfigBuilder.build();
+
       logger.debug(">> creating new container with containerConfig(%s)", containerConfig);
       Container container = api.getContainerApi().createContainer(name, containerConfig);
       logger.trace("<< container(%s)", container.id());

From 61fb3ec14581a8a91bb35d45536d61968d13c973 Mon Sep 17 00:00:00 2001
From: Andrew Donald Kennedy 
Date: Wed, 18 May 2016 11:28:58 +0100
Subject: [PATCH 65/81] Support supplying key and certificate to Docker as data

---
 .../org/jclouds/docker/DockerApiMetadata.java |  6 +-
 .../suppliers/DockerSSLContextSupplier.java   | 18 ++++--
 .../DockerUntrustedSSLContextSupplier.java    | 12 ++--
 .../docker/suppliers/SSLContextBuilder.java   | 57 ++++++++++++++-----
 4 files changed, 67 insertions(+), 26 deletions(-)

diff --git a/apis/docker/src/main/java/org/jclouds/docker/DockerApiMetadata.java b/apis/docker/src/main/java/org/jclouds/docker/DockerApiMetadata.java
index 9089fd06d8..453f3932e4 100644
--- a/apis/docker/src/main/java/org/jclouds/docker/DockerApiMetadata.java
+++ b/apis/docker/src/main/java/org/jclouds/docker/DockerApiMetadata.java
@@ -38,6 +38,7 @@ import com.google.inject.Module;
 public class DockerApiMetadata extends BaseHttpApiMetadata {
 
     public static final String DOCKER_CA_CERT_PATH = "docker.cacert.path";
+    public static final String DOCKER_CA_CERT_DATA = "docker.cacert.data";
 
    @Override
    public Builder toBuilder() {
@@ -58,6 +59,7 @@ public class DockerApiMetadata extends BaseHttpApiMetadata {
       properties.setProperty(ComputeServiceProperties.IMAGE_LOGIN_USER, "root:password");
       properties.setProperty(TEMPLATE, "osFamily=UBUNTU,os64Bit=true");
       properties.setProperty(DOCKER_CA_CERT_PATH, "");
+      properties.setProperty(DOCKER_CA_CERT_DATA, "");
       return properties;
    }
 
@@ -67,8 +69,8 @@ public class DockerApiMetadata extends BaseHttpApiMetadata {
          super(DockerApi.class);
          id("docker")
                  .name("Docker API")
-                 .identityName("Path to certificate .pem file")
-                 .credentialName("Path to key .pem file")
+                 .identityName("Path or data for certificate .pem file")
+                 .credentialName("Path or data for key .pem file")
                  .documentation(URI.create("https://docs.docker.com/reference/api/docker_remote_api/"))
                  .version("1.21")
                  .defaultEndpoint("https://127.0.0.1:2376")
diff --git a/apis/docker/src/main/java/org/jclouds/docker/suppliers/DockerSSLContextSupplier.java b/apis/docker/src/main/java/org/jclouds/docker/suppliers/DockerSSLContextSupplier.java
index ed901f36dd..326528a61f 100644
--- a/apis/docker/src/main/java/org/jclouds/docker/suppliers/DockerSSLContextSupplier.java
+++ b/apis/docker/src/main/java/org/jclouds/docker/suppliers/DockerSSLContextSupplier.java
@@ -18,6 +18,7 @@ package org.jclouds.docker.suppliers;
 
 import com.google.common.base.Strings;
 import com.google.common.base.Supplier;
+
 import org.jclouds.docker.DockerApiMetadata;
 import org.jclouds.domain.Credentials;
 import org.jclouds.location.Provider;
@@ -26,22 +27,25 @@ import javax.inject.Inject;
 import javax.inject.Named;
 import javax.inject.Singleton;
 import javax.net.ssl.SSLContext;
+
 import java.io.IOException;
 import java.security.GeneralSecurityException;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.base.Throwables.propagate;
+import static org.jclouds.docker.suppliers.SSLContextBuilder.isClientKeyAndCertificateData;
 
 @Singleton
 public class DockerSSLContextSupplier implements Supplier {
    private final Supplier creds;
    private final String caCertPath;
-
+   private final String caCertData;
 
    @Inject
-   DockerSSLContextSupplier(@Provider Supplier creds, @Named(DockerApiMetadata.DOCKER_CA_CERT_PATH) String caCertPath) {
+   DockerSSLContextSupplier(@Provider Supplier creds, @Named(DockerApiMetadata.DOCKER_CA_CERT_PATH) String caCertPath, @Named(DockerApiMetadata.DOCKER_CA_CERT_DATA) String caCertData) {
       this.creds = creds;
       this.caCertPath = caCertPath;
+      this.caCertData = caCertData;
    }
 
    @Override
@@ -49,9 +53,15 @@ public class DockerSSLContextSupplier implements Supplier {
       Credentials currentCreds = checkNotNull(creds.get(), "credential supplier returned null");
       try {
          SSLContextBuilder builder = new SSLContextBuilder();
-         builder.clientKeyAndCertificate(currentCreds.credential, currentCreds.identity);
+         if (isClientKeyAndCertificateData(currentCreds.credential, currentCreds.identity)) {
+             builder.clientKeyAndCertificateData(currentCreds.credential, currentCreds.identity);
+         } else {
+             builder.clientKeyAndCertificatePaths(currentCreds.credential, currentCreds.identity);
+         }
          if (!Strings.isNullOrEmpty(caCertPath)) {
-            builder.caCertificate(caCertPath);
+            builder.caCertificatePath(caCertPath);
+         } else if (!Strings.isNullOrEmpty(caCertData)) {
+            builder.caCertificateData(caCertData);
          }
          return builder.build();
       } catch (GeneralSecurityException e) {
diff --git a/apis/docker/src/main/java/org/jclouds/docker/suppliers/DockerUntrustedSSLContextSupplier.java b/apis/docker/src/main/java/org/jclouds/docker/suppliers/DockerUntrustedSSLContextSupplier.java
index 880a48a839..f972bb403c 100644
--- a/apis/docker/src/main/java/org/jclouds/docker/suppliers/DockerUntrustedSSLContextSupplier.java
+++ b/apis/docker/src/main/java/org/jclouds/docker/suppliers/DockerUntrustedSSLContextSupplier.java
@@ -17,6 +17,7 @@
 package org.jclouds.docker.suppliers;
 
 import com.google.common.base.Supplier;
+
 import org.jclouds.domain.Credentials;
 import org.jclouds.http.config.SSLModule;
 import org.jclouds.location.Provider;
@@ -30,6 +31,7 @@ import java.io.IOException;
 import java.security.GeneralSecurityException;
 
 import static com.google.common.base.Throwables.propagate;
+import static org.jclouds.docker.suppliers.SSLContextBuilder.isClientKeyAndCertificateData;
 
 @Singleton
 public class DockerUntrustedSSLContextSupplier implements Supplier {
@@ -45,13 +47,13 @@ public class DockerUntrustedSSLContextSupplier implements Supplier {
 
    @Override
    public SSLContext get() {
-      Credentials currentCreds = creds.get();
+      Credentials currentCreds = checkNotNull(creds.get(), "credential supplier returned null");
       try {
          SSLContextBuilder builder = new SSLContextBuilder();
-         // check if identity and credential are files, to set up sslContext
-         if (currentCreds != null && new File(currentCreds.identity).isFile()
-               && new File(currentCreds.credential).isFile()) {
-            builder.clientKeyAndCertificate(currentCreds.credential, currentCreds.identity);
+         if (isClientKeyAndCertificateData(currentCreds.credential, currentCreds.identity)) {
+            builder.clientKeyAndCertificateData(currentCreds.credential, currentCreds.identity);
+         } else if (new File(currentCreds.identity).isFile() && new File(currentCreds.credential).isFile()) {
+            builder.clientKeyAndCertificatePaths(currentCreds.credential, currentCreds.identity);
          }
          builder.trustManager(insecureTrustManager);
          return builder.build();
diff --git a/apis/docker/src/main/java/org/jclouds/docker/suppliers/SSLContextBuilder.java b/apis/docker/src/main/java/org/jclouds/docker/suppliers/SSLContextBuilder.java
index 6030def592..cc1025b744 100644
--- a/apis/docker/src/main/java/org/jclouds/docker/suppliers/SSLContextBuilder.java
+++ b/apis/docker/src/main/java/org/jclouds/docker/suppliers/SSLContextBuilder.java
@@ -18,10 +18,13 @@ package org.jclouds.docker.suppliers;
 
 import com.google.common.base.Charsets;
 import com.google.common.io.Files;
+
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
 import org.bouncycastle.openssl.PEMKeyPair;
 import org.bouncycastle.openssl.PEMParser;
 import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
+
+import org.jclouds.crypto.Pems;
 import org.jclouds.util.Closeables2;
 
 import javax.net.ssl.KeyManager;
@@ -29,6 +32,7 @@ import javax.net.ssl.SSLContext;
 import javax.net.ssl.TrustManager;
 import javax.net.ssl.TrustManagerFactory;
 import javax.net.ssl.X509ExtendedKeyManager;
+
 import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.IOException;
@@ -54,22 +58,48 @@ public class SSLContextBuilder {
     private KeyManager[] keyManagers;
     private TrustManager[] trustManagers;
 
+    public static final boolean isClientKeyAndCertificateData(String key, String cert) {
+        return (key.startsWith(Pems.PUBLIC_X509_MARKER) || key.startsWith(Pems.PUBLIC_PKCS1_MARKER)) &&
+                cert.startsWith(Pems.CERTIFICATE_X509_MARKER);
+    }
+
     public SSLContextBuilder() { }
 
-    public SSLContextBuilder clientKeyAndCertificate(String keyPath, String certPath) throws IOException, CertificateException {
+    public SSLContextBuilder clientKeyAndCertificatePaths(String keyPath, String certPath) throws IOException, CertificateException {
         X509Certificate certificate = getCertificate(loadFile(certPath));
         PrivateKey privateKey = getKey(loadFile(keyPath));
-        keyManagers = new KeyManager[]{new InMemoryKeyManager(certificate, privateKey)};
+        keyManager(new InMemoryKeyManager(certificate, privateKey));
         return this;
     }
 
-    public SSLContextBuilder caCertificate(String caCertPath){
+    public SSLContextBuilder clientKeyAndCertificateData(String keyData, String certData) throws CertificateException {
+        X509Certificate certificate = getCertificate(certData);
+        PrivateKey privateKey = getKey(keyData);
+        keyManager(new InMemoryKeyManager(certificate, privateKey));
+        return this;
+    }
+
+    public SSLContextBuilder caCertificatePath(String caCertPath) {
+        try {
+            trustManagers = getTrustManagerWithCaCert(loadFile(caCertPath));
+        } catch (IOException e) {
+            throw propagate(e);
+        }
+        return this;
+    }
+
+    public SSLContextBuilder caCertificateData(String caCertPath) {
         trustManagers = getTrustManagerWithCaCert(caCertPath);
         return this;
     }
 
+    public SSLContextBuilder keyManager(KeyManager keyManager) {
+        keyManagers = new KeyManager[] { keyManager };
+        return this;
+    }
+
     public SSLContextBuilder trustManager(TrustManager trustManager) {
-        trustManagers = new TrustManager[]{trustManager};
+        trustManagers = new TrustManager[] { trustManager };
         return this;
     }
 
@@ -79,9 +109,9 @@ public class SSLContextBuilder {
         return sslContext;
     }
 
-    private TrustManager[] getTrustManagerWithCaCert(String caCertPath) {
+    private TrustManager[] getTrustManagerWithCaCert(String caCertData) {
         try {
-            X509Certificate caCert = getCertificate(loadFile(caCertPath));
+            X509Certificate caCert = getCertificate(caCertData);
             KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
             trustStore.load(null, null);
             trustStore.setCertificateEntry("ca", caCert);
@@ -132,32 +162,29 @@ public class SSLContextBuilder {
 
         private final PrivateKey privateKey;
 
-        public InMemoryKeyManager(final X509Certificate certificate, final PrivateKey privateKey)
-                throws IOException, CertificateException {
+        public InMemoryKeyManager(final X509Certificate certificate, final PrivateKey privateKey) throws CertificateException {
             this.certificate = certificate;
             this.privateKey = privateKey;
         }
 
         @Override
-        public String chooseClientAlias(final String[] keyType, final Principal[] issuers,
-                                        final Socket socket) {
+        public String chooseClientAlias(final String[] keyType, final Principal[] issuers, final Socket socket) {
             return DEFAULT_ALIAS;
         }
 
         @Override
-        public String chooseServerAlias(final String keyType, final Principal[] issuers,
-                                        final Socket socket) {
+        public String chooseServerAlias(final String keyType, final Principal[] issuers, final Socket socket) {
             return DEFAULT_ALIAS;
         }
 
         @Override
         public X509Certificate[] getCertificateChain(final String alias) {
-            return new X509Certificate[]{certificate};
+            return new X509Certificate[] { certificate };
         }
 
         @Override
         public String[] getClientAliases(final String keyType, final Principal[] issuers) {
-            return new String[]{DEFAULT_ALIAS};
+            return new String[] { DEFAULT_ALIAS };
         }
 
         @Override
@@ -167,7 +194,7 @@ public class SSLContextBuilder {
 
         @Override
         public String[] getServerAliases(final String keyType, final Principal[] issuers) {
-            return new String[]{DEFAULT_ALIAS};
+            return new String[] { DEFAULT_ALIAS };
         }
     }
 

From 4ef28251c58ea0bbeedcadb3df03169c55687427 Mon Sep 17 00:00:00 2001
From: Andrew Gaul 
Date: Mon, 23 May 2016 20:26:01 -0700
Subject: [PATCH 66/81] Add missing import

Regression from c5d845d368adf01909fd8ab5eeb824f7c44fb69e.
---
 .../docker/suppliers/DockerUntrustedSSLContextSupplier.java     | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/apis/docker/src/main/java/org/jclouds/docker/suppliers/DockerUntrustedSSLContextSupplier.java b/apis/docker/src/main/java/org/jclouds/docker/suppliers/DockerUntrustedSSLContextSupplier.java
index f972bb403c..f20cceff9f 100644
--- a/apis/docker/src/main/java/org/jclouds/docker/suppliers/DockerUntrustedSSLContextSupplier.java
+++ b/apis/docker/src/main/java/org/jclouds/docker/suppliers/DockerUntrustedSSLContextSupplier.java
@@ -16,6 +16,8 @@
  */
 package org.jclouds.docker.suppliers;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
 import com.google.common.base.Supplier;
 
 import org.jclouds.domain.Credentials;

From d4cf7743897000430eb8f102d713c0e4e8532c5b Mon Sep 17 00:00:00 2001
From: Josef Cacek 
Date: Tue, 24 May 2016 14:09:06 +0200
Subject: [PATCH 67/81] [JCLOUDS-1117] fix for advanced Docker configuration -
 the Config.Builder should stay unchanged if used

---
 .../strategy/DockerComputeServiceAdapter.java | 74 ++++++++++---------
 .../compute/SshToCustomPortLiveTest.java      | 14 +++-
 2 files changed, 51 insertions(+), 37 deletions(-)

diff --git a/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java b/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java
index a1c9caea60..3c88f3153e 100644
--- a/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java
+++ b/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java
@@ -89,9 +89,11 @@ public class DockerComputeServiceAdapter implements
       String loginUserPassword = template.getImage().getDefaultCredentials().getOptionalPassword().or("password");
 
       DockerTemplateOptions templateOptions = DockerTemplateOptions.class.cast(options);
+
+      Config containerConfig = null;
       Config.Builder containerConfigBuilder = templateOptions.getConfigBuilder();
       if (containerConfigBuilder == null) {
-         containerConfigBuilder = Config.builder();
+         containerConfigBuilder = Config.builder().image(imageId);
 
          containerConfigBuilder.entrypoint(templateOptions.getEntrypoint());
          containerConfigBuilder.cmd(templateOptions.getCommands());
@@ -110,14 +112,14 @@ public class DockerComputeServiceAdapter implements
          }
 
          HostConfig.Builder hostConfigBuilder = HostConfig.builder()
-                 .publishAllPorts(true)
-                 .privileged(templateOptions.getPrivileged());
+               .publishAllPorts(true)
+               .privileged(templateOptions.getPrivileged());
 
          if (!templateOptions.getPortBindings().isEmpty()) {
             Map>> portBindings = Maps.newHashMap();
             for (Map.Entry entry : templateOptions.getPortBindings().entrySet()) {
                portBindings.put(entry.getValue() + "/tcp",
-                       Lists.>newArrayList(ImmutableMap.of("HostIp", "0.0.0.0", "HostPort", Integer.toString(entry.getKey()))));
+                     Lists.>newArrayList(ImmutableMap.of("HostIp", "0.0.0.0", "HostPort", Integer.toString(entry.getKey()))));
             }
             hostConfigBuilder.portBindings(portBindings);
          }
@@ -147,41 +149,43 @@ public class DockerComputeServiceAdapter implements
          hostConfigBuilder.networkMode(templateOptions.getNetworkMode());
 
          containerConfigBuilder.hostConfig(hostConfigBuilder.build());
-      }
 
-      containerConfigBuilder.image(imageId);
-
-      // add the inbound ports into exposed ports map
-      Config containerConfig = containerConfigBuilder.build();
-      Map exposedPorts = Maps.newHashMap();
-      if (containerConfig.exposedPorts() == null) {
-         exposedPorts.putAll(containerConfig.exposedPorts());
-      }
-      for (int inboundPort : templateOptions.getInboundPorts()) {
-         String portKey = inboundPort + "/tcp";
-         if (!exposedPorts.containsKey(portKey)) {
-            exposedPorts.put(portKey, Maps.newHashMap());
+         // add the inbound ports into exposed ports map
+         containerConfig = containerConfigBuilder.build();
+         Map exposedPorts = Maps.newHashMap();
+         if (containerConfig.exposedPorts() == null) {
+            exposedPorts.putAll(containerConfig.exposedPorts());
          }
-      }
-      containerConfigBuilder.exposedPorts(exposedPorts);
-
-      // build once more after setting inboundPorts
-      containerConfig = containerConfigBuilder.build();
-
-      // finally update port bindings
-      Map>> portBindings = Maps.newHashMap();
-      Map>> existingBindings = containerConfig.hostConfig().portBindings();
-      if (existingBindings != null) {
-          portBindings.putAll(existingBindings);
-      }
-      for (String exposedPort : containerConfig.exposedPorts().keySet()) {
-         if (!portBindings.containsKey(exposedPort)) {
-            portBindings.put(exposedPort, Lists.>newArrayList(ImmutableMap.of("HostIp", "0.0.0.0")));
+         for (int inboundPort : templateOptions.getInboundPorts()) {
+            String portKey = inboundPort + "/tcp";
+            if (!exposedPorts.containsKey(portKey)) {
+               exposedPorts.put(portKey, Maps.newHashMap());
+            }
          }
+         containerConfigBuilder.exposedPorts(exposedPorts);
+
+         // build once more after setting inboundPorts
+         containerConfig = containerConfigBuilder.build();
+
+         // finally update port bindings
+         Map>> portBindings = Maps.newHashMap();
+         Map>> existingBindings = containerConfig.hostConfig().portBindings();
+         if (existingBindings != null) {
+            portBindings.putAll(existingBindings);
+         }
+         for (String exposedPort : containerConfig.exposedPorts().keySet()) {
+            if (!portBindings.containsKey(exposedPort)) {
+               portBindings.put(exposedPort, Lists.>newArrayList(ImmutableMap.of("HostIp", "0.0.0.0")));
+            }
+         }
+         hostConfigBuilder = HostConfig.builder().fromHostConfig(containerConfig.hostConfig());
+         hostConfigBuilder.portBindings(portBindings);
+         containerConfigBuilder.hostConfig(hostConfigBuilder.build());
+
+      } else {
+         containerConfigBuilder.image(imageId);
       }
-      HostConfig.Builder hostConfigBuilder = HostConfig.builder().fromHostConfig(containerConfig.hostConfig());
-      hostConfigBuilder.portBindings(portBindings);
-      containerConfigBuilder.hostConfig(hostConfigBuilder.build());
+
       containerConfig = containerConfigBuilder.build();
 
       logger.debug(">> creating new container with containerConfig(%s)", containerConfig);
diff --git a/apis/docker/src/test/java/org/jclouds/docker/compute/SshToCustomPortLiveTest.java b/apis/docker/src/test/java/org/jclouds/docker/compute/SshToCustomPortLiveTest.java
index ba11feb089..23f3838f2a 100644
--- a/apis/docker/src/test/java/org/jclouds/docker/compute/SshToCustomPortLiveTest.java
+++ b/apis/docker/src/test/java/org/jclouds/docker/compute/SshToCustomPortLiveTest.java
@@ -26,6 +26,7 @@ import static org.testng.Assert.assertNotNull;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.List;
+import java.util.Map;
 
 import org.jclouds.compute.RunNodesException;
 import org.jclouds.compute.domain.ExecResponse;
@@ -49,8 +50,11 @@ import org.testng.annotations.Test;
 import com.google.common.base.Optional;
 import com.google.common.base.Predicate;
 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.collect.Lists;
+import com.google.common.collect.Maps;
 import com.google.inject.AbstractModule;
 import com.google.inject.Module;
 
@@ -61,6 +65,7 @@ import com.google.inject.Module;
 public class SshToCustomPortLiveTest extends BaseComputeServiceContextLiveTest {
 
    private static final int SSH_PORT = 8822;
+   private static final int SSH_PORT_BRIDGE = 18822;
    private static final String IMAGE_REPOSITORY = "jclouds/testrepo";
    private static final String IMAGE_TAG_1 = "testtag";
    private static final String IMAGE_TAG_2 = "second";
@@ -130,12 +135,17 @@ public class SshToCustomPortLiveTest extends BaseComputeServiceContextLiveTest {
 
    @Test(dependsOnMethods = "testImageCreated")
    public void testAdvancedConfig() throws RunNodesException {
+      final String portId = SSH_PORT + "/tcp";
       final DockerTemplateOptions options = DockerTemplateOptions.Builder
             .configBuilder(
                   Config.builder().env(ImmutableList. of("SSH_PORT=" + SSH_PORT, "ROOT_PASSWORD=jcloudsRulez"))
-                        .hostConfig(HostConfig.builder().networkMode("host").build())
+                        .exposedPorts(ImmutableMap. of(portId, Maps.newHashMap()))
+                        .hostConfig(HostConfig.builder().networkMode("bridge")
+                              .portBindings(ImmutableMap.>> of(portId,
+                                    Lists.>newArrayList(ImmutableMap.of("HostPort", String.valueOf(SSH_PORT_BRIDGE)))))
+                              .build())
                         .image("test-if-this-value-is-correctly-overriden"))
-            .overrideLoginUser("root").overrideLoginPassword("jcloudsRulez").blockOnPort(SSH_PORT, 30);
+            .overrideLoginUser("root").overrideLoginPassword("jcloudsRulez").blockOnPort(SSH_PORT_BRIDGE, 30);
 
       final Template template = view.getComputeService().templateBuilder().imageId(image.id()).options(options).build();
 

From c0469cc9b73719ac6ccf58a7da7b7da2917e4620 Mon Sep 17 00:00:00 2001
From: Josef Cacek 
Date: Tue, 24 May 2016 14:51:45 +0200
Subject: [PATCH 68/81] [JCLOUDS-1118] fix configuration options location; mark
 some fields Nullable

---
 .../org/jclouds/docker/domain/Config.java     | 120 +-------
 .../org/jclouds/docker/domain/HostConfig.java |  50 +++-
 .../java/org/jclouds/docker/domain/Info.java  |   4 +-
 .../docker/domain/NetworkSettings.java        |   8 +-
 .../org/jclouds/docker/domain/ConfigTest.java |   3 -
 .../docker/features/ContainerApiLiveTest.java |   5 +-
 .../docker/parse/ContainerParseTest.java      |   3 +
 .../parse/ContainerVersionMajor1Minor21.java  | 263 ++++++++++++++++++
 .../jclouds/docker/parse/Info2ParseTest.java  |  72 +++++
 .../test/resources/container-1.21-create.json |  72 +++++
 .../resources/container-1.21-inspect.json     | 147 ++++++++++
 apis/docker/src/test/resources/container.json |   7 +-
 apis/docker/src/test/resources/info2.json     |  90 ++++++
 13 files changed, 712 insertions(+), 132 deletions(-)
 create mode 100644 apis/docker/src/test/java/org/jclouds/docker/parse/ContainerVersionMajor1Minor21.java
 create mode 100644 apis/docker/src/test/java/org/jclouds/docker/parse/Info2ParseTest.java
 create mode 100644 apis/docker/src/test/resources/container-1.21-create.json
 create mode 100644 apis/docker/src/test/resources/container-1.21-inspect.json
 create mode 100644 apis/docker/src/test/resources/info2.json

diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/Config.java b/apis/docker/src/main/java/org/jclouds/docker/domain/Config.java
index fbe07ca2ff..58658ded88 100644
--- a/apis/docker/src/main/java/org/jclouds/docker/domain/Config.java
+++ b/apis/docker/src/main/java/org/jclouds/docker/domain/Config.java
@@ -23,6 +23,7 @@ import static org.jclouds.docker.internal.NullSafeCopies.copyWithNullOf;
 import java.util.List;
 import java.util.Map;
 
+import org.jclouds.docker.domain.HostConfig.Builder;
 import org.jclouds.javax.annotation.Nullable;
 import org.jclouds.json.SerializedNames;
 
@@ -76,30 +77,6 @@ public abstract class Config {
 
    @Nullable public abstract HostConfig hostConfig();
 
-   @Nullable public abstract List binds();
-
-   @Nullable public abstract List links();
-
-   public abstract List> lxcConf();
-
-   public abstract Map>> portBindings();
-
-   public abstract boolean publishAllPorts();
-
-   public abstract boolean privileged();
-
-   @Nullable public abstract List dns();
-
-   @Nullable public abstract List dnsSearch();
-
-   @Nullable public abstract List volumesFrom();
-
-   @Nullable public abstract List capAdd();
-
-   @Nullable public abstract List capDrop();
-
-   public abstract Map restartPolicy();
-
    Config() {
    }
 
@@ -107,24 +84,17 @@ public abstract class Config {
          {
                  "Hostname", "Domainname", "User", "Memory", "MemorySwap", "CpuShares", "AttachStdin", "AttachStdout",
                  "AttachStderr", "Tty", "OpenStdin", "StdinOnce", "Env", "Cmd", "Entrypoint", "Image", "Volumes",
-                 "WorkingDir", "NetworkDisabled", "ExposedPorts", "SecurityOpts", "HostConfig", "Binds", "Links",
-                 "LxcConf", "PortBindings", "PublishAllPorts", "Privileged", "Dns", "DnsSearch", "VolumesFrom",
-                 "CapAdd", "CapDrop", "RestartPolicy"
+                 "WorkingDir", "NetworkDisabled", "ExposedPorts", "SecurityOpts", "HostConfig"
          })
    public static Config create(String hostname, String domainname, String user, int memory, int memorySwap,
          int cpuShares, boolean attachStdin, boolean attachStdout, boolean attachStderr, boolean tty,
          boolean openStdin, boolean stdinOnce, List env, List cmd, List entrypoint,
          String image, Map volumes, String workingDir, boolean networkDisabled,
-         Map exposedPorts, List securityOpts, HostConfig hostConfig, List binds,
-         List links, List> lxcConf, Map>> portBindings,
-         boolean publishAllPorts, boolean privileged, List dns, List dnsSearch, List volumesFrom,
-         List capAdd, List capDrop, Map restartPolicy) {
+         Map exposedPorts, List securityOpts, HostConfig hostConfig) {
       return new AutoValue_Config(hostname, domainname, user, memory, memorySwap, cpuShares, attachStdin,
               attachStdout, attachStderr, tty, openStdin, stdinOnce, copyWithNullOf(env), copyWithNullOf(cmd),
               copyWithNullOf(entrypoint), image, copyWithNullOf(volumes), workingDir, networkDisabled,
-              copyOf(exposedPorts), copyOf(securityOpts), hostConfig,
-              copyWithNullOf(binds), copyWithNullOf(links), copyOf(lxcConf), copyOf(portBindings), publishAllPorts, privileged,
-              copyWithNullOf(dns), copyWithNullOf(dnsSearch), copyWithNullOf(volumesFrom), copyWithNullOf(capAdd), copyWithNullOf(capDrop), copyOf(restartPolicy));
+              copyOf(exposedPorts), copyOf(securityOpts), hostConfig);
    }
 
    public static Builder builder() {
@@ -158,18 +128,6 @@ public abstract class Config {
       private Map exposedPorts = Maps.newHashMap();
       private List securityOpts = Lists.newArrayList();
       private HostConfig hostConfig;
-      private List binds;
-      private List links;
-      private List> lxcConf = Lists.newArrayList();
-      private Map>> portBindings = Maps.newHashMap();
-      private boolean publishAllPorts;
-      private boolean privileged;
-      private List dns;
-      private List dnsSearch;
-      private List volumesFrom;
-      private List capAdd;
-      private List capDrop;
-      private Map restartPolicy = Maps.newHashMap();
 
       public Builder hostname(String hostname) {
          this.hostname = hostname;
@@ -287,71 +245,10 @@ public abstract class Config {
          return this;
       }
 
-      public Builder binds(List binds) {
-         this.binds = binds;
-         return this;
-      }
-
-      public Builder links(List links) {
-         this.links = links;
-         return this;
-      }
-
-      public Builder lxcConf(List> lxcConf) {
-         this.lxcConf = lxcConf;
-         return this;
-      }
-
-      public Builder portBindings(Map>> portBindings) {
-         this.portBindings = portBindings;
-         return this;
-      }
-
-      public Builder publishAllPorts(boolean publishAllPorts) {
-         this.publishAllPorts = publishAllPorts;
-         return this;
-      }
-
-      public Builder privileged(boolean privileged) {
-         this.privileged = privileged;
-         return this;
-      }
-
-      public Builder dns(List  dns) {
-         this.dns = dns;
-         return this;
-      }
-
-      public Builder dnsSearch(List dnsSearch) {
-         this.dnsSearch = dnsSearch;
-         return this;
-      }
-
-      public Builder volumesFrom(List volumesFrom) {
-         this.volumesFrom = volumesFrom;
-         return this;
-      }
-
-      public Builder capAdd(List capAdd) {
-         this.capAdd = capAdd;
-         return this;
-      }
-
-      public Builder capDrop(List capDrop) {
-         this.capDrop = capDrop;
-         return this;
-      }
-
-      public Builder restartPolicy(Map restartPolicy) {
-         this.restartPolicy = restartPolicy;
-         return this;
-      }
-
       public Config build() {
          return Config.create(hostname, domainname, user, memory, memorySwap, cpuShares, attachStdin, attachStdout,
                  attachStderr, tty, openStdin, stdinOnce, env, cmd, entrypoint, image, volumes, workingDir,
-                 networkDisabled, exposedPorts, securityOpts, hostConfig, binds, links, lxcConf, portBindings,
-                 publishAllPorts, privileged, dns, dnsSearch, volumesFrom, capAdd, capDrop, restartPolicy);
+                 networkDisabled, exposedPorts, securityOpts, hostConfig);
       }
 
       public Builder fromConfig(Config in) {
@@ -360,11 +257,8 @@ public abstract class Config {
                  .attachStdout(in.attachStdout()).attachStderr(in.attachStderr()).tty(in.tty())
                  .openStdin(in.openStdin()).stdinOnce(in.stdinOnce()).env(in.env()).cmd(in.cmd())
                  .entrypoint(in.entrypoint()).image(in.image()).volumes(in.volumes()).workingDir(in.workingDir())
-                 .networkDisabled(in.networkDisabled()).exposedPorts(in.exposedPorts()).securityOpts(in.securityOpts())
-                 .hostConfig(in.hostConfig()).binds(in.binds()).links(in.links()).lxcConf(in.lxcConf())
-                 .portBindings(in.portBindings()).publishAllPorts(in.publishAllPorts()).privileged(in.privileged())
-                 .dns(in.dns()).dnsSearch(in.dnsSearch()).volumesFrom(in.volumesFrom()).capAdd(in.capAdd())
-                 .capDrop(in.capDrop()).restartPolicy(in.restartPolicy());
+                 .networkDisabled(in.networkDisabled()).exposedPorts(in.exposedPorts())
+                 .securityOpts(in.securityOpts()).hostConfig(in.hostConfig());
       }
 
    }
diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/HostConfig.java b/apis/docker/src/main/java/org/jclouds/docker/domain/HostConfig.java
index 1ebdb2cbe0..b4ee4f3477 100644
--- a/apis/docker/src/main/java/org/jclouds/docker/domain/HostConfig.java
+++ b/apis/docker/src/main/java/org/jclouds/docker/domain/HostConfig.java
@@ -55,16 +55,29 @@ public abstract class HostConfig {
 
    @Nullable public abstract String networkMode();
 
+   @Nullable public abstract List securityOpt();
+
+   @Nullable public abstract List capAdd();
+
+   @Nullable public abstract List capDrop();
+
+   public abstract Map restartPolicy();
+
+
+
    HostConfig() {
    }
 
    @SerializedNames({ "ContainerIDFile", "Binds", "LxcConf", "Privileged", "Dns", "DnsSearch", "PortBindings",
-         "Links", "ExtraHosts", "PublishAllPorts", "VolumesFrom", "NetworkMode" })
+         "Links", "ExtraHosts", "PublishAllPorts", "VolumesFrom", "NetworkMode", "SecurityOpt",
+         "CapAdd", "CapDrop", "RestartPolicy" })
    public static HostConfig create(String containerIDFile, List binds, List> lxcConf,
          boolean privileged, List dns, List dnsSearch, Map>> portBindings,
-         List links, List extraHosts, boolean publishAllPorts, List volumesFrom, String networkMode) {
+         List links, List extraHosts, boolean publishAllPorts, List volumesFrom, String networkMode, 
+         List securityOpt, List capAdd, List capDrop, Map restartPolicy) {
       return new AutoValue_HostConfig(containerIDFile, copyWithNullOf(binds), copyOf(lxcConf), privileged, copyWithNullOf(dns), copyWithNullOf(dnsSearch),
-            copyOf(portBindings), copyWithNullOf(links), copyWithNullOf(extraHosts), publishAllPorts, copyWithNullOf(volumesFrom), networkMode);
+            copyOf(portBindings), copyWithNullOf(links), copyWithNullOf(extraHosts), publishAllPorts, copyWithNullOf(volumesFrom), networkMode,
+            copyOf(securityOpt), copyWithNullOf(capAdd), copyWithNullOf(capDrop), copyOf(restartPolicy));
    }
 
    public static Builder builder() {
@@ -89,7 +102,11 @@ public abstract class HostConfig {
       private boolean publishAllPorts;
       private List volumesFrom;
       private String networkMode;
-
+      private List securityOpt = Lists.newArrayList();
+      private List capAdd;
+      private List capDrop;
+      private Map restartPolicy = Maps.newHashMap();
+      
       public Builder containerIDFile(String containerIDFile) {
          this.containerIDFile = containerIDFile;
          return this;
@@ -150,16 +167,37 @@ public abstract class HostConfig {
          return this;
       }
 
+      public Builder securityOpt(List securityOpt) {
+         this.securityOpt = securityOpt;
+         return this;
+      }
+
+      public Builder capAdd(List capAdd) {
+         this.capAdd = capAdd;
+         return this;
+      }
+
+      public Builder capDrop(List capDrop) {
+         this.capDrop = capDrop;
+         return this;
+      }
+
+      public Builder restartPolicy(Map restartPolicy) {
+         this.restartPolicy = restartPolicy;
+         return this;
+      }
+      
       public HostConfig build() {
          return HostConfig.create(containerIDFile, binds, lxcConf, privileged, dns, dnsSearch, portBindings, links,
-               extraHosts, publishAllPorts, volumesFrom, networkMode);
+               extraHosts, publishAllPorts, volumesFrom, networkMode, securityOpt, capAdd, capDrop, restartPolicy);
       }
 
       public Builder fromHostConfig(HostConfig in) {
          return this.containerIDFile(in.containerIDFile()).binds(in.binds()).lxcConf(in.lxcConf())
                .privileged(in.privileged()).dns(in.dns()).dnsSearch(in.dnsSearch()).links(in.links())
                .extraHosts(in.extraHosts()).portBindings(in.portBindings()).publishAllPorts(in.publishAllPorts())
-               .volumesFrom(in.volumesFrom()).networkMode(in.networkMode());
+               .volumesFrom(in.volumesFrom()).networkMode(in.networkMode()).securityOpt(in.securityOpt())
+               .capAdd(in.capAdd()).capDrop(in.capDrop()).restartPolicy(in.restartPolicy());
       }
    }
 }
diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/Info.java b/apis/docker/src/main/java/org/jclouds/docker/domain/Info.java
index e2b51ca630..6a071c3486 100644
--- a/apis/docker/src/main/java/org/jclouds/docker/domain/Info.java
+++ b/apis/docker/src/main/java/org/jclouds/docker/domain/Info.java
@@ -42,9 +42,9 @@ public abstract class Info {
 
    public abstract String indexServerAddress();
 
-   public abstract String initPath();
+   @Nullable public abstract String initPath();
 
-   public abstract String initSha1();
+   @Nullable public abstract String initSha1();
 
    public abstract String kernelVersion();
 
diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/NetworkSettings.java b/apis/docker/src/main/java/org/jclouds/docker/domain/NetworkSettings.java
index ca2772916c..2217992171 100644
--- a/apis/docker/src/main/java/org/jclouds/docker/domain/NetworkSettings.java
+++ b/apis/docker/src/main/java/org/jclouds/docker/domain/NetworkSettings.java
@@ -16,16 +16,16 @@
  */
 package org.jclouds.docker.domain;
 
-import static com.google.common.base.Preconditions.checkNotNull;
 import static org.jclouds.docker.internal.NullSafeCopies.copyOf;
+
 import java.util.List;
 import java.util.Map;
 
+import org.jclouds.docker.internal.NullSafeCopies;
 import org.jclouds.javax.annotation.Nullable;
 import org.jclouds.json.SerializedNames;
 
 import com.google.auto.value.AutoValue;
-import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 
@@ -135,7 +135,7 @@ public abstract class NetworkSettings {
       private String gateway;
       private String bridge;
       private String portMapping;
-      private Map>> ports = ImmutableMap.of();
+      private Map>> ports;
       private String sandboxId;
       private boolean hairpinMode;
       private String linkLocalIPv6Address;
@@ -176,7 +176,7 @@ public abstract class NetworkSettings {
       }
 
       public Builder ports(Map>> ports) {
-         this.ports = ImmutableMap.copyOf(checkNotNull(ports, "ports"));
+         this.ports = NullSafeCopies.copyWithNullOf(ports);
          return this;
       }
 
diff --git a/apis/docker/src/test/java/org/jclouds/docker/domain/ConfigTest.java b/apis/docker/src/test/java/org/jclouds/docker/domain/ConfigTest.java
index 0eef24d38c..6ba9ac5852 100644
--- a/apis/docker/src/test/java/org/jclouds/docker/domain/ConfigTest.java
+++ b/apis/docker/src/test/java/org/jclouds/docker/domain/ConfigTest.java
@@ -58,8 +58,5 @@ public class ConfigTest {
       assertThat(config.domainname()).isNull();
       assertThat(config.workingDir()).isNull();
       assertThat(config.hostConfig()).isNull();
-      assertThat(config.dns()).isNull();
-      assertThat(config.dnsSearch()).isNull();
-      assertThat(config.volumesFrom()).isNull();
    }
 }
diff --git a/apis/docker/src/test/java/org/jclouds/docker/features/ContainerApiLiveTest.java b/apis/docker/src/test/java/org/jclouds/docker/features/ContainerApiLiveTest.java
index 1b23b07b83..6c024d6775 100644
--- a/apis/docker/src/test/java/org/jclouds/docker/features/ContainerApiLiveTest.java
+++ b/apis/docker/src/test/java/org/jclouds/docker/features/ContainerApiLiveTest.java
@@ -85,7 +85,10 @@ public class ContainerApiLiveTest extends BaseDockerApiLiveTest {
    }
 
    @Test(dependsOnMethods = "testStartContainer")
-   public void testAttachContainer() {
+   public void testAttachContainer() throws InterruptedException {
+      // wait 2 seconds - give a container chance to echo the string
+      Thread.sleep(2000L);
+      
       InputStream attachStream = api().attach(container.id(), AttachOptions.Builder.logs(true).stream(false).stdout(true));
       String stream = consumeStream(attachStream);
       assertThat(stream.trim()).contains("hello world");
diff --git a/apis/docker/src/test/java/org/jclouds/docker/parse/ContainerParseTest.java b/apis/docker/src/test/java/org/jclouds/docker/parse/ContainerParseTest.java
index 42e94be7a6..53ef7dcf81 100644
--- a/apis/docker/src/test/java/org/jclouds/docker/parse/ContainerParseTest.java
+++ b/apis/docker/src/test/java/org/jclouds/docker/parse/ContainerParseTest.java
@@ -111,10 +111,13 @@ public class ContainerParseTest extends BaseDockerParseTest {
                                       "6783/tcp", ImmutableList.>of(ImmutableMap.of("HostIp", "", "HostPort", "6783")),
                                       "6783/udp", ImmutableList.>of(ImmutableMap.of("HostIp", "", "HostPort", "6783")))
                       )
+                      .capAdd(ImmutableList.of("NET_ADMIN"))
+                      .capDrop(ImmutableList.of("MKNOD"))
                       .dns(ImmutableList.of("8.8.8.8", "8.8.4.4"))
                       .extraHosts(ImmutableList.of("extra:169.254.0.1"))
                       .privileged(true)
                       .networkMode("bridge")
+                      .restartPolicy(ImmutableMap. of("MaximumRetryCount", "0", "Name", ""))
                       .build())
               .driver("aufs")
               .execDriver("native-0.2")
diff --git a/apis/docker/src/test/java/org/jclouds/docker/parse/ContainerVersionMajor1Minor21.java b/apis/docker/src/test/java/org/jclouds/docker/parse/ContainerVersionMajor1Minor21.java
new file mode 100644
index 0000000000..2b990e14c7
--- /dev/null
+++ b/apis/docker/src/test/java/org/jclouds/docker/parse/ContainerVersionMajor1Minor21.java
@@ -0,0 +1,263 @@
+/*
+ * 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.parse;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.core.MediaType;
+
+import org.jclouds.date.internal.SimpleDateFormatDateService;
+import org.jclouds.docker.domain.Config;
+import org.jclouds.docker.domain.Container;
+import org.jclouds.docker.domain.HostConfig;
+import org.jclouds.docker.domain.NetworkSettings;
+import org.jclouds.docker.domain.State;
+import org.jclouds.docker.internal.BaseDockerParseTest;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+/**
+ * This class tests Containers and Config parsing for Docker API in version 1.21. The input JSON comes from examples in 
+ * Docker Remote API documentation 1.21.
+ * 

+ * Two modifications were made in the "/container-1.21-create.json" due to incompatible types + *

    + *
  • the Entrypoint field value was changed from String to List
  • + *
  • the LxcConf field value was changed from Map to List
  • + *
      + */ +public class ContainerVersionMajor1Minor21 { + + @Test(groups = "unit") + public static class CreateTest extends BaseDockerParseTest { + @Override + public String resource() { + return "/container-1.21-create.json"; + } + + @Override + @Consumes(MediaType.APPLICATION_JSON) + public Config expected() { + return Config.builder() + .hostname("") + .domainname("") + .user("") + .attachStdin(false) + .attachStdout(true) + .attachStderr(true) + .tty(false) + .openStdin(false) + .stdinOnce(false) + .env(ImmutableList.of("FOO=bar", "BAZ=quux")) + .cmd(ImmutableList.of("date")) + //original value of the "Entrypoint" in JSON doesn't contain List but String! + //Both types are allowed by docker Remote API, but we are not able to parse both. + .entrypoint(ImmutableList.of("")) + .image("ubuntu") +// "Labels": { +// "com.example.vendor": "Acme", +// "com.example.license": "GPL", +// "com.example.version": "1.0" +// }, + .volumes(ImmutableMap.of("/volumes/data", ImmutableMap.of())) + .workingDir("") + .networkDisabled(false) +// "MacAddress": "12:34:56:78:9a:bc", + .exposedPorts(ImmutableMap.of("22/tcp", ImmutableMap.of())) +// "StopSignal": "SIGTERM", + .hostConfig(HostConfig.builder() + .binds(ImmutableList.of("/tmp:/tmp")) + .links(ImmutableList.of("redis3:redis")) + //The LxcConf causes the type mismatch too (Map vs List) + .lxcConf(ImmutableList.> of( + ImmutableMap. of("lxc.utsname", "docker"))) +// "Memory": 0, +// "MemorySwap": 0, +// "MemoryReservation": 0, +// "KernelMemory": 0, +// "CpuShares": 512, +// "CpuPeriod": 100000, +// "CpuQuota": 50000, +// "CpusetCpus": "0,1", +// "CpusetMems": "0,1", +// "BlkioWeight": 300, +// "MemorySwappiness": 60, +// "OomKillDisable": false, + .portBindings(ImmutableMap.>> of( + "22/tcp", ImmutableList.> of(ImmutableMap.of("HostPort", "11022")))) + .publishAllPorts(false) + .privileged(false) +// "ReadonlyRootfs": false, + .dns(ImmutableList.of("8.8.8.8")) +// "DnsOptions": [""], + .dnsSearch(ImmutableList.of("")) + .extraHosts(null) + .volumesFrom(ImmutableList.of("parent", "other:ro")) + .capAdd(ImmutableList.of("NET_ADMIN")) + .capDrop(ImmutableList.of("MKNOD")) +// "GroupAdd": ["newgroup"], + .restartPolicy(ImmutableMap.of("Name", "", "MaximumRetryCount", "0")) + .networkMode("bridge") +// "Devices": [], +// "Ulimits": [{}], +// "LogConfig": { "Type": "json-file", "Config": {} }, + .securityOpt(ImmutableList.of()) +// "CgroupParent": "", +// "VolumeDriver": "" + .build() + ) + .build(); + } + } + + @Test(groups = "unit") + public static class InspectTest extends BaseDockerParseTest { + @Override + public String resource() { + return "/container-1.21-inspect.json"; + } + + @Override + @Consumes(MediaType.APPLICATION_JSON) + public Container expected() { + return Container.builder() +// "AppArmorProfile": "", + .args(ImmutableList.of("-c", "exit 9")) + .config(Config.builder() + .attachStderr(true) + .attachStdin(false) + .attachStdout(true) + .cmd(ImmutableList. of("/bin/sh", "-c", "exit 9")) + .domainname("") + .entrypoint(null) + .env(ImmutableList. of("PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin")) + .exposedPorts(null) + .hostname("ba033ac44011") + .image("ubuntu") +// "Labels": { +// "com.example.vendor": "Acme", +// "com.example.license": "GPL", +// "com.example.version": "1.0" +// }, +// "MacAddress": "", + .networkDisabled(false) +// "OnBuild": null, + .openStdin(false) + .stdinOnce(false) + .tty(false) + .user("") + .volumes(null) + .workingDir("") +// "StopSignal": "SIGTERM" + .build()) + .created(new SimpleDateFormatDateService().iso8601DateParse("2015-01-06T15:47:31.485331387Z")) + .driver("devicemapper") + .execDriver("native-0.2") +// "ExecIDs": null, + .hostConfig(HostConfig.builder() + .binds(null) +// "BlkioWeight": 0, + .capAdd(null) + .capDrop(null) + .containerIDFile("") +// "CpusetCpus": "", +// "CpusetMems": "", +// "CpuShares": 0, +// "CpuPeriod": 100000, +// "Devices": [], + .dns(null) +// "DnsOptions": null, + .dnsSearch(null) + .extraHosts(null) +// "IpcMode": "", + .links(null) + .lxcConf(ImmutableList.> of()) +// "Memory": 0, +// "MemorySwap": 0, +// "MemoryReservation": 0, +// "KernelMemory": 0, +// "OomKillDisable": false, + .networkMode("bridge") + .portBindings(ImmutableMap.>> of()) + .privileged(false) +// "ReadonlyRootfs": false, + .publishAllPorts(false) + .restartPolicy(ImmutableMap. of("MaximumRetryCount", "2", "Name", "on-failure")) +// "LogConfig": { +// "Config": null, +// "Type": "json-file" +// }, + .securityOpt(null) + .volumesFrom(null) +// "Ulimits": [{}], +// "VolumeDriver": "" + .build()) + .hostnamePath("/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/hostname") + .hostsPath("/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/hosts") +// "LogPath": "/var/lib/docker/containers/1eb5fabf5a03807136561b3c00adcd2992b535d624d5e18b6cdc6a6844d9767b/1eb5fabf5a03807136561b3c00adcd2992b535d624d5e18b6cdc6a6844d9767b-json.log", + .id("ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39") + .image("04c5d3b7b0656168630d3ba35d8889bd0e9caafcaeb3004d2bfbc47e7c5d35d2") + .mountLabel("") + .name("/boring_euclid") + .networkSettings(NetworkSettings.builder() + .bridge("") + .sandboxId("") + .hairpinMode(false) + .linkLocalIPv6Address("") + .linkLocalIPv6PrefixLen(0) + .ports(null) + .sandboxKey("") + .secondaryIPAddresses(null) + .secondaryIPv6Addresses(null) + .endpointId("") + .gateway("") + .globalIPv6Address("") + .globalIPv6PrefixLen(0) + .ipAddress("") + .ipPrefixLen(0) + .ipv6Gateway("") + .macAddress("") + .networks(ImmutableMap. of( + "bridge", NetworkSettings.Details.create("", "", "", 0, "", "", 0, ""))) + .build()) + .path("/bin/sh") + .processLabel("") + .resolvConfPath("/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/resolv.conf") +// "RestartCount": 1, + .state(State.create(0, true, 9, "2015-01-06T15:47:32.072697474Z", "2015-01-06T15:47:32.080254511Z", false, false, "running", false, + // We don't have the "Dead" field in this API version! + false, + "" +// "Paused": false, +// "Running": true, + )) +// "Mounts": [ +// { +// "Source": "/data", +// "Destination": "/data", +// "Mode": "ro,Z", +// "RW": false +// } + .build(); + } + } + +} diff --git a/apis/docker/src/test/java/org/jclouds/docker/parse/Info2ParseTest.java b/apis/docker/src/test/java/org/jclouds/docker/parse/Info2ParseTest.java new file mode 100644 index 0000000000..949acc7aa8 --- /dev/null +++ b/apis/docker/src/test/java/org/jclouds/docker/parse/Info2ParseTest.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.parse; + +import java.util.List; + +import javax.ws.rs.Consumes; +import javax.ws.rs.core.MediaType; + +import org.jclouds.docker.domain.Info; +import org.jclouds.docker.internal.BaseDockerParseTest; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableList; + +@Test(groups = "unit") +public class Info2ParseTest extends BaseDockerParseTest { + + @Override + public String resource() { + return "/info2.json"; + } + + @Override + @Consumes(MediaType.APPLICATION_JSON) + public Info expected() { + return Info.create( + 0, // containers + false, // debug + "aufs", // driver + ImmutableList.>of( + ImmutableList.of("Root Dir", "/var/lib/docker/aufs"), + ImmutableList.of("Backing Filesystem", "extfs"), + ImmutableList.of("Dirs", "117"), + ImmutableList.of( "Dirperm1 Supported", "true") + ), // driverStatus + "", // ExecutionDriver + true, // IPv4Forwarding + 39, // Images + "https://index.docker.io/v1/", // IndexServerAddress + null, // InitPath + null, // InitSha1 + "4.4.0-22-generic", // KernelVersion + true, // MemoryLimit + 0, // NEventsListener + 33, // NFd + 83, // NGoroutines + "Ubuntu 16.04 LTS", // OperatingSystem + false, // SwapLimit + "/var/lib/docker", // DockerRootDir + null, // Labels + 8248356864L, // MemTotal + 4, // NCPU + "KFWR:PMVY:BEWE:TD52:5WEU:NXF4:I6S3:WDIE:GCRD:L3YA:VWC4:ZRYZ", // ID + "test" // name + ); + } +} diff --git a/apis/docker/src/test/resources/container-1.21-create.json b/apis/docker/src/test/resources/container-1.21-create.json new file mode 100644 index 0000000000..4998569640 --- /dev/null +++ b/apis/docker/src/test/resources/container-1.21-create.json @@ -0,0 +1,72 @@ +{ + "Hostname": "", + "Domainname": "", + "User": "", + "AttachStdin": false, + "AttachStdout": true, + "AttachStderr": true, + "Tty": false, + "OpenStdin": false, + "StdinOnce": false, + "Env": [ + "FOO=bar", + "BAZ=quux" + ], + "Cmd": [ + "date" + ], + "Entrypoint": [""], + "Image": "ubuntu", + "Labels": { + "com.example.vendor": "Acme", + "com.example.license": "GPL", + "com.example.version": "1.0" + }, + "Volumes": { + "/volumes/data": {} + }, + "WorkingDir": "", + "NetworkDisabled": false, + "MacAddress": "12:34:56:78:9a:bc", + "ExposedPorts": { + "22/tcp": {} + }, + "StopSignal": "SIGTERM", + "HostConfig": { + "Binds": ["/tmp:/tmp"], + "Links": ["redis3:redis"], + "LxcConf": [{"lxc.utsname":"docker"}], + "Memory": 0, + "MemorySwap": 0, + "MemoryReservation": 0, + "KernelMemory": 0, + "CpuShares": 512, + "CpuPeriod": 100000, + "CpuQuota": 50000, + "CpusetCpus": "0,1", + "CpusetMems": "0,1", + "BlkioWeight": 300, + "MemorySwappiness": 60, + "OomKillDisable": false, + "PortBindings": { "22/tcp": [{ "HostPort": "11022" }] }, + "PublishAllPorts": false, + "Privileged": false, + "ReadonlyRootfs": false, + "Dns": ["8.8.8.8"], + "DnsOptions": [""], + "DnsSearch": [""], + "ExtraHosts": null, + "VolumesFrom": ["parent", "other:ro"], + "CapAdd": ["NET_ADMIN"], + "CapDrop": ["MKNOD"], + "GroupAdd": ["newgroup"], + "RestartPolicy": { "Name": "", "MaximumRetryCount": 0 }, + "NetworkMode": "bridge", + "Devices": [], + "Ulimits": [{}], + "LogConfig": { "Type": "json-file", "Config": {} }, + "SecurityOpt": [], + "CgroupParent": "", + "VolumeDriver": "" + } + } \ No newline at end of file diff --git a/apis/docker/src/test/resources/container-1.21-inspect.json b/apis/docker/src/test/resources/container-1.21-inspect.json new file mode 100644 index 0000000000..ade2851cbe --- /dev/null +++ b/apis/docker/src/test/resources/container-1.21-inspect.json @@ -0,0 +1,147 @@ +{ + "AppArmorProfile": "", + "Args": [ + "-c", + "exit 9" + ], + "Config": { + "AttachStderr": true, + "AttachStdin": false, + "AttachStdout": true, + "Cmd": [ + "/bin/sh", + "-c", + "exit 9" + ], + "Domainname": "", + "Entrypoint": null, + "Env": [ + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + ], + "ExposedPorts": null, + "Hostname": "ba033ac44011", + "Image": "ubuntu", + "Labels": { + "com.example.vendor": "Acme", + "com.example.license": "GPL", + "com.example.version": "1.0" + }, + "MacAddress": "", + "NetworkDisabled": false, + "OnBuild": null, + "OpenStdin": false, + "StdinOnce": false, + "Tty": false, + "User": "", + "Volumes": null, + "WorkingDir": "", + "StopSignal": "SIGTERM" + }, + "Created": "2015-01-06T15:47:31.485331387Z", + "Driver": "devicemapper", + "ExecDriver": "native-0.2", + "ExecIDs": null, + "HostConfig": { + "Binds": null, + "BlkioWeight": 0, + "CapAdd": null, + "CapDrop": null, + "ContainerIDFile": "", + "CpusetCpus": "", + "CpusetMems": "", + "CpuShares": 0, + "CpuPeriod": 100000, + "Devices": [], + "Dns": null, + "DnsOptions": null, + "DnsSearch": null, + "ExtraHosts": null, + "IpcMode": "", + "Links": null, + "LxcConf": [], + "Memory": 0, + "MemorySwap": 0, + "MemoryReservation": 0, + "KernelMemory": 0, + "OomKillDisable": false, + "NetworkMode": "bridge", + "PortBindings": {}, + "Privileged": false, + "ReadonlyRootfs": false, + "PublishAllPorts": false, + "RestartPolicy": { + "MaximumRetryCount": 2, + "Name": "on-failure" + }, + "LogConfig": { + "Config": null, + "Type": "json-file" + }, + "SecurityOpt": null, + "VolumesFrom": null, + "Ulimits": [{}], + "VolumeDriver": "" + }, + "HostnamePath": "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/hostname", + "HostsPath": "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/hosts", + "LogPath": "/var/lib/docker/containers/1eb5fabf5a03807136561b3c00adcd2992b535d624d5e18b6cdc6a6844d9767b/1eb5fabf5a03807136561b3c00adcd2992b535d624d5e18b6cdc6a6844d9767b-json.log", + "Id": "ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39", + "Image": "04c5d3b7b0656168630d3ba35d8889bd0e9caafcaeb3004d2bfbc47e7c5d35d2", + "MountLabel": "", + "Name": "/boring_euclid", + "NetworkSettings": { + "Bridge": "", + "SandboxID": "", + "HairpinMode": false, + "LinkLocalIPv6Address": "", + "LinkLocalIPv6PrefixLen": 0, + "Ports": null, + "SandboxKey": "", + "SecondaryIPAddresses": null, + "SecondaryIPv6Addresses": null, + "EndpointID": "", + "Gateway": "", + "GlobalIPv6Address": "", + "GlobalIPv6PrefixLen": 0, + "IPAddress": "", + "IPPrefixLen": 0, + "IPv6Gateway": "", + "MacAddress": "", + "Networks": { + "bridge": { + "EndpointID": "", + "Gateway": "", + "IPAddress": "", + "IPPrefixLen": 0, + "IPv6Gateway": "", + "GlobalIPv6Address": "", + "GlobalIPv6PrefixLen": 0, + "MacAddress": "" + } + } + }, + "Path": "/bin/sh", + "ProcessLabel": "", + "ResolvConfPath": "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/resolv.conf", + "RestartCount": 1, + "State": { + "Error": "", + "ExitCode": 9, + "FinishedAt": "2015-01-06T15:47:32.080254511Z", + "OOMKilled": false, + "Paused": false, + "Pid": 0, + "Restarting": false, + "Running": true, + "StartedAt": "2015-01-06T15:47:32.072697474Z", + "Status": "running" + }, + "Mounts": [ + { + "Source": "/data", + "Destination": "/data", + "Mode": "ro,Z", + "RW": false + } + ] +} \ No newline at end of file diff --git a/apis/docker/src/test/resources/container.json b/apis/docker/src/test/resources/container.json index fa1b61fd86..bc18e270d1 100644 --- a/apis/docker/src/test/resources/container.json +++ b/apis/docker/src/test/resources/container.json @@ -40,7 +40,7 @@ "OnBuild": null, "OpenStdin": false, "PortSpecs": null, - "SecurityOpt": null, + "SecurityOpts": null, "StdinOnce": false, "Tty": false, "User": "", @@ -52,8 +52,8 @@ "ExecDriver": "native-0.2", "HostConfig": { "Binds": null, - "CapAdd": null, - "CapDrop": null, + "CapAdd": ["NET_ADMIN"], + "CapDrop": ["MKNOD"], "ContainerIDFile": "", "Devices": [], "Dns": [ @@ -137,6 +137,7 @@ "Path": "/home/weave/weaver", "ProcessLabel": "", "ResolvConfPath": "/var/lib/docker/containers/6c9932f478bd761f32ddb54ed28ab42ab6fac6f2a279f561ea31503ee9d39524/resolv.conf", + "SecurityOpt": [], "State": { "Status": "running", "Running": true, diff --git a/apis/docker/src/test/resources/info2.json b/apis/docker/src/test/resources/info2.json new file mode 100644 index 0000000000..11a13d5860 --- /dev/null +++ b/apis/docker/src/test/resources/info2.json @@ -0,0 +1,90 @@ +{ + "Architecture": "x86_64", + "BridgeNfIp6tables": true, + "BridgeNfIptables": true, + "CPUSet": true, + "CPUShares": true, + "CgroupDriver": "cgroupfs", + "ClusterAdvertise": "", + "ClusterStore": "", + "Containers": 0, + "ContainersPaused": 0, + "ContainersRunning": 0, + "ContainersStopped": 0, + "CpuCfsPeriod": true, + "CpuCfsQuota": true, + "Debug": false, + "DockerRootDir": "/var/lib/docker", + "Driver": "aufs", + "DriverStatus": [ + [ + "Root Dir", + "/var/lib/docker/aufs" + ], + [ + "Backing Filesystem", + "extfs" + ], + [ + "Dirs", + "117" + ], + [ + "Dirperm1 Supported", + "true" + ] + ], + "ExecutionDriver": "", + "ExperimentalBuild": false, + "HttpProxy": "", + "HttpsProxy": "", + "ID": "KFWR:PMVY:BEWE:TD52:5WEU:NXF4:I6S3:WDIE:GCRD:L3YA:VWC4:ZRYZ", + "IPv4Forwarding": true, + "Images": 39, + "IndexServerAddress": "https://index.docker.io/v1/", + "KernelMemory": true, + "KernelVersion": "4.4.0-22-generic", + "Labels": null, + "LoggingDriver": "json-file", + "MemTotal": 8248356864, + "MemoryLimit": true, + "NCPU": 4, + "NEventsListener": 0, + "NFd": 33, + "NGoroutines": 83, + "Name": "test", + "NoProxy": "", + "OSType": "linux", + "OomKillDisable": true, + "OperatingSystem": "Ubuntu 16.04 LTS", + "Plugins": { + "Authorization": null, + "Network": [ + "bridge", + "null", + "host" + ], + "Volume": [ + "local" + ] + }, + "RegistryConfig": { + "IndexConfigs": { + "docker.io": { + "Mirrors": null, + "Name": "docker.io", + "Official": true, + "Secure": true + } + }, + "InsecureRegistryCIDRs": [ + "0.0.0.0/0", + "127.0.0.0/8" + ], + "Mirrors": null + }, + "ServerVersion": "1.11.1", + "SwapLimit": false, + "SystemStatus": null, + "SystemTime": "2016-05-24T15:20:15.407969411+02:00" +} From ebc8f7568f17df207f3fa2111c2789920c437b5d Mon Sep 17 00:00:00 2001 From: Duncan Grant Date: Thu, 26 May 2016 10:34:13 +0100 Subject: [PATCH 69/81] Use container host ip not manager ip When providing node ip in metadata jclouds was using the ip of the docker manager which was fine for docker machine but fails when using a swarm cluster. This uses the ip returned from docker. --- .../functions/ContainerToNodeMetadata.java | 24 ++++--- .../org/jclouds/docker/domain/Container.java | 18 ++++-- .../java/org/jclouds/docker/domain/Node.java | 63 +++++++++++++++++++ .../ContainerToNodeMetadataTest.java | 1 + .../docker/internal/DockerTestUtils.java | 3 +- 5 files changed, 94 insertions(+), 15 deletions(-) create mode 100644 apis/docker/src/main/java/org/jclouds/docker/domain/Node.java diff --git a/apis/docker/src/main/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadata.java b/apis/docker/src/main/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadata.java index db75e50d42..d2133a6d5d 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadata.java +++ b/apis/docker/src/main/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadata.java @@ -36,6 +36,7 @@ import org.jclouds.domain.Location; import org.jclouds.providers.ProviderMetadata; import com.google.common.base.Function; +import com.google.common.base.Strings; import com.google.common.base.Supplier; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; @@ -61,9 +62,9 @@ public class ContainerToNodeMetadata implements Function toPortableStatus, GroupNamingConvention.Factory namingConvention, - Supplier> images, @Memoized Supplier> locations, - LoginPortForContainer loginPortForContainer) { + Function toPortableStatus, GroupNamingConvention.Factory namingConvention, + Supplier> images, @Memoized Supplier> locations, + LoginPortForContainer loginPortForContainer) { this.providerMetadata = providerMetadata; this.toPortableStatus = toPortableStatus; this.nodeNamingConvention = namingConvention.createWithoutPrefix(); @@ -81,7 +82,7 @@ public class ContainerToNodeMetadata implements Function getPublicIpAddresses() { - String dockerIpAddress = URI.create(providerMetadata.getEndpoint()).getHost(); + private List getPublicIpAddresses(Container container) { + String dockerIpAddress; + if (container.node() != null && !Strings.isNullOrEmpty(container.node().ip())) { + dockerIpAddress = container.node().ip(); + } else { + dockerIpAddress = URI.create(providerMetadata.getEndpoint()).getHost(); + } return ImmutableList.of(dockerIpAddress); } diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/Container.java b/apis/docker/src/main/java/org/jclouds/docker/domain/Container.java index e784351b40..b53cdee287 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/Container.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/Container.java @@ -77,6 +77,8 @@ public abstract class Container { @Nullable public abstract String processLabel(); + @Nullable public abstract Node node(); + Container() { } @@ -84,17 +86,17 @@ public abstract class Container { { "Id", "Created", "Path", "Name", "Args", "Config", "State", "Image", "NetworkSettings", "SysInitPath", "ResolvConfPath", "Volumes", "HostConfig", "Driver", "ExecDriver", "VolumesRW", "Command", "Status", - "Ports", "HostnamePath", "HostsPath", "MountLabel", "ProcessLabel" + "Ports", "HostnamePath", "HostsPath", "MountLabel", "ProcessLabel", "Node" }) public static Container create(String id, Date created, String path, String name, List args, Config config, State state, String image, NetworkSettings networkSettings, String sysInitPath, String resolvConfPath, Map volumes, HostConfig hostConfig, String driver, String execDriver, Map volumesRW, String command, String status, List ports, String hostnamePath, String hostsPath, - String mountLabel, String processLabel) { + String mountLabel, String processLabel, Node node) { return new AutoValue_Container(id, created, path, name, copyOf(args), config, state, image, networkSettings, sysInitPath, resolvConfPath, copyOf(volumes), hostConfig, driver, execDriver, copyOf(volumesRW), command, - status, copyOf(ports), hostnamePath, hostsPath, mountLabel, processLabel); + status, copyOf(ports), hostnamePath, hostsPath, mountLabel, processLabel, node); } public static Builder builder() { @@ -130,6 +132,7 @@ public abstract class Container { private String hostsPath; private String mountLabel; private String processLabel; + private Node node; public Builder id(String id) { this.id = id; @@ -246,10 +249,15 @@ public abstract class Container { return this; } + public Builder node(Node node) { + this.node = node; + return this; + } + public Container build() { return Container.create(id, created, path, name, args, config, state, image, networkSettings, sysInitPath, resolvConfPath, volumes, hostConfig, driver, execDriver, volumesRW, command, status, - ports, hostnamePath, hostsPath, mountLabel, processLabel); + ports, hostnamePath, hostsPath, mountLabel, processLabel, node); } public Builder fromContainer(Container in) { @@ -258,7 +266,7 @@ public abstract class Container { .sysInitPath(in.sysInitPath()).resolvConfPath(in.resolvConfPath()).driver(in.driver()) .execDriver(in.execDriver()).volumes(in.volumes()).hostConfig(in.hostConfig()).volumesRW(in.volumesRW()) .command(in.command()).status(in.status()).ports(in.ports()).hostnamePath(in.hostnamePath()) - .hostsPath(in.hostsPath()).mountLabel(in.mountLabel()).processLabel(in.processLabel()); + .hostsPath(in.hostsPath()).mountLabel(in.mountLabel()).processLabel(in.processLabel()).node(in.node()); } } } diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/Node.java b/apis/docker/src/main/java/org/jclouds/docker/domain/Node.java new file mode 100644 index 0000000000..343835a3cc --- /dev/null +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/Node.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.javax.annotation.Nullable; +import org.jclouds.json.SerializedNames; + +import com.google.auto.value.AutoValue; + +@AutoValue +public abstract class Node { + + Node() { + } + + @SerializedNames({"IP"}) + public static Node create(String ip) { + return new AutoValue_Node(ip); + } + + public static Builder builder() { + return new Builder(); + } + + @Nullable + public abstract String ip(); + + public Builder toBuilder() { + return builder().fromNode(this); + } + + public static final class Builder { + + private String ip; + + public Builder ip(String ip) { + this.ip = ip; + return this; + } + + public Node build() { + return Node.create(this.ip); + } + + public Builder fromNode(Node in) { + return this.ip(in.ip()); + } + } +} diff --git a/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadataTest.java b/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadataTest.java index d9fc464e29..53ba6277e7 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadataTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadataTest.java @@ -203,6 +203,7 @@ public class ContainerToNodeMetadataTest { expect(mockContainer.name()).andReturn(container.name()); expect(mockContainer.config()).andReturn(container.config()).anyTimes(); expect(mockContainer.networkSettings()).andReturn(container.networkSettings()).anyTimes(); + expect(mockContainer.node()).andReturn(container.node()).anyTimes(); expect(mockContainer.state()).andReturn(container.state()); expect(mockContainer.image()).andReturn(container.image()).anyTimes(); replay(mockContainer); diff --git a/apis/docker/src/test/java/org/jclouds/docker/internal/DockerTestUtils.java b/apis/docker/src/test/java/org/jclouds/docker/internal/DockerTestUtils.java index 102684af93..aa136d703f 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/internal/DockerTestUtils.java +++ b/apis/docker/src/test/java/org/jclouds/docker/internal/DockerTestUtils.java @@ -23,6 +23,7 @@ import java.util.logging.Level; import org.jclouds.docker.DockerApi; import org.jclouds.docker.features.ImageApi; +import org.jclouds.docker.options.DeleteImageOptions; import com.google.common.base.Preconditions; @@ -72,7 +73,7 @@ public class DockerTestUtils { Preconditions.checkNotNull(imageName, "Docker image name has to be provided"); final ImageApi imageApi = dockerApi.getImageApi(); if (null != imageApi.inspectImage(imageName)) { - consumeStreamSilently(imageApi.deleteImage(imageName)); + consumeStreamSilently(imageApi.deleteImage(imageName, DeleteImageOptions.Builder.force(true))); } } } From 91339b200ee19b38dfea217191da315415d38b74 Mon Sep 17 00:00:00 2001 From: Duncan Grant Date: Thu, 9 Jun 2016 09:04:56 +0100 Subject: [PATCH 70/81] Node json should be optional The Node element of the container json is optional as well as nullable. --- .../functions/ContainerToNodeMetadata.java | 5 +-- .../org/jclouds/docker/domain/Container.java | 11 +++--- .../ContainerToNodeMetadataTest.java | 1 + .../jclouds/docker/domain/ContainerTest.java | 37 +++++++++++++++++++ .../docker/parse/ContainerParseTest.java | 3 ++ .../parse/ContainerVersionMajor1Minor21.java | 1 + apis/docker/src/test/resources/container.json | 3 ++ 7 files changed, 53 insertions(+), 8 deletions(-) create mode 100644 apis/docker/src/test/java/org/jclouds/docker/domain/ContainerTest.java diff --git a/apis/docker/src/main/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadata.java b/apis/docker/src/main/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadata.java index d2133a6d5d..53b70f126e 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadata.java +++ b/apis/docker/src/main/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadata.java @@ -36,7 +36,6 @@ import org.jclouds.domain.Location; import org.jclouds.providers.ProviderMetadata; import com.google.common.base.Function; -import com.google.common.base.Strings; import com.google.common.base.Supplier; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; @@ -113,8 +112,8 @@ public class ContainerToNodeMetadata implements Function getPublicIpAddresses(Container container) { String dockerIpAddress; - if (container.node() != null && !Strings.isNullOrEmpty(container.node().ip())) { - dockerIpAddress = container.node().ip(); + if (container.node().isPresent()) { + dockerIpAddress = container.node().get().ip(); } else { dockerIpAddress = URI.create(providerMetadata.getEndpoint()).getHost(); } diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/Container.java b/apis/docker/src/main/java/org/jclouds/docker/domain/Container.java index b53cdee287..d0c10c9a82 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/Container.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/Container.java @@ -26,6 +26,7 @@ import org.jclouds.javax.annotation.Nullable; import org.jclouds.json.SerializedNames; import com.google.auto.value.AutoValue; +import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; @@ -77,7 +78,7 @@ public abstract class Container { @Nullable public abstract String processLabel(); - @Nullable public abstract Node node(); + public abstract Optional node(); Container() { } @@ -93,7 +94,7 @@ public abstract class Container { String resolvConfPath, Map volumes, HostConfig hostConfig, String driver, String execDriver, Map volumesRW, String command, String status, List ports, String hostnamePath, String hostsPath, - String mountLabel, String processLabel, Node node) { + String mountLabel, String processLabel, Optional node) { return new AutoValue_Container(id, created, path, name, copyOf(args), config, state, image, networkSettings, sysInitPath, resolvConfPath, copyOf(volumes), hostConfig, driver, execDriver, copyOf(volumesRW), command, status, copyOf(ports), hostnamePath, hostsPath, mountLabel, processLabel, node); @@ -132,7 +133,7 @@ public abstract class Container { private String hostsPath; private String mountLabel; private String processLabel; - private Node node; + private Optional node = Optional.absent(); public Builder id(String id) { this.id = id; @@ -250,7 +251,7 @@ public abstract class Container { } public Builder node(Node node) { - this.node = node; + this.node = Optional.fromNullable(node); return this; } @@ -266,7 +267,7 @@ public abstract class Container { .sysInitPath(in.sysInitPath()).resolvConfPath(in.resolvConfPath()).driver(in.driver()) .execDriver(in.execDriver()).volumes(in.volumes()).hostConfig(in.hostConfig()).volumesRW(in.volumesRW()) .command(in.command()).status(in.status()).ports(in.ports()).hostnamePath(in.hostnamePath()) - .hostsPath(in.hostsPath()).mountLabel(in.mountLabel()).processLabel(in.processLabel()).node(in.node()); + .hostsPath(in.hostsPath()).mountLabel(in.mountLabel()).processLabel(in.processLabel()).node(in.node().orNull()); } } } diff --git a/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadataTest.java b/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadataTest.java index 53ba6277e7..7588174671 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadataTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadataTest.java @@ -128,6 +128,7 @@ public class ContainerToNodeMetadataTest { .status("") .hostConfig(HostConfig.builder().publishAllPorts(true).build()) .ports(ImmutableList.of()) + .node(null) .build(); ProviderMetadata providerMetadata = EasyMock.createMock(ProviderMetadata.class); expect(providerMetadata.getEndpoint()).andReturn("http://127.0.0.1:4243"); diff --git a/apis/docker/src/test/java/org/jclouds/docker/domain/ContainerTest.java b/apis/docker/src/test/java/org/jclouds/docker/domain/ContainerTest.java new file mode 100644 index 0000000000..7be6e3c233 --- /dev/null +++ b/apis/docker/src/test/java/org/jclouds/docker/domain/ContainerTest.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.domain; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.testng.annotations.Test; + +@Test(groups = "unit", testName = "ContainerTest") +public class ContainerTest { + + @Test + public void testFromContainer() { + Container testContainer = Container.builder() + .id("testcontainer") + .build(); + + + Container newTestContainer = testContainer.toBuilder().build(); + assertThat(newTestContainer).isEqualTo(testContainer); + } +} + diff --git a/apis/docker/src/test/java/org/jclouds/docker/parse/ContainerParseTest.java b/apis/docker/src/test/java/org/jclouds/docker/parse/ContainerParseTest.java index 53ef7dcf81..884733ea79 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/parse/ContainerParseTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/parse/ContainerParseTest.java @@ -27,6 +27,7 @@ import org.jclouds.docker.domain.Config; import org.jclouds.docker.domain.Container; import org.jclouds.docker.domain.HostConfig; import org.jclouds.docker.domain.NetworkSettings; +import org.jclouds.docker.domain.Node; import org.jclouds.docker.domain.State; import org.jclouds.docker.internal.BaseDockerParseTest; import org.testng.annotations.Test; @@ -125,6 +126,8 @@ public class ContainerParseTest extends BaseDockerParseTest { .hostsPath("/var/lib/docker/containers/6c9932f478bd761f32ddb54ed28ab42ab6fac6f2a279f561ea31503ee9d39524/hosts") .mountLabel("") .processLabel("") + .node(Node.builder(). + ip("10.10.10.10").build()) .build(); } } diff --git a/apis/docker/src/test/java/org/jclouds/docker/parse/ContainerVersionMajor1Minor21.java b/apis/docker/src/test/java/org/jclouds/docker/parse/ContainerVersionMajor1Minor21.java index 2b990e14c7..8bca8c83cf 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/parse/ContainerVersionMajor1Minor21.java +++ b/apis/docker/src/test/java/org/jclouds/docker/parse/ContainerVersionMajor1Minor21.java @@ -239,6 +239,7 @@ public class ContainerVersionMajor1Minor21 { "bridge", NetworkSettings.Details.create("", "", "", 0, "", "", 0, ""))) .build()) .path("/bin/sh") + .node(null) .processLabel("") .resolvConfPath("/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/resolv.conf") // "RestartCount": 1, diff --git a/apis/docker/src/test/resources/container.json b/apis/docker/src/test/resources/container.json index bc18e270d1..37c5db4fd8 100644 --- a/apis/docker/src/test/resources/container.json +++ b/apis/docker/src/test/resources/container.json @@ -134,6 +134,9 @@ } } }, + "Node": { + "IP": "10.10.10.10" + }, "Path": "/home/weave/weaver", "ProcessLabel": "", "ResolvConfPath": "/var/lib/docker/containers/6c9932f478bd761f32ddb54ed28ab42ab6fac6f2a279f561ea31503ee9d39524/resolv.conf", From fc88756d5a29052a1cc42b260225b3827f2b5f16 Mon Sep 17 00:00:00 2001 From: Aled Sage Date: Mon, 13 Jun 2016 14:03:26 +0100 Subject: [PATCH 71/81] =?UTF-8?q?docker:=20get=20container=E2=80=99s=20IPs?= =?UTF-8?q?=20from=20all=20networks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../functions/ContainerToNodeMetadata.java | 20 ++++++- .../docker/domain/NetworkSettings.java | 28 +++++++++- .../ContainerToNodeMetadataTest.java | 55 +++++++++++++++++-- .../docker/parse/ContainerParseTest.java | 3 +- 4 files changed, 97 insertions(+), 9 deletions(-) diff --git a/apis/docker/src/main/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadata.java b/apis/docker/src/main/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadata.java index 53b70f126e..c3b58312ac 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadata.java +++ b/apis/docker/src/main/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadata.java @@ -31,6 +31,7 @@ import org.jclouds.compute.domain.NodeMetadataBuilder; import org.jclouds.compute.domain.Processor; import org.jclouds.compute.functions.GroupNamingConvention; import org.jclouds.docker.domain.Container; +import org.jclouds.docker.domain.NetworkSettings; import org.jclouds.docker.domain.State; import org.jclouds.domain.Location; import org.jclouds.providers.ProviderMetadata; @@ -38,6 +39,7 @@ import org.jclouds.providers.ProviderMetadata; import com.google.common.base.Function; import com.google.common.base.Supplier; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.inject.Singleton; @@ -106,8 +108,24 @@ public class ContainerToNodeMetadata implements Function getPrivateIpAddresses(Container container) { + // A container can be attached to multiple networks. It can therefore have multiple private + // IPs. The NetworkSettings.ipAddress might in fact be blank, with the only IP being on + // network objects. if (container.networkSettings() == null) return ImmutableList.of(); - return ImmutableList.of(container.networkSettings().ipAddress()); + ImmutableSet.Builder builder = ImmutableSet.builder(); + NetworkSettings settings = container.networkSettings(); + if (settings.ipAddress() != null && settings.ipAddress().length() > 0) { + builder.add(settings.ipAddress()); + } + if (settings.networks() != null) { + for (Map.Entry entry : settings.networks().entrySet()) { + String ipAddress = entry.getValue().ipAddress(); + if (ipAddress != null && ipAddress.length() > 0) { + builder.add(ipAddress); + } + } + } + return builder.build(); } private List getPublicIpAddresses(Container container) { diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/NetworkSettings.java b/apis/docker/src/main/java/org/jclouds/docker/domain/NetworkSettings.java index 2217992171..b8c1a832a9 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/NetworkSettings.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/NetworkSettings.java @@ -56,8 +56,32 @@ public abstract class NetworkSettings { @SerializedNames({ "EndpointID", "Gateway", "IPAddress", "IPPrefixLen", "IPv6Gateway", "GlobalIPv6Address", "GlobalIPv6PrefixLen", "MacAddress" }) public static Details create(String endpointId, String gateway, String ipAddress, int ipPrefixLen, String ipv6Gateway, String globalIPv6Address, int globalIPv6PrefixLen, String macAddress) { - return new AutoValue_NetworkSettings_Details(endpointId, gateway, ipAddress, ipPrefixLen, ipv6Gateway, globalIPv6Address, globalIPv6PrefixLen, - macAddress); + return builder().endpoint(endpointId).gateway(gateway).ipAddress(ipAddress).ipPrefixLen(ipPrefixLen) + .ipv6Gateway(ipv6Gateway).globalIPv6Address(globalIPv6Address) + .globalIPv6PrefixLen(globalIPv6PrefixLen).macAddress(macAddress) + .build(); + } + + public Builder toBuilder() { + return new AutoValue_NetworkSettings_Details.Builder(this); + } + + public static Builder builder() { + return new AutoValue_NetworkSettings_Details.Builder(); + } + + @AutoValue.Builder + public abstract static class Builder { + public abstract Builder endpoint(String value); + public abstract Builder gateway(String value); + public abstract Builder ipAddress(String value); + public abstract Builder ipPrefixLen(int value); + public abstract Builder ipv6Gateway(String value); + public abstract Builder globalIPv6Address(String value); + public abstract Builder globalIPv6PrefixLen(int value); + public abstract Builder macAddress(String value); + + public abstract Details build(); } } diff --git a/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadataTest.java b/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadataTest.java index 7588174671..c439e47617 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadataTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadataTest.java @@ -131,7 +131,7 @@ public class ContainerToNodeMetadataTest { .node(null) .build(); ProviderMetadata providerMetadata = EasyMock.createMock(ProviderMetadata.class); - expect(providerMetadata.getEndpoint()).andReturn("http://127.0.0.1:4243"); + expect(providerMetadata.getEndpoint()).andReturn("http://127.0.0.1:4243").atLeastOnce(); replay(providerMetadata); GroupNamingConvention.Factory namingConvention = Guice.createInjector().getInstance(GroupNamingConvention.Factory.class); @@ -177,7 +177,7 @@ public class ContainerToNodeMetadataTest { private Function toPortableStatus() { StateToStatus function = EasyMock.createMock(StateToStatus.class); - expect(function.apply(anyObject(State.class))).andReturn(NodeMetadata.Status.RUNNING); + expect(function.apply(anyObject(State.class))).andReturn(NodeMetadata.Status.RUNNING).atLeastOnce(); replay(function); return function; } @@ -193,11 +193,58 @@ public class ContainerToNodeMetadataTest { assertEquals(node.getGroup(), "hopeful_mclean"); assertEquals(node.getImageId(), "af0f59f1c19eef9471c3b8c8d587c39b8f130560b54f3766931b37d76d5de4b6"); assertEquals(node.getLoginPort(), 49199); - assertEquals(node.getPrivateAddresses().size(), 1); - assertEquals(node.getPublicAddresses().size(), 1); + assertEquals(node.getPrivateAddresses(), ImmutableSet.of("172.17.0.2")); + assertEquals(node.getPublicAddresses(), ImmutableSet.of("127.0.0.1")); + } + + public void testVirtualMachineWithNetworksToNodeMetadata() { + // Example networks taken from container.json + Container containerWithNetwork = container.toBuilder() + .networkSettings(container.networkSettings().toBuilder() + .networks(ImmutableMap.builder() + .put("JCLOUDS_NETWORK", NetworkSettings.Details.builder() + .endpoint("1a10519f808faf1181cfdf3d1d6dd93e19ede2d1c8fed82562a4c17c297c4db3") + .gateway("172.19.0.1") + .ipAddress("172.19.0.2") + .ipPrefixLen(16) + .ipv6Gateway("") + .globalIPv6Address("") + .globalIPv6PrefixLen(0) + .macAddress("02:42:ac:13:00:02") + .build()) + .put("bridge", NetworkSettings.Details.builder() + .endpoint("9e8dcc0c8288938a923018fee0728cee8e6de7c01a5150738ee6e51c1caf8cf6") + .gateway("172.17.0.1") + .ipAddress("172.17.0.2") + .ipPrefixLen(16) + .ipv6Gateway("") + .globalIPv6Address("") + .globalIPv6PrefixLen(0) + .macAddress("02:42:ac:11:00:02") + .build()) + .build()) + .build()) + .build(); + + Container mockContainer = mockContainer(containerWithNetwork); + + NodeMetadata node = function.apply(mockContainer); + + verify(mockContainer); + + assertEquals(node.getId(), "6d35806c1bd2b25cd92bba2d2c2c5169dc2156f53ab45c2b62d76e2d2fee14a9"); + assertEquals(node.getGroup(), "hopeful_mclean"); + assertEquals(node.getImageId(), "af0f59f1c19eef9471c3b8c8d587c39b8f130560b54f3766931b37d76d5de4b6"); + assertEquals(node.getLoginPort(), 49199); + assertEquals(node.getPrivateAddresses(), ImmutableSet.of("172.17.0.2", "172.19.0.2")); + assertEquals(node.getPublicAddresses(), ImmutableSet.of("127.0.0.1")); } private Container mockContainer() { + return mockContainer(container); + } + + private Container mockContainer(Container container) { Container mockContainer = EasyMock.createMock(Container.class); expect(mockContainer.id()).andReturn(container.id()); diff --git a/apis/docker/src/test/java/org/jclouds/docker/parse/ContainerParseTest.java b/apis/docker/src/test/java/org/jclouds/docker/parse/ContainerParseTest.java index 884733ea79..291fa13232 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/parse/ContainerParseTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/parse/ContainerParseTest.java @@ -55,12 +55,11 @@ public class ContainerParseTest extends BaseDockerParseTest { .config(Config.builder() .hostname("6c9932f478bd") .env(ImmutableList.of("PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin")) - .image("57e570db16baba1e8c0d6f3c15868ddb400f64ff76ec948e65c3ca3f15fb3587") + .image("zettio/weave") .domainname("") .user("") .cmd(ImmutableList.of("-name", "7a:63:a2:39:7b:0f")) .entrypoint(ImmutableList.of("/home/weave/weaver", "-iface", "ethwe", "-wait", "5")) - .image("zettio/weave") .workingDir("/home/weave") .exposedPorts(ImmutableMap.of("6783/tcp", ImmutableMap.of(), "6783/udp", ImmutableMap.of())) .build()) From 730a4cfe87fc37f62461d9d652009f1f5e51cc84 Mon Sep 17 00:00:00 2001 From: Aled Sage Date: Mon, 13 Jun 2016 14:13:34 +0100 Subject: [PATCH 72/81] Docker.ContainerToNodeMetadata: remove needless mocking Previously it mocked the container and the StateToStatus. It just asserted that getter methods were called, rather than confirming the resulting NodeMetadata had the right values. By removing some of the mocks, it not only simplifies the code but improves code-coverage. (e.g. if StateToStatus was being passed the wrong value, the previous test would not have noticed). --- .../ContainerToNodeMetadataTest.java | 49 +++---------------- 1 file changed, 6 insertions(+), 43 deletions(-) diff --git a/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadataTest.java b/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadataTest.java index c439e47617..5360097172 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadataTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/compute/functions/ContainerToNodeMetadataTest.java @@ -16,10 +16,8 @@ */ package org.jclouds.docker.compute.functions; -import static org.easymock.EasyMock.anyObject; import static org.easymock.EasyMock.expect; import static org.easymock.EasyMock.replay; -import static org.easymock.EasyMock.verify; import static org.testng.Assert.assertEquals; import java.util.Arrays; @@ -48,7 +46,6 @@ import org.jclouds.providers.ProviderMetadata; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; -import com.google.common.base.Function; import com.google.common.base.Supplier; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; @@ -171,28 +168,19 @@ public class ContainerToNodeMetadataTest { } }; - function = new ContainerToNodeMetadata(providerMetadata, toPortableStatus(), namingConvention, images, locations, + function = new ContainerToNodeMetadata(providerMetadata, new StateToStatus(), namingConvention, images, locations, new LoginPortForContainer.LoginPortLookupChain(null)); } - private Function toPortableStatus() { - StateToStatus function = EasyMock.createMock(StateToStatus.class); - expect(function.apply(anyObject(State.class))).andReturn(NodeMetadata.Status.RUNNING).atLeastOnce(); - replay(function); - return function; - } - public void testVirtualMachineToNodeMetadata() { - Container mockContainer = mockContainer(); - - NodeMetadata node = function.apply(mockContainer); - - verify(mockContainer); + NodeMetadata node = function.apply(container); assertEquals(node.getId(), "6d35806c1bd2b25cd92bba2d2c2c5169dc2156f53ab45c2b62d76e2d2fee14a9"); assertEquals(node.getGroup(), "hopeful_mclean"); assertEquals(node.getImageId(), "af0f59f1c19eef9471c3b8c8d587c39b8f130560b54f3766931b37d76d5de4b6"); assertEquals(node.getLoginPort(), 49199); + assertEquals(node.getStatus(), NodeMetadata.Status.RUNNING); + assertEquals(node.getImageId(), "af0f59f1c19eef9471c3b8c8d587c39b8f130560b54f3766931b37d76d5de4b6"); assertEquals(node.getPrivateAddresses(), ImmutableSet.of("172.17.0.2")); assertEquals(node.getPublicAddresses(), ImmutableSet.of("127.0.0.1")); } @@ -226,36 +214,11 @@ public class ContainerToNodeMetadataTest { .build()) .build(); - Container mockContainer = mockContainer(containerWithNetwork); + NodeMetadata node = function.apply(containerWithNetwork); - NodeMetadata node = function.apply(mockContainer); - - verify(mockContainer); - - assertEquals(node.getId(), "6d35806c1bd2b25cd92bba2d2c2c5169dc2156f53ab45c2b62d76e2d2fee14a9"); - assertEquals(node.getGroup(), "hopeful_mclean"); - assertEquals(node.getImageId(), "af0f59f1c19eef9471c3b8c8d587c39b8f130560b54f3766931b37d76d5de4b6"); + // Only asserting network-related aspects; the rest is covered by testVirtualMachineToNodeMetadata assertEquals(node.getLoginPort(), 49199); assertEquals(node.getPrivateAddresses(), ImmutableSet.of("172.17.0.2", "172.19.0.2")); assertEquals(node.getPublicAddresses(), ImmutableSet.of("127.0.0.1")); } - - private Container mockContainer() { - return mockContainer(container); - } - - private Container mockContainer(Container container) { - Container mockContainer = EasyMock.createMock(Container.class); - - expect(mockContainer.id()).andReturn(container.id()); - expect(mockContainer.name()).andReturn(container.name()); - expect(mockContainer.config()).andReturn(container.config()).anyTimes(); - expect(mockContainer.networkSettings()).andReturn(container.networkSettings()).anyTimes(); - expect(mockContainer.node()).andReturn(container.node()).anyTimes(); - expect(mockContainer.state()).andReturn(container.state()); - expect(mockContainer.image()).andReturn(container.image()).anyTimes(); - replay(mockContainer); - - return mockContainer; - } } From 496fbae646f6538be492db35d1dd54514aec5c55 Mon Sep 17 00:00:00 2001 From: Andrew Donald Kennedy Date: Thu, 16 Jun 2016 20:02:14 +0100 Subject: [PATCH 73/81] Fix incorrect key format check in Docker --- .../java/org/jclouds/docker/suppliers/SSLContextBuilder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apis/docker/src/main/java/org/jclouds/docker/suppliers/SSLContextBuilder.java b/apis/docker/src/main/java/org/jclouds/docker/suppliers/SSLContextBuilder.java index cc1025b744..2933f7890a 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/suppliers/SSLContextBuilder.java +++ b/apis/docker/src/main/java/org/jclouds/docker/suppliers/SSLContextBuilder.java @@ -59,7 +59,7 @@ public class SSLContextBuilder { private TrustManager[] trustManagers; public static final boolean isClientKeyAndCertificateData(String key, String cert) { - return (key.startsWith(Pems.PUBLIC_X509_MARKER) || key.startsWith(Pems.PUBLIC_PKCS1_MARKER)) && + return (key.startsWith(Pems.PRIVATE_PKCS1_MARKER) || key.startsWith(Pems.PRIVATE_PKCS8_MARKER)) && cert.startsWith(Pems.CERTIFICATE_X509_MARKER); } From 7a979ba87bc7de73d5ecc6a3b83b4b18366c99af Mon Sep 17 00:00:00 2001 From: Duncan Grant Date: Mon, 20 Jun 2016 16:07:42 +0100 Subject: [PATCH 74/81] ImageId can be image name or id and will pull from hub --- .../strategy/DockerComputeServiceAdapter.java | 28 ++++++++-- .../DockerComputeServiceAdapterLiveTest.java | 52 ++++++++++++++----- .../compute/DockerComputeServiceLiveTest.java | 8 +-- 3 files changed, 64 insertions(+), 24 deletions(-) diff --git a/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java b/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java index 3c88f3153e..9ce63e5de7 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java +++ b/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java @@ -51,6 +51,7 @@ import org.jclouds.docker.domain.ContainerSummary; import org.jclouds.docker.domain.HostConfig; import org.jclouds.docker.domain.Image; import org.jclouds.docker.domain.ImageSummary; +import org.jclouds.docker.options.CreateImageOptions; import org.jclouds.docker.options.ListContainerOptions; import org.jclouds.docker.options.RemoveContainerOptions; import org.jclouds.domain.Location; @@ -237,13 +238,32 @@ public class DockerComputeServiceAdapter implements } @Override - public Image getImage(final String imageId) { - // less efficient than just inspectImage but listImages return repoTags - return find(listImages(), new Predicate() { + public Image getImage(final String imageIdOrName) { + checkNotNull(imageIdOrName); + if (imageIdOrName.startsWith("sha256")) { + // less efficient than just inspectImage but listImages return repoTags + return find(listImages(), new Predicate() { + @Override + public boolean apply(Image input) { + // Only attempt match on id as we should try to pull again anyway if using name + return input.id().equals(imageIdOrName); + } + }, null); + } + // Image is not cached or getting image by name so try to pull it + api.getImageApi().createImage(CreateImageOptions.Builder.fromImage(imageIdOrName)); + + // as above this ensure repotags are returned + return find(listImages(), new Predicate() { @Override public boolean apply(Image input) { - return input.id().equals(imageId); + for (String tag : input.repoTags()) { + if (tag.equals(imageIdOrName) || tag.equals(imageIdOrName + ":latest")) { + return true; + } + } + return false; } }, null); } diff --git a/apis/docker/src/test/java/org/jclouds/docker/compute/DockerComputeServiceAdapterLiveTest.java b/apis/docker/src/test/java/org/jclouds/docker/compute/DockerComputeServiceAdapterLiveTest.java index 022177691f..3480d882a7 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/compute/DockerComputeServiceAdapterLiveTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/compute/DockerComputeServiceAdapterLiveTest.java @@ -19,10 +19,18 @@ package org.jclouds.docker.compute; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertNull; import java.util.Properties; import java.util.Random; +import com.google.common.base.Predicate; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; +import com.google.inject.Injector; +import com.google.inject.Module; + import org.jclouds.compute.ComputeServiceAdapter.NodeAndInitialCredentials; import org.jclouds.compute.domain.Hardware; import org.jclouds.compute.domain.Template; @@ -32,18 +40,11 @@ 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.sshj.config.SshjSshClientModule; import org.testng.annotations.AfterClass; 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; -import com.google.inject.Module; - @Test(groups = "live", singleThreaded = true, testName = "DockerComputeServiceAdapterLiveTest") public class DockerComputeServiceAdapterLiveTest extends BaseDockerApiLiveTest { @@ -54,17 +55,13 @@ public class DockerComputeServiceAdapterLiveTest extends BaseDockerApiLiveTest { private DockerComputeServiceAdapter adapter; private TemplateBuilder templateBuilder; private NodeAndInitialCredentials guest; + private static final String CHUANWEN_COWSAY = "chuanwen/cowsay"; @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); + defaultImage = adapter.getImage(imageName); assertNotNull(defaultImage); } @@ -73,6 +70,9 @@ public class DockerComputeServiceAdapterLiveTest extends BaseDockerApiLiveTest { if (guest != null) { adapter.destroyNode(guest.getNode().id() + ""); } + if (api.getImageApi().inspectImage(CHUANWEN_COWSAY) != null) { + api.getImageApi().deleteImage(CHUANWEN_COWSAY); + } super.tearDown(); } @@ -105,6 +105,32 @@ public class DockerComputeServiceAdapterLiveTest extends BaseDockerApiLiveTest { } } + public void testGetImageNotHiddenByCache() { + + //Ensure image to be tested is unknown to jclouds and docker and that cache is warm + assertNull(findImageFromListImages(CHUANWEN_COWSAY)); + assertNull(api.getImageApi().inspectImage(CHUANWEN_COWSAY)); + + // Get new image + adapter.getImage(CHUANWEN_COWSAY); + + assertNotNull(findImageFromListImages(CHUANWEN_COWSAY), "New image is not available from listImages presumably due to caching"); + } + + private Image findImageFromListImages(final String image) { + return Iterables.find(adapter.listImages(), new Predicate() { + @Override + public boolean apply(Image input) { + for (String tag : input.repoTags()) { + if (tag.equals(image) || tag.equals(CHUANWEN_COWSAY + ":latest")) { + return true; + } + } + return false; + } + }, null); + } + @Override protected Iterable setupModules() { return ImmutableSet.of(getLoggingModule(), new SshjSshClientModule()); diff --git a/apis/docker/src/test/java/org/jclouds/docker/compute/DockerComputeServiceLiveTest.java b/apis/docker/src/test/java/org/jclouds/docker/compute/DockerComputeServiceLiveTest.java index 1e024e4cba..d02e215de6 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/compute/DockerComputeServiceLiveTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/compute/DockerComputeServiceLiveTest.java @@ -100,13 +100,7 @@ public class DockerComputeServiceLiveTest extends BaseComputeServiceContextLiveT client = view.getComputeService(); 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()); + defaultImage = client.getImage(imageName); assertNotNull(defaultImage); } From f2ce5679cc2dd61faf5d43484e0b638f5c496db5 Mon Sep 17 00:00:00 2001 From: Andrew Donald Kennedy Date: Sat, 2 Jul 2016 14:20:00 +0100 Subject: [PATCH 75/81] Connect container to list of networks in options --- .../compute/options/DockerTemplateOptions.java | 8 ++++++-- .../strategy/DockerComputeServiceAdapter.java | 13 ++++++++++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java b/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java index 1ff4b44b51..109d469500 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java +++ b/apis/docker/src/main/java/org/jclouds/docker/compute/options/DockerTemplateOptions.java @@ -164,7 +164,7 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable @Override public int hashCode() { return Objects.hashCode(super.hashCode(), volumes, hostname, dns, memory, entrypoint, commands, cpuShares, env, - portBindings, extraHosts, volumesFrom, privileged, openStdin, configBuilder); + portBindings, networkMode, extraHosts, volumesFrom, privileged, openStdin, configBuilder); } @Override @@ -264,7 +264,11 @@ public class DockerTemplateOptions extends TemplateOptions implements Cloneable } /** - * Sets the networking mode for the container. Supported values are: bridge, host, and container:[name|id] + * Sets the networking mode for the container. + *

      + * Supported values are: {@code bridge}, {@code none}, {@code host}, + * {@code networkname}, {@code networkid} or {@code container:[name|id]} + * * @param networkMode * @return this instance */ diff --git a/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java b/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java index 9ce63e5de7..8130838821 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java +++ b/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java @@ -32,6 +32,7 @@ import com.google.common.base.Predicate; 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.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; @@ -182,7 +183,6 @@ public class DockerComputeServiceAdapter implements hostConfigBuilder = HostConfig.builder().fromHostConfig(containerConfig.hostConfig()); hostConfigBuilder.portBindings(portBindings); containerConfigBuilder.hostConfig(hostConfigBuilder.build()); - } else { containerConfigBuilder.image(imageId); } @@ -193,9 +193,20 @@ public class DockerComputeServiceAdapter implements Container container = api.getContainerApi().createContainer(name, containerConfig); logger.trace("<< container(%s)", container.id()); + if (templateOptions.getNetworks() != null) { + logger.debug(">> connecting container(%s) to networks(%s)", container.id(), Iterables.toString(templateOptions.getNetworks())); + for (String networkIdOrName : templateOptions.getNetworks()) { + api.getNetworkApi().connectContainerToNetwork(networkIdOrName, container.id()); + } + logger.trace("<< connected(%s)", container.id()); + } + HostConfig hostConfig = containerConfig.hostConfig(); + logger.debug(">> starting container(%s) with hostConfig(%s)", container.id(), hostConfig); api.getContainerApi().startContainer(container.id(), hostConfig); + logger.trace("<< started(%s)", container.id()); + container = api.getContainerApi().inspectContainer(container.id()); if (container.state().exitCode() != 0) { destroyNode(container.id()); From 6408c3a8353d80ca19fccc3d0bf4078e31dffa57 Mon Sep 17 00:00:00 2001 From: Andrew Donald Kennedy Date: Sat, 2 Jul 2016 15:39:49 +0100 Subject: [PATCH 76/81] Add Network autovalue builder and tests --- .../org/jclouds/docker/domain/Network.java | 152 +++++++++++++++--- .../DockerComputeServiceAdapterLiveTest.java | 39 ++++- 2 files changed, 161 insertions(+), 30 deletions(-) diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/Network.java b/apis/docker/src/main/java/org/jclouds/docker/domain/Network.java index 74f9e816e7..d6de0d73b4 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/Network.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/Network.java @@ -17,48 +17,91 @@ package org.jclouds.docker.domain; import static org.jclouds.docker.internal.NullSafeCopies.copyOf; +import static org.jclouds.docker.internal.NullSafeCopies.copyWithNullOf; + import java.util.List; import java.util.Map; +import com.google.auto.value.AutoValue; + import org.jclouds.javax.annotation.Nullable; import org.jclouds.json.SerializedNames; -import com.google.auto.value.AutoValue; - @AutoValue public abstract class Network { @AutoValue public abstract static class IPAM { - IPAM() {} // For AutoValue only! + IPAM() { } - @Nullable - public abstract String driver(); + @Nullable public abstract String driver(); public abstract List config(); @SerializedNames({"Driver", "Config"}) - public static IPAM create(String driver, List config) { - return new AutoValue_Network_IPAM(driver, copyOf(config)); + public static IPAM create(@Nullable String driver, List config) { + return builder() + .driver(driver) + .config(config) + .build(); + } + + public static Builder builder() { + return new AutoValue_Network_IPAM.Builder(); + } + + @AutoValue.Builder + public abstract static class Builder { + + public abstract Builder driver(@Nullable String driver); + + public abstract Builder config(List config); + + abstract List config(); + + abstract IPAM autoBuild(); + + public IPAM build() { + return config(copyOf(config())) + .autoBuild(); + } } @AutoValue public abstract static class Config { - Config() {} // For AutoValue only! + Config() { } public abstract String subnet(); - @Nullable - public abstract String ipRange(); + @Nullable public abstract String ipRange(); - @Nullable - public abstract String gateway(); + @Nullable public abstract String gateway(); @SerializedNames({"Subnet", "IPRange", "Gateway"}) - public static Config create(String subnet, String ipRange, String gateway) { - return new AutoValue_Network_IPAM_Config(subnet, ipRange, gateway); + public static Config create(String subnet, @Nullable String ipRange, @Nullable String gateway) { + return builder() + .subnet(subnet) + .ipRange(ipRange) + .gateway(gateway) + .build(); + } + + public static Builder builder() { + return new AutoValue_Network_IPAM_Config.Builder(); + } + + @AutoValue.Builder + public abstract static class Builder { + + public abstract Builder subnet(String subnet); + + public abstract Builder ipRange(@Nullable String ipRange); + + public abstract Builder gateway(@Nullable String gateway); + + abstract Config build(); } } } @@ -66,7 +109,7 @@ public abstract class Network { @AutoValue public abstract static class Details { - Details() {} // For AutoValue only! + Details() { } public abstract String endpoint(); @@ -78,7 +121,30 @@ public abstract class Network { @SerializedNames({ "EndpointID", "MacAddress", "IPv4Address", "IPv6Address" }) public static Details create(String endpoint, String macAddress, String ipv4address, String ipv6address) { - return new AutoValue_Network_Details(endpoint, macAddress, ipv4address, ipv6address); + return builder() + .endpoint(endpoint) + .macAddress(macAddress) + .ipv4address(ipv4address) + .ipv6address(ipv6address) + .build(); + } + + public static Builder builder() { + return new AutoValue_Network_Details.Builder(); + } + + @AutoValue.Builder + public abstract static class Builder { + + public abstract Builder endpoint(String endpoint); + + public abstract Builder macAddress(String macAddress); + + public abstract Builder ipv4address(String ipv4address); + + public abstract Builder ipv6address(String ipv6address); + + abstract Details build(); } } @@ -92,16 +158,58 @@ public abstract class Network { @Nullable public abstract IPAM ipam(); - public abstract Map containers(); + @Nullable public abstract Map containers(); - public abstract Map options(); + @Nullable public abstract Map options(); - Network() {} + Network() { } @SerializedNames({ "Name", "Id", "Scope", "Driver", "IPAM", "Containers", "Options" }) - public static Network create(String name, String id, String scope, String driver, IPAM ipam, - Map containers, Map options) { - return new AutoValue_Network(name, id, scope, driver, ipam, copyOf(containers), copyOf(options)); + public static Network create(@Nullable String name, @Nullable String id, @Nullable String scope, + @Nullable String driver, @Nullable IPAM ipam, @Nullable Map containers, + @Nullable Map options) { + return builder() + .name(name) + .id(id) + .scope(scope) + .driver(driver) + .ipam(ipam) + .containers(containers) + .options(options) + .build(); } + public static Builder builder() { + return new AutoValue_Network.Builder(); + } + + @AutoValue.Builder + public abstract static class Builder { + + public abstract Builder name(@Nullable String name); + + public abstract Builder id(@Nullable String id); + + public abstract Builder scope(@Nullable String scope); + + public abstract Builder driver(@Nullable String driver); + + public abstract Builder ipam(@Nullable IPAM ipam); + + public abstract Builder containers(@Nullable Map containers); + + public abstract Builder options(@Nullable Map options); + + abstract Map containers(); + + abstract Map options(); + + abstract Network autoBuild(); + + public Network build() { + return containers(copyWithNullOf(containers())) + .options(copyOf(options())) + .autoBuild(); + } + } } diff --git a/apis/docker/src/test/java/org/jclouds/docker/compute/DockerComputeServiceAdapterLiveTest.java b/apis/docker/src/test/java/org/jclouds/docker/compute/DockerComputeServiceAdapterLiveTest.java index 3480d882a7..cb8ca3e683 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/compute/DockerComputeServiceAdapterLiveTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/compute/DockerComputeServiceAdapterLiveTest.java @@ -18,12 +18,17 @@ package org.jclouds.docker.compute; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertNull; import java.util.Properties; import java.util.Random; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + import com.google.common.base.Predicate; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; @@ -40,10 +45,8 @@ import org.jclouds.docker.compute.options.DockerTemplateOptions; import org.jclouds.docker.compute.strategy.DockerComputeServiceAdapter; import org.jclouds.docker.domain.Container; import org.jclouds.docker.domain.Image; +import org.jclouds.docker.domain.Network; import org.jclouds.sshj.config.SshjSshClientModule; -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; @Test(groups = "live", singleThreaded = true, testName = "DockerComputeServiceAdapterLiveTest") public class DockerComputeServiceAdapterLiveTest extends BaseDockerApiLiveTest { @@ -51,6 +54,8 @@ public class DockerComputeServiceAdapterLiveTest extends BaseDockerApiLiveTest { private static final String SSHABLE_IMAGE = "kwart/alpine-ext"; private static final String SSHABLE_IMAGE_TAG = "3.3-ssh"; private Image defaultImage; + private Network network1; + private Network network2; private DockerComputeServiceAdapter adapter; private TemplateBuilder templateBuilder; @@ -62,6 +67,8 @@ public class DockerComputeServiceAdapterLiveTest extends BaseDockerApiLiveTest { super.initialize(); String imageName = SSHABLE_IMAGE + ":" + SSHABLE_IMAGE_TAG; defaultImage = adapter.getImage(imageName); + network1 = api.getNetworkApi().createNetwork(Network.builder().name("network1").driver("overlay").build()); + network2 = api.getNetworkApi().createNetwork(Network.builder().name("network2").driver("overlay").build()); assertNotNull(defaultImage); } @@ -73,6 +80,12 @@ public class DockerComputeServiceAdapterLiveTest extends BaseDockerApiLiveTest { if (api.getImageApi().inspectImage(CHUANWEN_COWSAY) != null) { api.getImageApi().deleteImage(CHUANWEN_COWSAY); } + if (api.getNetworkApi().inspectNetwork("network1") != null) { + api.getNetworkApi().removeNetwork("network1"); + } + if (api.getNetworkApi().inspectNetwork("network2") != null) { + api.getNetworkApi().removeNetwork("network2"); + } super.tearDown(); } @@ -85,14 +98,11 @@ public class DockerComputeServiceAdapterLiveTest extends BaseDockerApiLiveTest { } public void testCreateNodeWithGroupEncodedIntoNameThenStoreCredentials() { - String group = "foo"; String name = "container" + new Random().nextInt(); - Template template = templateBuilder.imageId(defaultImage.id()).build(); - DockerTemplateOptions options = template.getOptions().as(DockerTemplateOptions.class); options.env(ImmutableList.of("ROOT_PASSWORD=password")); - guest = adapter.createNodeWithGroupEncodedIntoName(group, name, template); + guest = adapter.createNodeWithGroupEncodedIntoName("test", name, template); assertEquals(guest.getNodeId(), guest.getNode().id()); } @@ -106,7 +116,6 @@ public class DockerComputeServiceAdapterLiveTest extends BaseDockerApiLiveTest { } public void testGetImageNotHiddenByCache() { - //Ensure image to be tested is unknown to jclouds and docker and that cache is warm assertNull(findImageFromListImages(CHUANWEN_COWSAY)); assertNull(api.getImageApi().inspectImage(CHUANWEN_COWSAY)); @@ -117,6 +126,20 @@ public class DockerComputeServiceAdapterLiveTest extends BaseDockerApiLiveTest { assertNotNull(findImageFromListImages(CHUANWEN_COWSAY), "New image is not available from listImages presumably due to caching"); } + public void testCreateNodeWithMultipleNetworks() { + String name = "container" + new Random().nextInt(); + Template template = templateBuilder.imageId(defaultImage.id()).build(); + DockerTemplateOptions options = template.getOptions().as(DockerTemplateOptions.class); + options.env(ImmutableList.of("ROOT_PASSWORD=password")); + options.networkMode("bridge"); + options.networks(network1.name(), network2.name()); + guest = adapter.createNodeWithGroupEncodedIntoName("test", name, template); + + assertTrue(guest.getNode().networkSettings().networks().containsKey("network1")); + assertTrue(guest.getNode().networkSettings().networks().containsKey("network2")); + assertEquals(guest.getNode().networkSettings().secondaryIPAddresses().size(), 2); + } + private Image findImageFromListImages(final String image) { return Iterables.find(adapter.listImages(), new Predicate() { @Override From 9af449651de4a6b19c59029190cc0635df6f732f Mon Sep 17 00:00:00 2001 From: Josef Cacek Date: Thu, 11 Aug 2016 16:53:00 +0200 Subject: [PATCH 77/81] JCLOUDS-1153 Fix empty Docker repoTags field in ImageToImage function --- .../compute/functions/ImageToImage.java | 43 +++++++------- .../compute/functions/ImageToImageTest.java | 59 +++++++++++++++++++ 2 files changed, 82 insertions(+), 20 deletions(-) diff --git a/apis/docker/src/main/java/org/jclouds/docker/compute/functions/ImageToImage.java b/apis/docker/src/main/java/org/jclouds/docker/compute/functions/ImageToImage.java index 7e9de039b0..f030d9ac1b 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/compute/functions/ImageToImage.java +++ b/apis/docker/src/main/java/org/jclouds/docker/compute/functions/ImageToImage.java @@ -16,9 +16,10 @@ */ 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 static com.google.common.base.Preconditions.checkNotNull; + +import javax.annotation.Resource; +import javax.inject.Named; import org.jclouds.compute.domain.Image; import org.jclouds.compute.domain.ImageBuilder; @@ -27,11 +28,8 @@ import org.jclouds.compute.domain.OsFamily; import org.jclouds.compute.reference.ComputeServiceConstants; import org.jclouds.logging.Logger; -import javax.annotation.Resource; -import javax.inject.Named; - -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.collect.Iterables.get; +import com.google.common.base.Function; +import com.google.common.collect.Iterables; public class ImageToImage implements Function { @@ -46,13 +44,24 @@ public class ImageToImage implements Function"); + final int versionSeparatorPos = firstRepoTag.lastIndexOf(':'); - OsFamily osFamily = osFamily().apply(description); - String osVersion = parseVersion(description); + final String name; + final String osVersion; + if (versionSeparatorPos > -1) { + name = firstRepoTag.substring(0, versionSeparatorPos); + osVersion = firstRepoTag.substring(versionSeparatorPos + 1); + } else { + name = firstRepoTag; + osVersion = firstRepoTag; + } + logger.debug("os version for item: %s is %s", firstRepoTag, osVersion); + + OsFamily osFamily = osFamily().apply(firstRepoTag); OperatingSystem os = OperatingSystem.builder() - .description(description) + .description(firstRepoTag) .family(osFamily) .version(osVersion) .is64Bit(is64bit(from)) @@ -60,8 +69,8 @@ public class ImageToImage implements Function of() // repoTags + ); + + private static final org.jclouds.docker.domain.Image IMAGE_REPOTAG_WITH_PORT = org.jclouds.docker.domain.Image.create( + "id", // id + "author", + "comment", + Config.builder() + .image("imageId") + .build(), + Config.builder() + .image("imageId") + .build(), + "parent", // parent + new Date(), // created + "containerId", // container + "1.3.1", // dockerVersion + "x86_64", // architecture + "os", // os + 0l, // size + 0l, // virtualSize + ImmutableList.of("registry.company.example:8888/a/b/c/d:latest") // repoTags + ); + private ImageToImage function; private org.jclouds.docker.domain.Image image; @@ -76,6 +118,23 @@ public class ImageToImageTest { assertEquals(mockImage.id(), image.getId().toString()); } + public void testEmptyRepoTags() { + Image image = function.apply(IMAGE_EMPTY_REPOTAGS); + + assertEquals(image.getId(), "id"); + assertEquals(image.getDescription(), ""); + assertEquals(image.getOperatingSystem().getVersion(), ""); + assertEquals(image.getName(), ""); + } + + public void testRepoTagWithHostPort() { + Image image = function.apply(IMAGE_REPOTAG_WITH_PORT); + + assertEquals(image.getDescription(), "registry.company.example:8888/a/b/c/d:latest"); + assertEquals(image.getOperatingSystem().getVersion(), "latest"); + assertEquals(image.getName(), "registry.company.example:8888/a/b/c/d"); + } + private org.jclouds.docker.domain.Image mockImage() { org.jclouds.docker.domain.Image mockImage = EasyMock.createMock(org.jclouds.docker.domain.Image.class); From 58e11d42d1e9d28620e64d6cf8b49f1a9a01d2e6 Mon Sep 17 00:00:00 2001 From: Josef Cacek Date: Fri, 19 Aug 2016 15:47:21 +0200 Subject: [PATCH 78/81] Update Docker README.md --- apis/docker/README.md | 68 ++++++++++++++++++++++++++++++++----------- 1 file changed, 51 insertions(+), 17 deletions(-) diff --git a/apis/docker/README.md b/apis/docker/README.md index 4fdf700750..fef97b303a 100644 --- a/apis/docker/README.md +++ b/apis/docker/README.md @@ -1,20 +1,56 @@ # Docker as a local cloud provider + jclouds-docker is a local cloud provider modelled on [docker](http://www.docker.io). Similar to other jclouds supported providers, it supports the same portable abstractions offered by jclouds. -##Setup +## Setup Please follow these steps to configure your workstation for jclouds-docker: - install the latest Docker release (please visit https://docs.docker.com/installation/) +- [enable remote access](https://docs.docker.com/engine/quickstart/#bind-docker-to-another-host-port-or-a-unix-socket) to Docker -If you are using `docker-machine` then it can also manage certificates and help you setup `DOCKER_CERT_PATH` and `DOCKER_HOST` environment variables. +### Sample configuration for Linux systems using systemd -Assuming these environment variables are setup correctly there are no further setups steps are required. +Run following commands on a machine where is the Docker Engine installed. +It enables remote access (plain TCP - only for loopback address 127.0.0.1) +on standard port `2375`. -Live tests then can now be run: `mvn -Plive integration-test` +```bash +# switch to root account +sudo su - -#How it works +# create override for docker start-script +mkdir /etc/systemd/system/docker.service.d +cat << EOT > /etc/systemd/system/docker.service.d/allow-tcp.conf +[Service] +ExecStart= +ExecStart=/usr/bin/docker daemon -H fd:// -H tcp:// +EOT + +# reload configuration and restart docker daemon +systemctl daemon-reload +systemctl restart docker + +# close the 'root' session +exit +``` + +If the `-H fd://` Docker daemon parameter doesn't work on your Linux (e.g. Fedora), +then replace it by `-H unix:///var/run/docker.sock` + +Find more details in [Control and configure Docker with systemd](https://docs.docker.com/engine/admin/systemd/) guide. + +### Running live tests + +The `DOCKER_HOST` environment variable has to be configured as it's used as a value for `test.docker.endpoint` system property. + +``` +export DOCKER_HOST="http://localhost:2375/" +mvn -Plive integration-test +``` + +# How it works --------------- ------------- @@ -24,7 +60,7 @@ Live tests then can now be run: `mvn -Plive integration-test` | jclouds | ---------------------------> | DOCKER_HOST | --------- ---------------------------------------- -##Components +## Components - jclouds \- acts as a java client to access to docker features - DOCKER_HOST \- hosts Docker API, NB: jclouds-docker assumes that the latest Docker is installed @@ -37,19 +73,17 @@ Live tests then can now be run: `mvn -Plive integration-test` -------------- -#Notes: -- jclouds-docker is still at alpha stage please report any issues you find at [jclouds issues](https://issues.apache.org/jira/browse/JCLOUDS) -- jclouds-docker has been tested on Mac OSX, it might work on Linux iff vbox is running and set up correctly. However, it has never been tried on Windows. +# Notes: +- report any issues you find at [jclouds issues](https://issues.apache.org/jira/browse/JCLOUDS) +- jclouds-docker has been tested on Mac OSX and Linux. However, it has never been tried on Windows. -------------- -#Troubleshooting -As jclouds docker support is quite new, issues may occasionally arise. Please follow these steps to get things going again: +# Troubleshooting -1. Remove all containers +As jclouds docker support is quite new, issues may occasionally arise. +You can try to remove all containers to get things going again: - $ docker rm -f `docker ps -a` - -2. remove all the images - - $ docker rmi -f `docker images -q` +```bash +docker rm -f `docker ps -aq` +``` From c4605a32d4104ed1d8c4c6e01a3440bfaec0e54c Mon Sep 17 00:00:00 2001 From: Josef Cacek Date: Fri, 19 Aug 2016 15:24:44 +0200 Subject: [PATCH 79/81] [JCLOUDS-1157] fix networks in DockerComputeServiceAdapterLiveTest --- .../DockerComputeServiceAdapterLiveTest.java | 66 +++++++++++-------- 1 file changed, 40 insertions(+), 26 deletions(-) diff --git a/apis/docker/src/test/java/org/jclouds/docker/compute/DockerComputeServiceAdapterLiveTest.java b/apis/docker/src/test/java/org/jclouds/docker/compute/DockerComputeServiceAdapterLiveTest.java index cb8ca3e683..c37c474c92 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/compute/DockerComputeServiceAdapterLiveTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/compute/DockerComputeServiceAdapterLiveTest.java @@ -46,6 +46,7 @@ import org.jclouds.docker.compute.strategy.DockerComputeServiceAdapter; import org.jclouds.docker.domain.Container; import org.jclouds.docker.domain.Image; import org.jclouds.docker.domain.Network; +import org.jclouds.docker.features.NetworkApi; import org.jclouds.sshj.config.SshjSshClientModule; @Test(groups = "live", singleThreaded = true, testName = "DockerComputeServiceAdapterLiveTest") @@ -67,8 +68,8 @@ public class DockerComputeServiceAdapterLiveTest extends BaseDockerApiLiveTest { super.initialize(); String imageName = SSHABLE_IMAGE + ":" + SSHABLE_IMAGE_TAG; defaultImage = adapter.getImage(imageName); - network1 = api.getNetworkApi().createNetwork(Network.builder().name("network1").driver("overlay").build()); - network2 = api.getNetworkApi().createNetwork(Network.builder().name("network2").driver("overlay").build()); + network1 = createAndInspectNetwork("network1"); + network2 = createAndInspectNetwork("network2"); assertNotNull(defaultImage); } @@ -116,47 +117,60 @@ public class DockerComputeServiceAdapterLiveTest extends BaseDockerApiLiveTest { } public void testGetImageNotHiddenByCache() { - //Ensure image to be tested is unknown to jclouds and docker and that cache is warm + // Ensure image to be tested is unknown to jclouds and docker and that + // cache is warm assertNull(findImageFromListImages(CHUANWEN_COWSAY)); assertNull(api.getImageApi().inspectImage(CHUANWEN_COWSAY)); // Get new image adapter.getImage(CHUANWEN_COWSAY); - assertNotNull(findImageFromListImages(CHUANWEN_COWSAY), "New image is not available from listImages presumably due to caching"); + assertNotNull(findImageFromListImages(CHUANWEN_COWSAY), + "New image is not available from listImages presumably due to caching"); } public void testCreateNodeWithMultipleNetworks() { - String name = "container" + new Random().nextInt(); - Template template = templateBuilder.imageId(defaultImage.id()).build(); - DockerTemplateOptions options = template.getOptions().as(DockerTemplateOptions.class); - options.env(ImmutableList.of("ROOT_PASSWORD=password")); - options.networkMode("bridge"); - options.networks(network1.name(), network2.name()); - guest = adapter.createNodeWithGroupEncodedIntoName("test", name, template); + String name = "container" + new Random().nextInt(); + Template template = templateBuilder.imageId(defaultImage.id()).build(); + DockerTemplateOptions options = template.getOptions().as(DockerTemplateOptions.class); + options.env(ImmutableList.of("ROOT_PASSWORD=password")); + options.networkMode("bridge"); + options.networks(network1.name(), network2.name()); + guest = adapter.createNodeWithGroupEncodedIntoName("test", name, template); - assertTrue(guest.getNode().networkSettings().networks().containsKey("network1")); - assertTrue(guest.getNode().networkSettings().networks().containsKey("network2")); - assertEquals(guest.getNode().networkSettings().secondaryIPAddresses().size(), 2); - } + assertTrue(guest.getNode().networkSettings().networks().containsKey("network1")); + assertTrue(guest.getNode().networkSettings().networks().containsKey("network2")); + assertEquals(guest.getNode().networkSettings().networks().size(), 3); + } private Image findImageFromListImages(final String image) { return Iterables.find(adapter.listImages(), new Predicate() { - @Override - public boolean apply(Image input) { - for (String tag : input.repoTags()) { - if (tag.equals(image) || tag.equals(CHUANWEN_COWSAY + ":latest")) { - return true; - } - } - return false; - } - }, null); + @Override + public boolean apply(Image input) { + for (String tag : input.repoTags()) { + if (tag.equals(image) || tag.equals(CHUANWEN_COWSAY + ":latest")) { + return true; + } + } + return false; + } + }, null); } @Override protected Iterable setupModules() { - return ImmutableSet.of(getLoggingModule(), new SshjSshClientModule()); + return ImmutableSet. of(getLoggingModule(), new SshjSshClientModule()); } + /** + * Creates network (driver="bridge") with given name and then inspects it to + * fully populate the returned {@link Network} object. + * + * @param networkName + */ + private Network createAndInspectNetwork(final String networkName) { + final NetworkApi networkApi = api.getNetworkApi(); + Network network = networkApi.createNetwork(Network.builder().name(networkName).driver("bridge").build()); + return networkApi.inspectNetwork(network.id()); + } } From 71dd766227332c5f191788ee8d5d6dedd3771c19 Mon Sep 17 00:00:00 2001 From: Josef Cacek Date: Mon, 22 Aug 2016 13:44:19 +0200 Subject: [PATCH 80/81] Docker live test fix - SshToCustomPortLiveTest didn't work for remote docker endpoints due to wrong port number used --- .../jclouds/docker/compute/SshToCustomPortLiveTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apis/docker/src/test/java/org/jclouds/docker/compute/SshToCustomPortLiveTest.java b/apis/docker/src/test/java/org/jclouds/docker/compute/SshToCustomPortLiveTest.java index 23f3838f2a..2e815a4488 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/compute/SshToCustomPortLiveTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/compute/SshToCustomPortLiveTest.java @@ -121,7 +121,7 @@ public class SshToCustomPortLiveTest extends BaseComputeServiceContextLiveTest { String nodeId = null; try { NodeMetadata node = Iterables - .getOnlyElement(view.getComputeService().createNodesInGroup("ssh-test", 1, template)); + .getOnlyElement(view.getComputeService().createNodesInGroup("ssh-net-host", 1, template)); nodeId = node.getId(); ExecResponse response = view.getComputeService().runScriptOnNode(nodeId, "sh -c 'echo hello && sleep 0.2'", wrapInInitScript(false)); @@ -152,7 +152,7 @@ public class SshToCustomPortLiveTest extends BaseComputeServiceContextLiveTest { String nodeId = null; try { NodeMetadata node = Iterables - .getOnlyElement(view.getComputeService().createNodesInGroup("ssh-test-advanced", 1, template)); + .getOnlyElement(view.getComputeService().createNodesInGroup("ssh-net-bridge", 1, template)); nodeId = node.getId(); ExecResponse response = view.getComputeService().runScriptOnNode(nodeId, "sh -c 'true'", @@ -214,8 +214,8 @@ public class SshToCustomPortLiveTest extends BaseComputeServiceContextLiveTest { protected void configure() { bind(LoginPortForContainer.class).toInstance(new LoginPortForContainer() { @Override - public Optional apply(Container input) { - return Optional.of(SSH_PORT); + public Optional apply(Container container) { + return Optional.of(container.name().contains("ssh-net-bridge") ? SSH_PORT_BRIDGE : SSH_PORT); } }); } From a4e2110a6307f00e10f1f2193b5f609bc185b628 Mon Sep 17 00:00:00 2001 From: Josef Cacek Date: Mon, 22 Aug 2016 15:21:09 +0200 Subject: [PATCH 81/81] JCLOUDS-1158 fix Docker find image Predicate for cases where repoTags field contains 'docker.io/' registry host prefix --- .../strategy/DockerComputeServiceAdapter.java | 43 +++++-- .../DockerComputeServiceAdapterLiveTest.java | 4 +- .../PredicateLocateImageByNameTest.java | 111 ++++++++++++++++++ 3 files changed, 146 insertions(+), 12 deletions(-) create mode 100644 apis/docker/src/test/java/org/jclouds/docker/compute/strategy/PredicateLocateImageByNameTest.java diff --git a/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java b/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java index 8130838821..690b50ec04 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java +++ b/apis/docker/src/main/java/org/jclouds/docker/compute/strategy/DockerComputeServiceAdapter.java @@ -22,6 +22,7 @@ import static com.google.common.collect.Iterables.find; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.regex.Pattern; import javax.annotation.Resource; import javax.inject.Inject; @@ -67,6 +68,21 @@ import org.jclouds.logging.Logger; public class DockerComputeServiceAdapter implements ComputeServiceAdapter { + + /** + * Some Docker versions returns host prefix even for images from Docker hub in repoTags field. We use this constant + * to correctly identify requested image name. + */ + public static final String PREFIX_DOCKER_HUB_HOST = "docker.io/"; + + /** + * (Optional) Suffix used, when image version is not used during searching images. + */ + public static final String SUFFIX_LATEST_VERSION = ":latest"; + + private static final String PATTERN_IMAGE_PREFIX = "^(" + Pattern.quote(PREFIX_DOCKER_HUB_HOST) + ")?"; + private static final String PATTERN_IMAGE_SUFFIX = "(" + Pattern.quote(SUFFIX_LATEST_VERSION) + ")?$"; + @Resource @Named(ComputeServiceConstants.COMPUTE_LOGGER) protected Logger logger = Logger.NULL; @@ -266,17 +282,7 @@ public class DockerComputeServiceAdapter implements api.getImageApi().createImage(CreateImageOptions.Builder.fromImage(imageIdOrName)); // as above this ensure repotags are returned - return find(listImages(), new Predicate() { - @Override - public boolean apply(Image input) { - for (String tag : input.repoTags()) { - if (tag.equals(imageIdOrName) || tag.equals(imageIdOrName + ":latest")) { - return true; - } - } - return false; - } - }, null); + return find(listImages(), createPredicateMatchingRepoTags(imageIdOrName), null); } @Override @@ -329,4 +335,19 @@ public class DockerComputeServiceAdapter implements api.getContainerApi().pause(id); } + protected static Predicate createPredicateMatchingRepoTags(final String imageIdOrName) { + final Pattern imgPattern = Pattern + .compile(PATTERN_IMAGE_PREFIX + Pattern.quote(imageIdOrName) + PATTERN_IMAGE_SUFFIX); + return new Predicate() { + @Override + public boolean apply(Image input) { + for (String tag : input.repoTags()) { + if (imgPattern.matcher(tag).matches()) { + return true; + } + } + return false; + } + }; + } } diff --git a/apis/docker/src/test/java/org/jclouds/docker/compute/DockerComputeServiceAdapterLiveTest.java b/apis/docker/src/test/java/org/jclouds/docker/compute/DockerComputeServiceAdapterLiveTest.java index c37c474c92..72fc8d50e5 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/compute/DockerComputeServiceAdapterLiveTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/compute/DockerComputeServiceAdapterLiveTest.java @@ -148,7 +148,9 @@ public class DockerComputeServiceAdapterLiveTest extends BaseDockerApiLiveTest { @Override public boolean apply(Image input) { for (String tag : input.repoTags()) { - if (tag.equals(image) || tag.equals(CHUANWEN_COWSAY + ":latest")) { + if (tag.equals(CHUANWEN_COWSAY + DockerComputeServiceAdapter.SUFFIX_LATEST_VERSION) + || tag.equals(DockerComputeServiceAdapter.PREFIX_DOCKER_HUB_HOST + CHUANWEN_COWSAY + + DockerComputeServiceAdapter.SUFFIX_LATEST_VERSION)) { return true; } } diff --git a/apis/docker/src/test/java/org/jclouds/docker/compute/strategy/PredicateLocateImageByNameTest.java b/apis/docker/src/test/java/org/jclouds/docker/compute/strategy/PredicateLocateImageByNameTest.java new file mode 100644 index 0000000000..dd8b65433d --- /dev/null +++ b/apis/docker/src/test/java/org/jclouds/docker/compute/strategy/PredicateLocateImageByNameTest.java @@ -0,0 +1,111 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.docker.compute.strategy; + +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + +import java.util.Date; + +import org.jclouds.docker.domain.Config; +import org.jclouds.docker.domain.Image; +import org.testng.annotations.Test; + +import com.google.common.base.Predicate; +import com.google.common.collect.ImmutableList; + +/** + * Unit tests for finding images logic used in + * {@link DockerComputeServiceAdapter#getImage(String)} method. It's mainly a + * regression test for issue + * JCLOUDS-1158 + * . + */ +@Test(groups = "unit", testName = "PredicateLocateImageByNameTest") +public class PredicateLocateImageByNameTest { + + private static final Image IMAGE_REPO_TAGS_MULTI = Image.create("id", // id + "author", "comment", Config.builder().image("imageId").build(), Config.builder().image("imageId").build(), + "parent", // parent + new Date(), // created + "containerId", // container + "1.3.1", // dockerVersion + "x86_64", // architecture + "os", // os + 0l, // size + 0l, // virtualSize + ImmutableList. of("kwart/alpine-ext:3.3-ssh", "kwart/alpine-ext:latest", "my-tag:latestdock") // repoTags + ); + + private static final Image IMAGE_REPO_TAGS_EMPTY = Image.create("id", // id + "author", "comment", Config.builder().image("imageId").build(), Config.builder().image("imageId").build(), + "parent", // parent + new Date(), // created + "containerId", // container + "1.3.1", // dockerVersion + "x86_64", // architecture + "os", // os + 0l, // size + 0l, // virtualSize + ImmutableList. of() // repoTags + ); + + private static final Image IMAGE_REPO_TAGS_WITH_HOST = Image.create("id", // id + "author", "comment", Config.builder().image("imageId").build(), Config.builder().image("imageId").build(), + "parent", // parent + new Date(), // created + "containerId", // container + "1.3.1", // dockerVersion + "x86_64", // architecture + "os", // os + 0l, // size + 0l, // virtualSize + ImmutableList. of("docker.io/kwart/alpine-ext:3.3-ssh", "docker.io/kwart/alpine-ext:latest") // repoTags + ); + + public void testRepoTagVersion() { + final Predicate predicate = DockerComputeServiceAdapter + .createPredicateMatchingRepoTags("kwart/alpine-ext:3.3-ssh"); + assertTrue(predicate.apply(IMAGE_REPO_TAGS_MULTI)); + assertFalse(predicate.apply(IMAGE_REPO_TAGS_EMPTY)); + assertTrue(predicate.apply(IMAGE_REPO_TAGS_WITH_HOST)); + } + + public void testRepoTagLatest() { + final Predicate predicate = DockerComputeServiceAdapter.createPredicateMatchingRepoTags("kwart/alpine-ext"); + assertTrue(predicate.apply(IMAGE_REPO_TAGS_MULTI)); + assertFalse(predicate.apply(IMAGE_REPO_TAGS_EMPTY)); + assertTrue(predicate.apply(IMAGE_REPO_TAGS_WITH_HOST)); + } + + public void testRepoTagVersionWithHost() { + final Predicate predicate = DockerComputeServiceAdapter + .createPredicateMatchingRepoTags("docker.io/kwart/alpine-ext:3.3-ssh"); + assertFalse(predicate.apply(IMAGE_REPO_TAGS_MULTI)); + assertFalse(predicate.apply(IMAGE_REPO_TAGS_EMPTY)); + assertTrue(predicate.apply(IMAGE_REPO_TAGS_WITH_HOST)); + } + + public void testRepoTagLatestWithHost() { + final Predicate predicate = DockerComputeServiceAdapter + .createPredicateMatchingRepoTags("docker.io/kwart/alpine-ext"); + assertFalse(predicate.apply(IMAGE_REPO_TAGS_MULTI)); + assertFalse(predicate.apply(IMAGE_REPO_TAGS_EMPTY)); + assertTrue(predicate.apply(IMAGE_REPO_TAGS_WITH_HOST)); + } + +}