├── src ├── it │ ├── container-logs-it │ │ ├── src │ │ │ ├── main │ │ │ │ ├── resources │ │ │ │ │ ├── ratpack.properties │ │ │ │ │ └── log4j.xml │ │ │ │ ├── docker │ │ │ │ │ └── Dockerfile │ │ │ │ └── java │ │ │ │ │ └── net │ │ │ │ │ └── wouterdanes │ │ │ │ │ └── Application.java │ │ │ └── test │ │ │ │ └── java │ │ │ │ └── net │ │ │ │ └── wouterdanes │ │ │ │ └── ApplicationIT.groovy │ │ └── verify.groovy │ ├── sample-api-mongo-it │ │ └── src │ │ │ ├── main │ │ │ ├── resources │ │ │ │ ├── ratpack.properties │ │ │ │ └── log4j.xml │ │ │ ├── docker │ │ │ │ └── Dockerfile │ │ │ └── java │ │ │ │ └── net │ │ │ │ └── wouterdanes │ │ │ │ └── Application.java │ │ │ └── test │ │ │ └── java │ │ │ └── net │ │ │ └── wouterdanes │ │ │ └── ApplicationIT.groovy │ ├── maven-artifact-it │ │ └── src │ │ │ ├── main │ │ │ └── docker │ │ │ │ └── Dockerfile │ │ │ └── test │ │ │ └── java │ │ │ └── net │ │ │ └── wouterdanes │ │ │ └── ApplicationIT.groovy │ ├── simple-it │ │ ├── src │ │ │ └── test │ │ │ │ ├── resources │ │ │ │ ├── Dockerfile │ │ │ │ └── 1234567890abcdefghij1234567890abcdefghij1234567890abcdefghij1234567890abcdefghij1234567890abcdefghij │ │ │ │ └── java │ │ │ │ └── NginxIT.java │ │ └── pom.xml │ ├── skip-docker-it │ │ ├── src │ │ │ └── test │ │ │ │ ├── resources │ │ │ │ └── Dockerfile │ │ │ │ └── java │ │ │ │ └── NginxIT.java │ │ └── pom.xml │ ├── skip-using-property-it │ │ ├── src │ │ │ └── test │ │ │ │ ├── resources │ │ │ │ └── Dockerfile │ │ │ │ └── java │ │ │ │ └── NginxIT.java │ │ └── pom.xml │ ├── buildargs-it │ │ ├── src │ │ │ └── test │ │ │ │ ├── resources │ │ │ │ └── Dockerfile │ │ │ │ └── java │ │ │ │ └── BuildArgsIT.java │ │ └── pom.xml │ └── settings.xml ├── broken-it │ ├── tag-and-push-it │ │ ├── src │ │ │ └── test │ │ │ │ └── resources │ │ │ │ └── Dockerfile │ │ └── pom.xml │ ├── push-with-creds-it │ │ ├── src │ │ │ └── test │ │ │ │ ├── resources │ │ │ │ └── Dockerfile │ │ │ │ └── java │ │ │ │ └── NginxIT.java │ │ └── pom.xml │ └── push-with-explicit-registry-it │ │ ├── src │ │ └── test │ │ │ └── resources │ │ │ └── Dockerfile │ │ └── pom.xml ├── main │ └── java │ │ └── net │ │ └── wouterdanes │ │ └── docker │ │ ├── remoteapi │ │ ├── exception │ │ │ ├── MavenArtifactNotFoundException.java │ │ │ ├── ContainerNotFoundException.java │ │ │ ├── ImageNotFoundException.java │ │ │ └── DockerException.java │ │ ├── model │ │ │ ├── ContainerCommitResponse.java │ │ │ ├── ContainerLink.java │ │ │ ├── ContainerCreateResponse.java │ │ │ ├── DockerVersionInfo.java │ │ │ ├── Credentials.java │ │ │ ├── ContainerStartRequest.java │ │ │ ├── ContainerCreateRequest.java │ │ │ └── ImageDescriptor.java │ │ ├── util │ │ │ ├── DockerEnvironmentSupplier.java │ │ │ ├── DockerHostFromPropertySupplier.java │ │ │ ├── DockerPortFromPropertySupplier.java │ │ │ ├── DockerHostFromEnvironmentSupplier.java │ │ │ ├── DockerPortFromEnvironmentSupplier.java │ │ │ └── HttpsHelper.java │ │ ├── ImagesService.java │ │ └── ContainersService.java │ │ ├── provider │ │ ├── model │ │ │ ├── Artifact.java │ │ │ ├── MavenArtifact.java │ │ │ ├── ExposedPort.java │ │ │ ├── BuiltImageInfo.java │ │ │ ├── ContainerCommitConfiguration.java │ │ │ ├── ImageTagConfiguration.java │ │ │ ├── PushableImage.java │ │ │ ├── ImageBuildConfiguration.java │ │ │ └── ContainerStartConfiguration.java │ │ ├── DockerProviderSupplier.java │ │ ├── LocalDockerProvider.java │ │ ├── RemoteDockerProvider.java │ │ └── DockerProvider.java │ │ └── maven │ │ ├── StartedContainerInfo.java │ │ ├── AbstractPreVerifyDockerMojo.java │ │ ├── DockerPluginError.java │ │ ├── VerifyMojo.java │ │ ├── StopContainerMojo.java │ │ ├── TagImageMojo.java │ │ ├── PushImageMojo.java │ │ ├── CommitContainerMojo.java │ │ └── BuildImageMojo.java └── test │ └── java │ └── net │ └── wouterdanes │ └── docker │ ├── provider │ ├── model │ │ └── ContainerStartConfigurationTest.java │ ├── RemoteDockerProviderTest.java │ ├── AbstractFakeDockerProvider.java │ └── DockerExceptionThrowingDockerProvider.java │ ├── VerifyHostnameSetIT.java │ ├── maven │ ├── AbstractDockerMojoTest.java │ ├── VerifyMojoTest.java │ ├── CommitContainerMojoTest.java │ ├── PushImageMojoTest.java │ └── BuildImageMojoTest.java │ ├── VerifyPushedImagesIT.java │ └── remoteapi │ ├── BuiltImageInfoTest.java │ ├── PushableImageTest.java │ ├── CredentialsTest.java │ └── ImageDescriptorTest.java ├── .gitignore ├── .travis.yml ├── deploy-to-sonatype.sh ├── generate-maven-settings.sh └── CHANGELOG.md /src/it/container-logs-it/src/main/resources/ratpack.properties: -------------------------------------------------------------------------------- 1 | handlerFactory=net.wouterdanes.Application 2 | port=8080 -------------------------------------------------------------------------------- /src/it/sample-api-mongo-it/src/main/resources/ratpack.properties: -------------------------------------------------------------------------------- 1 | handlerFactory=net.wouterdanes.Application 2 | port=8080 -------------------------------------------------------------------------------- /src/it/container-logs-it/verify.groovy: -------------------------------------------------------------------------------- 1 | 2 | def logfile = new File( basedir, "target/logs/app.log" ) 3 | assert logfile.exists() -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | *.iml 3 | target/ 4 | pom.xml.tag 5 | pom.xml.releaseBackup 6 | pom.xml.next 7 | release.properties 8 | .classpath 9 | .settings 10 | .project 11 | -------------------------------------------------------------------------------- /src/it/maven-artifact-it/src/main/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM tomcat:8.0.30-jre8 2 | 3 | RUN rm -rf /usr/local/tomcat/webapps/* 4 | 5 | ADD test/rest.war /usr/local/tomcat/webapps/rest.war 6 | 7 | EXPOSE 8080 8 | -------------------------------------------------------------------------------- /src/it/simple-it/src/test/resources/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:jessie 2 | 3 | RUN apt-get update && apt-get install -y nginx && echo "\ndaemon off;" >> /etc/nginx/nginx.conf 4 | 5 | CMD ["nginx"] 6 | 7 | EXPOSE 80 8 | -------------------------------------------------------------------------------- /src/it/skip-docker-it/src/test/resources/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:jessie 2 | 3 | RUN apt-get update && apt-get install -y nginx && echo "\ndaemon off;" >> /etc/nginx/nginx.conf 4 | 5 | CMD ["nginx"] 6 | 7 | EXPOSE 80 8 | -------------------------------------------------------------------------------- /src/broken-it/tag-and-push-it/src/test/resources/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:jessie 2 | 3 | RUN apt-get update && apt-get install -y nginx && echo "\ndaemon off;" >> /etc/nginx/nginx.conf 4 | 5 | CMD ["nginx"] 6 | 7 | EXPOSE 80 -------------------------------------------------------------------------------- /src/broken-it/push-with-creds-it/src/test/resources/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:jessie 2 | 3 | RUN apt-get update && apt-get install -y nginx && echo "\ndaemon off;" >> /etc/nginx/nginx.conf 4 | 5 | CMD ["nginx"] 6 | 7 | EXPOSE 80 8 | -------------------------------------------------------------------------------- /src/it/container-logs-it/src/main/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM wouterd/java8 2 | 3 | ADD discuss-jar-with-dependencies.jar /discuss.jar 4 | 5 | ENTRYPOINT ["java", "-jar", "-Dmongo.host=mongo", "/discuss.jar"] 6 | 7 | EXPOSE 8080 8 | -------------------------------------------------------------------------------- /src/it/skip-using-property-it/src/test/resources/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:jessie 2 | 3 | RUN apt-get update && apt-get install -y nginx && echo "\ndaemon off;" >> /etc/nginx/nginx.conf 4 | 5 | CMD ["nginx"] 6 | 7 | EXPOSE 80 8 | -------------------------------------------------------------------------------- /src/broken-it/push-with-explicit-registry-it/src/test/resources/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:jessie 2 | 3 | RUN apt-get update && apt-get install -y nginx && echo "\ndaemon off;" >> /etc/nginx/nginx.conf 4 | 5 | CMD ["nginx"] 6 | 7 | EXPOSE 80 -------------------------------------------------------------------------------- /src/it/sample-api-mongo-it/src/main/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM jeanblanchard/java:8 2 | 3 | ADD discuss-jar-with-dependencies.jar /discuss.jar 4 | 5 | ENTRYPOINT ["java", "-jar", "-Dmongo.host=mongo", "/discuss.jar"] 6 | 7 | EXPOSE 8080 8 | -------------------------------------------------------------------------------- /src/it/buildargs-it/src/test/resources/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:jessie 2 | 3 | ARG packagename 4 | 5 | RUN apt-get update && apt-get install -y $packagename && echo "\ndaemon off;" >> /etc/nginx/nginx.conf 6 | 7 | CMD ["nginx"] 8 | 9 | EXPOSE 80 10 | -------------------------------------------------------------------------------- /src/it/simple-it/src/test/resources/1234567890abcdefghij1234567890abcdefghij1234567890abcdefghij1234567890abcdefghij1234567890abcdefghij: -------------------------------------------------------------------------------- 1 | Annoyingly long file name, so annoying long that the commons-compress library said "screw this, I'm not adding you!". 2 | So this file is here to make sure the build falls over instead of the plugin when this happens again. :-) -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | services: 3 | - docker 4 | language: java 5 | jdk: 6 | - oraclejdk8 7 | install: 8 | - uname -a 9 | - env | grep -i docker 10 | script: mvn clean verify 11 | after_success: ./deploy-to-sonatype.sh 12 | env: 13 | global: 14 | - secure: Lft4JvZkRsoKrSIhLKTie45uycZZ5ixmugR5hVlX9qjpugLswIUCrWmiV2KvjNf4dwKTWoVOvphMpxzZbIU6+QK91MTH81Gt5kLYFifQDPLL49gal1ahIfUKJIJUFOC2wJjVY7ZC2lQrNVLU78IQGdidshZNIRS43ugyBPfxXjM= 15 | - secure: HGyAWb8PhDMuPUrFz/YHk+zpeR8aOJ74s9Q4+vZ5bKq1aoETXI+bdf3Ne6p4Ud2jwIPUSxTPCEMUVbaUowDyFzeNiHOCj9i08oL01A+sGLuv5eQi5FgiwLgW6t1tTDbdbISgRT/tWjh7b5Ze2K4SQRnjNwdvWcoxgSJtaBzT/hs= 16 | -------------------------------------------------------------------------------- /src/it/container-logs-it/src/main/resources/log4j.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/it/sample-api-mongo-it/src/main/resources/log4j.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/main/java/net/wouterdanes/docker/remoteapi/exception/MavenArtifactNotFoundException.java: -------------------------------------------------------------------------------- 1 | package net.wouterdanes.docker.remoteapi.exception; 2 | 3 | public class MavenArtifactNotFoundException extends DockerException { 4 | 5 | public MavenArtifactNotFoundException(String artifactName) { 6 | super(makeMessage(artifactName)); 7 | } 8 | 9 | public MavenArtifactNotFoundException(String artifactName, Throwable cause) { 10 | super(makeMessage(artifactName), cause); 11 | } 12 | 13 | private static String makeMessage(String artifactName) { 14 | return String.format("Maven artifact '%s' not found.", artifactName); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/net/wouterdanes/docker/remoteapi/model/ContainerCommitResponse.java: -------------------------------------------------------------------------------- 1 | package net.wouterdanes.docker.remoteapi.model; 2 | 3 | import org.codehaus.jackson.annotate.JsonProperty; 4 | 5 | /** 6 | * See 7 | * http://docs.docker.io/reference/api/docker_remote_api_v1.10/#create-a-new-image-from-a-containers-changes 8 | */ 9 | public class ContainerCommitResponse { 10 | 11 | @JsonProperty("Id") 12 | private String id; 13 | 14 | public String getId() { 15 | return id; 16 | } 17 | 18 | public void setId(String id) { 19 | this.id = id; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/net/wouterdanes/docker/provider/model/Artifact.java: -------------------------------------------------------------------------------- 1 | package net.wouterdanes.docker.provider.model; 2 | 3 | import org.apache.maven.plugins.annotations.Parameter; 4 | 5 | import java.io.File; 6 | import java.util.Optional; 7 | 8 | public class Artifact { 9 | @Parameter(required = true) 10 | private File file; 11 | 12 | @Parameter 13 | private String dest; 14 | 15 | public Optional getDest() { 16 | return Optional.ofNullable(dest); 17 | } 18 | 19 | public void setDest(String dest) { 20 | this.dest = dest; 21 | } 22 | 23 | public File getFile() { 24 | return file; 25 | } 26 | 27 | public void setFile(File file) { 28 | this.file = file; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/it/maven-artifact-it/src/test/java/net/wouterdanes/ApplicationIT.groovy: -------------------------------------------------------------------------------- 1 | package net.wouterdanes 2 | 3 | import groovyx.net.http.ContentType 4 | import groovyx.net.http.RESTClient 5 | import spock.lang.Specification 6 | 7 | class ApplicationIT extends Specification { 8 | 9 | def "Check that WAR pulled down from Maven Central is deployed and working"() { 10 | 11 | setup: 12 | String baseUrl = System.getProperty("app.base.url"); 13 | def client = new RESTClient(baseUrl) 14 | 15 | when: "we attempt to hit the hello endpoint" 16 | def resp = client.get(path: "/rest/hello") 17 | 18 | then: "we should get a valid 200 response" 19 | with(resp) { 20 | status == 200 21 | contentType == ContentType.HTML.toString() 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /deploy-to-sonatype.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2014 Wouter Danes 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | if [[ "${TRAVIS_BRANCH}" == 'master' ]] ; then 17 | ./generate-maven-settings.sh 18 | mvn deploy -s ./target/maven-settings.xml 19 | fi -------------------------------------------------------------------------------- /src/main/java/net/wouterdanes/docker/remoteapi/util/DockerEnvironmentSupplier.java: -------------------------------------------------------------------------------- 1 | package net.wouterdanes.docker.remoteapi.util; 2 | 3 | import net.wouterdanes.docker.provider.RemoteDockerProvider; 4 | 5 | import java.net.URI; 6 | import java.util.Optional; 7 | 8 | public abstract class DockerEnvironmentSupplier { 9 | 10 | protected Optional getDockerUriFromEnvironment() { 11 | String envDockerHost = System.getenv(RemoteDockerProvider.DOCKER_HOST_SYSTEM_ENV); 12 | if (envDockerHost == null) { 13 | return Optional.empty(); 14 | } 15 | try { 16 | URI uri = URI.create(envDockerHost); 17 | return Optional.of(uri); 18 | } catch (IllegalArgumentException ignored) { 19 | return Optional.empty(); 20 | } 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/net/wouterdanes/docker/remoteapi/util/DockerHostFromPropertySupplier.java: -------------------------------------------------------------------------------- 1 | package net.wouterdanes.docker.remoteapi.util; 2 | 3 | import net.wouterdanes.docker.provider.RemoteDockerProvider; 4 | 5 | import java.util.Optional; 6 | import java.util.function.Supplier; 7 | 8 | /** 9 | * Supplies the docker host from the system property 10 | * {@value net.wouterdanes.docker.provider.RemoteDockerProvider#DOCKER_HOST_PROPERTY} 11 | */ 12 | public final class DockerHostFromPropertySupplier implements Supplier> { 13 | 14 | public static final DockerHostFromPropertySupplier INSTANCE = new DockerHostFromPropertySupplier(); 15 | 16 | private DockerHostFromPropertySupplier() { } 17 | 18 | @Override 19 | public Optional get() { 20 | return Optional.ofNullable(System.getProperty(RemoteDockerProvider.DOCKER_HOST_PROPERTY)); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/net/wouterdanes/docker/maven/StartedContainerInfo.java: -------------------------------------------------------------------------------- 1 | package net.wouterdanes.docker.maven; 2 | 3 | import net.wouterdanes.docker.remoteapi.model.ContainerInspectionResult; 4 | 5 | /** 6 | * Holds the information for a started container: it's starting id and an inspection result just after starting. 7 | */ 8 | public class StartedContainerInfo { 9 | 10 | private final String containerId; 11 | private final ContainerInspectionResult containerInfo; 12 | 13 | public StartedContainerInfo(final String containerId, final ContainerInspectionResult containerInfo) { 14 | this.containerId = containerId; 15 | this.containerInfo = containerInfo; 16 | } 17 | 18 | public String getContainerId() { 19 | return containerId; 20 | } 21 | 22 | public ContainerInspectionResult getContainerInfo() { 23 | return containerInfo; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/net/wouterdanes/docker/provider/model/MavenArtifact.java: -------------------------------------------------------------------------------- 1 | package net.wouterdanes.docker.provider.model; 2 | 3 | import org.apache.maven.plugins.annotations.Parameter; 4 | 5 | import java.util.Optional; 6 | 7 | public class MavenArtifact { 8 | 9 | /** 10 | * The {@code :[:[:]]:} of the artifact to resolve. 11 | */ 12 | @Parameter(required = true) 13 | private String dependency; 14 | 15 | @Parameter 16 | private String dest; 17 | 18 | public String getDependency() { 19 | return dependency; 20 | } 21 | 22 | public void setDependency(String dependency) { 23 | this.dependency = dependency; 24 | } 25 | 26 | public Optional getDest() { 27 | return Optional.ofNullable(dest); 28 | } 29 | 30 | public void setDest(String dest) { 31 | this.dest = dest; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/net/wouterdanes/docker/remoteapi/util/DockerPortFromPropertySupplier.java: -------------------------------------------------------------------------------- 1 | package net.wouterdanes.docker.remoteapi.util; 2 | 3 | import net.wouterdanes.docker.provider.RemoteDockerProvider; 4 | 5 | import java.util.Optional; 6 | import java.util.function.Supplier; 7 | 8 | /** 9 | * Supplies the docker host from the system property 10 | * {@value net.wouterdanes.docker.provider.RemoteDockerProvider#DOCKER_HOST_PROPERTY} 11 | */ 12 | public final class DockerPortFromPropertySupplier implements Supplier> { 13 | 14 | public static final DockerPortFromPropertySupplier INSTANCE = new DockerPortFromPropertySupplier(); 15 | 16 | private DockerPortFromPropertySupplier() { } 17 | 18 | @Override 19 | public Optional get() { 20 | Optional port = Optional.ofNullable(System.getProperty(RemoteDockerProvider.DOCKER_PORT_PROPERTY)); 21 | return port.isPresent() ? Optional.of(Integer.valueOf(port.get())) : Optional.empty(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/net/wouterdanes/docker/remoteapi/model/ContainerLink.java: -------------------------------------------------------------------------------- 1 | package net.wouterdanes.docker.remoteapi.model; 2 | 3 | import org.apache.maven.plugins.annotations.Parameter; 4 | 5 | /** 6 | * Holds the information for a link to another docker container. 7 | */ 8 | public class ContainerLink { 9 | @Parameter(required = true) 10 | private String containerId; 11 | @Parameter(required = true) 12 | private String containerAlias; 13 | 14 | public ContainerLink toContainer(String containerId) { 15 | this.containerId = containerId; 16 | return this; 17 | } 18 | 19 | public ContainerLink withAlias(String alias) { 20 | this.containerAlias = alias; 21 | return this; 22 | } 23 | 24 | public String getContainerId() { 25 | return containerId; 26 | } 27 | 28 | /** 29 | * Returns the configured alias for this container 30 | * 31 | * @return the linked container's alias 32 | */ 33 | public String getContainerAlias() { 34 | return containerAlias; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/net/wouterdanes/docker/remoteapi/exception/ContainerNotFoundException.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Wouter Danes 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | */ 17 | 18 | package net.wouterdanes.docker.remoteapi.exception; 19 | 20 | 21 | public class ContainerNotFoundException extends DockerException { 22 | public ContainerNotFoundException(final String id) { 23 | super(String.format("Container '%s' not found.", id)); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /generate-maven-settings.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2014 Wouter Danes 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | mkdir -p ./target 17 | MVNSETTINGS=' 18 | 19 | 20 | 21 | sonatype-nexus-snapshots 22 | '"${SONATYPE_USER}"' 23 | '"${SONATYPE_PASS}"' 24 | 25 | 26 | ' 27 | echo ${MVNSETTINGS} > ./target/maven-settings.xml -------------------------------------------------------------------------------- /src/main/java/net/wouterdanes/docker/maven/AbstractPreVerifyDockerMojo.java: -------------------------------------------------------------------------------- 1 | package net.wouterdanes.docker.maven; 2 | 3 | import net.wouterdanes.docker.remoteapi.exception.DockerException; 4 | 5 | import java.util.Optional; 6 | 7 | /** 8 | * Base class for Mojos that execute prior to the "verify" phase. In these Mojos, 9 | * {@link DockerException}s must be caught, suppressed but retained to be rethrown 10 | * during verification. 11 | */ 12 | public abstract class AbstractPreVerifyDockerMojo extends AbstractDockerMojo { 13 | 14 | @Override 15 | protected void handleDockerException(String message, DockerException e) { 16 | getLog().error(message, e); 17 | Optional apiResponse = e.getApiResponse(); 18 | if (apiResponse.isPresent()) { 19 | getLog().info(String.format("Api response:%n%s", apiResponse.get())); 20 | } 21 | registerPluginError(new DockerPluginError(getMojoGoalName(), message, e)); 22 | } 23 | 24 | /** 25 | * For diagnostic purposes. 26 | * 27 | * @return the goal for this mojo 28 | */ 29 | protected abstract String getMojoGoalName(); 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/net/wouterdanes/docker/remoteapi/exception/ImageNotFoundException.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Wouter Danes 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | */ 17 | 18 | package net.wouterdanes.docker.remoteapi.exception; 19 | 20 | public class ImageNotFoundException extends DockerException { 21 | 22 | public ImageNotFoundException(String imageName) { 23 | super(makeMessage(imageName)); 24 | } 25 | 26 | public ImageNotFoundException(String imageName, Throwable cause) { 27 | super(makeMessage(imageName), cause); 28 | } 29 | 30 | private static String makeMessage(String imageName) { 31 | return String.format("Image '%s' not found.", imageName); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/net/wouterdanes/docker/remoteapi/util/DockerHostFromEnvironmentSupplier.java: -------------------------------------------------------------------------------- 1 | package net.wouterdanes.docker.remoteapi.util; 2 | 3 | import net.wouterdanes.docker.provider.RemoteDockerProvider; 4 | 5 | import java.net.URI; 6 | import java.util.Optional; 7 | import java.util.function.Supplier; 8 | 9 | /** 10 | * Supplies the docker host from the environment variable 11 | * '{@value net.wouterdanes.docker.provider.RemoteDockerProvider#DOCKER_HOST_SYSTEM_ENV}' 12 | */ 13 | public final class DockerHostFromEnvironmentSupplier extends DockerEnvironmentSupplier 14 | implements Supplier> { 15 | 16 | public static final DockerHostFromEnvironmentSupplier INSTANCE = new DockerHostFromEnvironmentSupplier(); 17 | 18 | private DockerHostFromEnvironmentSupplier() { } 19 | 20 | @Override 21 | public Optional get() { 22 | Optional dockerUriFromEnvironment = getDockerUriFromEnvironment(); 23 | if (!dockerUriFromEnvironment.isPresent()) { 24 | return Optional.empty(); 25 | } 26 | URI dockerUrl = dockerUriFromEnvironment.get(); 27 | boolean isTcpSocket = RemoteDockerProvider.TCP_PROTOCOL.equalsIgnoreCase(dockerUrl.getScheme()); 28 | return isTcpSocket ? Optional.ofNullable(dockerUrl.getHost()) : Optional.empty(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/net/wouterdanes/docker/remoteapi/util/DockerPortFromEnvironmentSupplier.java: -------------------------------------------------------------------------------- 1 | package net.wouterdanes.docker.remoteapi.util; 2 | 3 | import net.wouterdanes.docker.provider.RemoteDockerProvider; 4 | 5 | import java.net.URI; 6 | import java.util.Optional; 7 | import java.util.function.Supplier; 8 | 9 | /** 10 | * Supplies the docker port from the environment variable 11 | * '{@value net.wouterdanes.docker.provider.RemoteDockerProvider#DOCKER_HOST_SYSTEM_ENV}' 12 | */ 13 | public final class DockerPortFromEnvironmentSupplier extends DockerEnvironmentSupplier 14 | implements Supplier> { 15 | 16 | public static final DockerPortFromEnvironmentSupplier INSTANCE = new DockerPortFromEnvironmentSupplier(); 17 | 18 | private DockerPortFromEnvironmentSupplier() { } 19 | 20 | @Override 21 | public Optional get() { 22 | Optional dockerUriFromEnvironment = getDockerUriFromEnvironment(); 23 | if (!dockerUriFromEnvironment.isPresent()) { 24 | return Optional.empty(); 25 | } 26 | URI dockerUrl = dockerUriFromEnvironment.get(); 27 | boolean isTcpSocket = RemoteDockerProvider.TCP_PROTOCOL.equalsIgnoreCase(dockerUrl.getScheme()); 28 | return isTcpSocket ? Optional.ofNullable(dockerUrl.getPort()) : Optional.empty(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/test/java/net/wouterdanes/docker/provider/model/ContainerStartConfigurationTest.java: -------------------------------------------------------------------------------- 1 | package net.wouterdanes.docker.provider.model; 2 | 3 | import org.junit.Test; 4 | 5 | import net.wouterdanes.docker.remoteapi.model.ContainerLink; 6 | 7 | public class ContainerStartConfigurationTest { 8 | 9 | @Test 10 | public void testThatGetEnvReturnsEmptyEnvWhenEnvNotInitialized() throws Exception { 11 | 12 | ContainerStartConfiguration configuration = new ContainerStartConfiguration(); 13 | 14 | assert configuration.getEnv().isEmpty(); 15 | 16 | } 17 | 18 | @Test 19 | public void testThatCallingWithLinksTwiceAddsToFirstLinks() throws Exception { 20 | 21 | ContainerLink link1 = new ContainerLink(); 22 | ContainerLink link2 = new ContainerLink(); 23 | ContainerStartConfiguration configuration = new ContainerStartConfiguration() 24 | .withLinks(link1) 25 | .withLinks(link2); 26 | 27 | assert configuration.getLinks().contains(link1); 28 | assert configuration.getLinks().contains(link2); 29 | } 30 | 31 | @Test 32 | public void testThatGetLinksReturnsEmptyListWhenNotInitialized() throws Exception { 33 | 34 | ContainerStartConfiguration configuration = new ContainerStartConfiguration(); 35 | 36 | assert configuration.getLinks().isEmpty(); 37 | 38 | } 39 | } -------------------------------------------------------------------------------- /src/test/java/net/wouterdanes/docker/VerifyHostnameSetIT.java: -------------------------------------------------------------------------------- 1 | package net.wouterdanes.docker; 2 | 3 | import org.apache.maven.plugin.logging.Log; 4 | import org.junit.Assert; 5 | import org.junit.Ignore; 6 | import org.junit.Test; 7 | import org.mockito.Mockito; 8 | 9 | import net.wouterdanes.docker.provider.DockerProvider; 10 | import net.wouterdanes.docker.provider.DockerProviderSupplier; 11 | import net.wouterdanes.docker.provider.model.ContainerStartConfiguration; 12 | import net.wouterdanes.docker.remoteapi.model.ContainerInspectionResult; 13 | 14 | @Ignore 15 | public class VerifyHostnameSetIT { 16 | 17 | @Test 18 | public void testThatHostnameIsSet() throws Exception { 19 | 20 | DockerProvider dockerProvider = new DockerProviderSupplier("remote").get(); 21 | 22 | Log log = Mockito.mock(Log.class); 23 | dockerProvider.setLogger(log); 24 | 25 | ContainerStartConfiguration startConfiguration = new ContainerStartConfiguration() 26 | .fromImage("busybox") 27 | .withHostname("hoaxname"); 28 | 29 | ContainerInspectionResult result = dockerProvider.startContainer(startConfiguration); 30 | 31 | dockerProvider.stopContainer(result.getId()); 32 | dockerProvider.deleteContainer(result.getId()); 33 | 34 | Assert.assertEquals("hoaxname", result.getConfig().getHostname()); 35 | 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/net/wouterdanes/docker/maven/DockerPluginError.java: -------------------------------------------------------------------------------- 1 | package net.wouterdanes.docker.maven; 2 | 3 | import net.wouterdanes.docker.remoteapi.exception.DockerException; 4 | 5 | import java.util.Optional; 6 | 7 | /** 8 | * This class holds plugin execution errors that are tested for in the verify goal of this plugin. 9 | * This class is immutable and should be initialized via the constructor. 10 | */ 11 | public class DockerPluginError { 12 | 13 | private final String pluginGoal; 14 | private final String message; 15 | private final Optional exception; 16 | 17 | public DockerPluginError(final String pluginGoal, final String message, final DockerException exception) { 18 | this(pluginGoal, message, Optional.of(exception)); 19 | } 20 | 21 | public DockerPluginError(final String pluginGoal, final String message) { 22 | this(pluginGoal, message, Optional.empty()); 23 | } 24 | 25 | public DockerPluginError(final String pluginGoal, final String message, final Optional exception) { 26 | this.pluginGoal = pluginGoal; 27 | this.message = message; 28 | this.exception = exception; 29 | } 30 | 31 | public String getPluginGoal() { 32 | return pluginGoal; 33 | } 34 | 35 | public String getMessage() { 36 | return message; 37 | } 38 | 39 | public Optional getException() { 40 | return exception; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/net/wouterdanes/docker/provider/model/ExposedPort.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Wouter Danes 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | */ 17 | 18 | package net.wouterdanes.docker.provider.model; 19 | 20 | /** 21 | * This class stores information about an exposed port on a docker container 22 | */ 23 | public class ExposedPort { 24 | private final String containerPort; 25 | private final int externalPort; 26 | private final String host; 27 | 28 | public ExposedPort(final String containerPort, final int externalPort, final String host) { 29 | this.containerPort = containerPort; 30 | this.externalPort = externalPort; 31 | this.host = host; 32 | } 33 | 34 | public String getContainerPort() { 35 | return containerPort; 36 | } 37 | 38 | public int getExternalPort() { 39 | return externalPort; 40 | } 41 | 42 | public String getHost() { 43 | return host; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/it/simple-it/src/test/java/NginxIT.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Wouter Danes 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | */ 17 | 18 | import org.apache.http.HttpEntity; 19 | import org.apache.http.client.methods.CloseableHttpResponse; 20 | import org.apache.http.client.methods.HttpGet; 21 | import org.apache.http.impl.client.CloseableHttpClient; 22 | import org.apache.http.impl.client.HttpClients; 23 | import org.apache.http.util.EntityUtils; 24 | import org.junit.Test; 25 | 26 | public class NginxIT { 27 | 28 | @Test 29 | public void testName() throws Exception { 30 | String baseUrl = System.getProperty("cache.base.url"); 31 | HttpGet get = new HttpGet(baseUrl); 32 | CloseableHttpClient httpClient = HttpClients.createDefault(); 33 | 34 | try (CloseableHttpResponse response = httpClient.execute(get)) { 35 | HttpEntity entity = response.getEntity(); 36 | EntityUtils.consume(entity); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/it/buildargs-it/src/test/java/BuildArgsIT.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Wouter Danes 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | */ 17 | 18 | import org.apache.http.HttpEntity; 19 | import org.apache.http.client.methods.CloseableHttpResponse; 20 | import org.apache.http.client.methods.HttpGet; 21 | import org.apache.http.impl.client.CloseableHttpClient; 22 | import org.apache.http.impl.client.HttpClients; 23 | import org.apache.http.util.EntityUtils; 24 | import org.junit.Test; 25 | 26 | public class BuildArgsIT { 27 | 28 | @Test 29 | public void buildArgsContainerRunsCorrectly() throws Exception { 30 | String baseUrl = System.getProperty("app.base.url"); 31 | HttpGet get = new HttpGet(baseUrl); 32 | CloseableHttpClient httpClient = HttpClients.createDefault(); 33 | 34 | try (CloseableHttpResponse response = httpClient.execute(get)) { 35 | HttpEntity entity = response.getEntity(); 36 | EntityUtils.consume(entity); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/broken-it/push-with-creds-it/src/test/java/NginxIT.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Wouter Danes 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | */ 17 | 18 | import org.apache.http.HttpEntity; 19 | import org.apache.http.client.methods.CloseableHttpResponse; 20 | import org.apache.http.client.methods.HttpGet; 21 | import org.apache.http.impl.client.CloseableHttpClient; 22 | import org.apache.http.impl.client.HttpClients; 23 | import org.apache.http.util.EntityUtils; 24 | import org.junit.Ignore; 25 | import org.junit.Test; 26 | 27 | @Ignore 28 | public class NginxIT { 29 | 30 | @Test 31 | public void testName() throws Exception { 32 | String baseUrl = System.getProperty("cache.base.url"); 33 | HttpGet get = new HttpGet(baseUrl); 34 | CloseableHttpClient httpClient = HttpClients.createDefault(); 35 | 36 | try (CloseableHttpResponse response = httpClient.execute(get)) { 37 | HttpEntity entity = response.getEntity(); 38 | EntityUtils.consume(entity); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/net/wouterdanes/docker/remoteapi/exception/DockerException.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Wouter Danes 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | */ 17 | 18 | package net.wouterdanes.docker.remoteapi.exception; 19 | 20 | import java.util.Optional; 21 | 22 | public class DockerException extends RuntimeException { 23 | 24 | private Optional apiResponse = Optional.empty(); 25 | 26 | public DockerException(final String message, final Throwable cause) { 27 | super(message, cause); 28 | } 29 | 30 | public DockerException(final Throwable e) { 31 | super("Docker internal error occurred", e); 32 | } 33 | 34 | public DockerException(final String message) { 35 | super(message); 36 | } 37 | 38 | public DockerException(final String message, final String apiResponse) { 39 | super(message); 40 | this.apiResponse = Optional.of(apiResponse); 41 | } 42 | 43 | public Optional getApiResponse() { 44 | return apiResponse; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/it/skip-docker-it/src/test/java/NginxIT.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Wouter Danes 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | */ 17 | 18 | import java.lang.IllegalArgumentException; 19 | 20 | import org.apache.http.HttpEntity; 21 | import org.apache.http.client.methods.CloseableHttpResponse; 22 | import org.apache.http.client.methods.HttpGet; 23 | import org.apache.http.impl.client.CloseableHttpClient; 24 | import org.apache.http.impl.client.HttpClients; 25 | import org.apache.http.util.EntityUtils; 26 | import org.junit.Test; 27 | 28 | public class NginxIT { 29 | 30 | @Test(expected = IllegalArgumentException.class) 31 | public void testName() throws Exception { 32 | String baseUrl = System.getProperty("cache.base.url"); 33 | HttpGet get = new HttpGet(baseUrl); 34 | CloseableHttpClient httpClient = HttpClients.createDefault(); 35 | 36 | try (CloseableHttpResponse response = httpClient.execute(get)) { 37 | HttpEntity entity = response.getEntity(); 38 | EntityUtils.consume(entity); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/it/skip-using-property-it/src/test/java/NginxIT.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Wouter Danes 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | */ 17 | 18 | import java.lang.IllegalArgumentException; 19 | 20 | import org.apache.http.HttpEntity; 21 | import org.apache.http.client.methods.CloseableHttpResponse; 22 | import org.apache.http.client.methods.HttpGet; 23 | import org.apache.http.impl.client.CloseableHttpClient; 24 | import org.apache.http.impl.client.HttpClients; 25 | import org.apache.http.util.EntityUtils; 26 | import org.junit.Test; 27 | 28 | public class NginxIT { 29 | 30 | @Test(expected = IllegalArgumentException.class) 31 | public void testName() throws Exception { 32 | String baseUrl = System.getProperty("cache.base.url"); 33 | HttpGet get = new HttpGet(baseUrl); 34 | CloseableHttpClient httpClient = HttpClients.createDefault(); 35 | 36 | try (CloseableHttpResponse response = httpClient.execute(get)) { 37 | HttpEntity entity = response.getEntity(); 38 | EntityUtils.consume(entity); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/net/wouterdanes/docker/remoteapi/model/ContainerCreateResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Wouter Danes 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | */ 17 | 18 | package net.wouterdanes.docker.remoteapi.model; 19 | 20 | import java.util.Collections; 21 | import java.util.List; 22 | 23 | import org.codehaus.jackson.annotate.JsonProperty; 24 | 25 | /** 26 | * See 27 | * http://docs.docker.io/reference/api/docker_remote_api_v1.10/#create-a-container 28 | */ 29 | @SuppressWarnings("unused") 30 | public class ContainerCreateResponse { 31 | 32 | @JsonProperty("Id") 33 | private String id; 34 | @JsonProperty("Warnings") 35 | private List warnings; 36 | 37 | public void setId(final String id) { 38 | this.id = id; 39 | } 40 | 41 | public void setWarnings(final List warnings) { 42 | this.warnings = warnings; 43 | } 44 | 45 | public String getId() { 46 | return id; 47 | } 48 | 49 | public List getWarnings() { 50 | return Collections.unmodifiableList(warnings); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/it/settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 21 | 22 | 23 | 24 | 25 | it-repo 26 | 27 | true 28 | 29 | 30 | 31 | local.central 32 | @localRepositoryUrl@ 33 | 34 | true 35 | 36 | 37 | true 38 | 39 | 40 | 41 | 42 | 43 | local.central 44 | @localRepositoryUrl@ 45 | 46 | true 47 | 48 | 49 | true 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /src/main/java/net/wouterdanes/docker/provider/model/BuiltImageInfo.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Wouter Danes 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | */ 17 | 18 | package net.wouterdanes.docker.provider.model; 19 | 20 | import java.util.Optional; 21 | 22 | /** 23 | * This class holds information about an image that was built so that it can be references in the start goal and 24 | * removed in the stop goal. 25 | */ 26 | public class BuiltImageInfo { 27 | 28 | private final String startId; 29 | private final String imageId; 30 | private final Optional registry; 31 | private final boolean keepAfterStopping; 32 | 33 | public BuiltImageInfo(final String imageId, ImageBuildConfiguration imageConfig) { 34 | this.imageId = imageId; 35 | this.startId = imageConfig.getId(); 36 | this.registry = Optional.ofNullable(imageConfig.getRegistry()); 37 | this.keepAfterStopping = imageConfig.isKeep() || imageConfig.isPush(); 38 | } 39 | 40 | public String getStartId() { 41 | return startId; 42 | } 43 | 44 | public String getImageId() { 45 | return imageId; 46 | } 47 | 48 | public Optional getRegistry() { 49 | return registry; 50 | } 51 | 52 | public boolean shouldKeepAfterStopping() { 53 | return keepAfterStopping; 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/net/wouterdanes/docker/provider/model/ContainerCommitConfiguration.java: -------------------------------------------------------------------------------- 1 | package net.wouterdanes.docker.provider.model; 2 | 3 | import org.apache.maven.plugins.annotations.Parameter; 4 | 5 | public class ContainerCommitConfiguration { 6 | @Parameter 7 | private String id; 8 | 9 | @Parameter 10 | private String repo; 11 | 12 | @Parameter 13 | private String tag; 14 | 15 | @Parameter 16 | private String comment; 17 | 18 | @Parameter 19 | private String author; 20 | 21 | @Parameter(defaultValue = "false") 22 | private boolean push; 23 | 24 | public String getId() { 25 | return id; 26 | } 27 | 28 | public void setId(String id) { 29 | this.id = id; 30 | } 31 | 32 | public String getRepo() { 33 | return repo; 34 | } 35 | 36 | public void setRepo(String repo) { 37 | this.repo = repo; 38 | } 39 | 40 | public String getTag() { 41 | return tag; 42 | } 43 | 44 | public void setTag(String tag) { 45 | this.tag = tag; 46 | } 47 | 48 | public String getComment() { 49 | return comment; 50 | } 51 | 52 | public void setComment(String comment) { 53 | this.comment = comment; 54 | } 55 | 56 | public String getAuthor() { 57 | return author; 58 | } 59 | 60 | public void setAuthor(String author) { 61 | this.author = author; 62 | } 63 | 64 | public boolean isPush() { 65 | return push; 66 | } 67 | 68 | public void setPush(boolean push) { 69 | this.push = push; 70 | } 71 | 72 | @Override 73 | public String toString() { 74 | return "ContainerCommitConfiguration{" + 75 | "id='" + id + '\'' + 76 | ", repo='" + repo + '\'' + 77 | ", tag='" + tag + '\'' + 78 | ", comment='" + comment + '\'' + 79 | ", author='" + author + '\'' + 80 | ", push=" + push + 81 | '}'; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/test/java/net/wouterdanes/docker/maven/AbstractDockerMojoTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Wouter Danes 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | */ 17 | 18 | package net.wouterdanes.docker.maven; 19 | 20 | import org.apache.maven.plugin.MojoExecutionException; 21 | import org.apache.maven.plugin.MojoFailureException; 22 | import org.apache.maven.plugin.logging.Log; 23 | import org.junit.Assert; 24 | import org.junit.Test; 25 | import org.mockito.Mockito; 26 | 27 | import static org.mockito.Mockito.atLeastOnce; 28 | import static org.mockito.Mockito.mock; 29 | import static org.mockito.Mockito.when; 30 | 31 | public class AbstractDockerMojoTest { 32 | 33 | @Test 34 | public void testThatMojoIsNotExecutedWhenSkipIsSet() throws Exception { 35 | AbstractDockerMojo mojo = new AbstractDockerMojo() { 36 | @Override 37 | protected void doExecute() throws MojoExecutionException, MojoFailureException { 38 | Assert.fail("doExecute should not be called"); 39 | } 40 | }; 41 | 42 | mojo.setSkip(true); 43 | mojo.execute(); 44 | } 45 | 46 | @Test 47 | public void testThatMojoIsExecutedWhenSkipIsNotSet() throws Exception { 48 | AbstractDockerMojo mojo = Mockito.mock(AbstractDockerMojo.class); 49 | when(mojo.getLog()).thenReturn(mock(Log.class)); 50 | 51 | mojo.execute(); 52 | 53 | Mockito.verify(mojo, atLeastOnce()).doExecute(); 54 | } 55 | } -------------------------------------------------------------------------------- /src/main/java/net/wouterdanes/docker/provider/model/ImageTagConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Lachlan Coote 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | */ 17 | 18 | package net.wouterdanes.docker.provider.model; 19 | 20 | import java.util.List; 21 | 22 | import org.apache.maven.plugins.annotations.Parameter; 23 | 24 | /** 25 | * This class is responsible for holding the configuration to assign one or more tags to a single 26 | * docker image within the {@link net.wouterdanes.docker.maven.TagImageMojo} 27 | */ 28 | public class ImageTagConfiguration { 29 | 30 | @Parameter(required = true) 31 | private String id; 32 | 33 | @Parameter(required = true) 34 | private List tags; 35 | 36 | @Parameter(defaultValue = "false") 37 | private boolean push; 38 | 39 | @Parameter 40 | private String registry; 41 | 42 | public String getId() { 43 | return id; 44 | } 45 | 46 | public void setId(final String id) { 47 | this.id = id; 48 | } 49 | 50 | public List getTags() { 51 | return tags; 52 | } 53 | 54 | public void setTags(List tags) { 55 | this.tags = tags; 56 | } 57 | 58 | public boolean isPush() { 59 | return push; 60 | } 61 | 62 | public void setPush(boolean push) { 63 | this.push = push; 64 | } 65 | 66 | public String getRegistry() { 67 | return registry; 68 | } 69 | 70 | public void setRegistry(String registry) { 71 | this.registry = registry; 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/net/wouterdanes/docker/remoteapi/model/DockerVersionInfo.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Wouter Danes 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | */ 17 | 18 | package net.wouterdanes.docker.remoteapi.model; 19 | 20 | import org.codehaus.jackson.annotate.JsonProperty; 21 | 22 | /** 23 | * This class wraps the "version" response of the docker api. 24 | */ 25 | public class DockerVersionInfo { 26 | 27 | @JsonProperty("ApiVersion") 28 | private String apiVersion; 29 | 30 | @JsonProperty("Arch") 31 | private String architecture; 32 | 33 | @JsonProperty("GitCommit") 34 | private String gitCommitHash; 35 | 36 | @JsonProperty("GoVersion") 37 | private String goVersion; 38 | 39 | @JsonProperty("KernelVersion") 40 | private String kernelVersion; 41 | 42 | @JsonProperty("Os") 43 | private String os; 44 | 45 | @JsonProperty("Version") 46 | private String dockerVersion; 47 | 48 | public String getApiVersion() { 49 | return apiVersion; 50 | } 51 | 52 | public String getArchitecture() { 53 | return architecture; 54 | } 55 | 56 | public String getGitCommitHash() { 57 | return gitCommitHash; 58 | } 59 | 60 | public String getGoVersion() { 61 | return goVersion; 62 | } 63 | 64 | public String getKernelVersion() { 65 | return kernelVersion; 66 | } 67 | 68 | public String getOs() { 69 | return os; 70 | } 71 | 72 | public String getDockerVersion() { 73 | return dockerVersion; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/test/java/net/wouterdanes/docker/maven/VerifyMojoTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Wouter Danes 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | */ 17 | 18 | package net.wouterdanes.docker.maven; 19 | 20 | import java.util.Collections; 21 | import java.util.HashMap; 22 | import java.util.Map; 23 | 24 | import org.apache.maven.plugin.MojoFailureException; 25 | import org.apache.maven.plugin.logging.Log; 26 | import org.junit.Before; 27 | import org.junit.Test; 28 | import org.mockito.Matchers; 29 | import org.mockito.Mockito; 30 | 31 | public class VerifyMojoTest { 32 | 33 | private VerifyMojo mojo; 34 | private Map fakePluginContext; 35 | 36 | @Before 37 | public void setUp() throws Exception { 38 | 39 | mojo = new VerifyMojo(); 40 | fakePluginContext = new HashMap<>(); 41 | mojo.setPluginContext(fakePluginContext); 42 | 43 | } 44 | 45 | @Test(expected = MojoFailureException.class) 46 | public void testThatBuildFailsAndLogsAnErrorWhenErrorsHaveHappened() throws Exception { 47 | 48 | DockerPluginError error = new DockerPluginError("some-goal", "Something went wrong"); 49 | fakePluginContext.put("errors", Collections.singletonList(error)); 50 | Log fakeLog = Mockito.mock(Log.class); 51 | mojo.setLog(fakeLog); 52 | 53 | mojo.execute(); 54 | 55 | Mockito.verify(fakeLog, Mockito.atLeastOnce()).error(Matchers.anyString()); 56 | } 57 | 58 | @Test 59 | public void testThatBuildDoesNotFailWhenNoErrorsHaveHappened() throws Exception { 60 | 61 | mojo.execute(); 62 | 63 | } 64 | } -------------------------------------------------------------------------------- /src/main/java/net/wouterdanes/docker/maven/VerifyMojo.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Wouter Danes 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | */ 17 | 18 | package net.wouterdanes.docker.maven; 19 | 20 | import org.apache.maven.plugin.MojoExecutionException; 21 | import org.apache.maven.plugin.MojoFailureException; 22 | import org.apache.maven.plugins.annotations.InstantiationStrategy; 23 | import org.apache.maven.plugins.annotations.LifecyclePhase; 24 | import org.apache.maven.plugins.annotations.Mojo; 25 | 26 | import java.util.List; 27 | import java.util.stream.Collectors; 28 | 29 | /** 30 | * This {@link org.apache.maven.plugin.Mojo} checks for any errors in the Docker Maven Plugin during the previous build 31 | * phases and fails the build if any errors have happened. 32 | */ 33 | @Mojo(name = "verify", defaultPhase = LifecyclePhase.VERIFY, threadSafe = true, 34 | instantiationStrategy = InstantiationStrategy.PER_LOOKUP) 35 | public class VerifyMojo extends AbstractDockerMojo { 36 | @Override 37 | protected void doExecute() throws MojoExecutionException, MojoFailureException { 38 | 39 | List errors = getPluginErrors(); 40 | if (!errors.isEmpty()) { 41 | List erroneousGoals = errors.stream() 42 | .map(DockerPluginError::getPluginGoal) 43 | .collect(Collectors.toList()); 44 | 45 | StringBuilder sb = new StringBuilder("The following plugin goals had errors: \n"); 46 | for (String goal : erroneousGoals) { 47 | sb.append(" - ").append(goal).append('\n'); 48 | } 49 | getLog().error(sb); 50 | 51 | throw new MojoFailureException("Errors occurred, stopping the build"); 52 | } 53 | 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/test/java/net/wouterdanes/docker/VerifyPushedImagesIT.java: -------------------------------------------------------------------------------- 1 | package net.wouterdanes.docker; 2 | 3 | import java.util.Map; 4 | 5 | import javax.ws.rs.client.ClientBuilder; 6 | import javax.ws.rs.client.WebTarget; 7 | import javax.ws.rs.core.GenericType; 8 | import javax.ws.rs.core.MediaType; 9 | 10 | import org.glassfish.jersey.client.ClientConfig; 11 | import org.glassfish.jersey.jackson.JacksonFeature; 12 | import org.junit.Assert; 13 | import org.junit.Before; 14 | import org.junit.Ignore; 15 | import org.junit.Test; 16 | 17 | /** 18 | * This Integration Test checks the registry to see if all images are pushed by the IT POMs. 19 | */ 20 | @Ignore 21 | public class VerifyPushedImagesIT { 22 | 23 | private WebTarget repositories; 24 | 25 | @Before 26 | public void setUp() throws Exception { 27 | 28 | String registryUri = System.getProperty("docker.registry"); 29 | ClientConfig config = new ClientConfig(new JacksonFeature()); 30 | repositories = ClientBuilder.newClient(config).target(registryUri).path("v1/repositories"); 31 | 32 | } 33 | 34 | @Test 35 | public void testThatPushWithCredsItImagesGotPushed() throws Exception { 36 | 37 | assertThatImageExists("drek", "latest", "push-with-creds-it"); 38 | assertThatImageExists("nginxier", "latest", "push-with-creds-it"); 39 | 40 | } 41 | 42 | @Test 43 | public void testThatPushWithExplicitRegistryItImagesGotPushed() throws Exception { 44 | 45 | assertThatImageExists("corgis", "latest", "push-with-explicit-registry-it"); 46 | 47 | } 48 | 49 | @Test 50 | public void testThatTagAndPushItImagesGotPushed() throws Exception { 51 | 52 | assertThatImageExists("dross", "snapshot", "tag-and-push-it"); 53 | assertThatImageExists("dross", "release", "tag-and-push-it"); 54 | assertThatImageExists("dross", "4.1", "tag-and-push-it"); 55 | 56 | } 57 | 58 | private void assertThatImageExists(String name, String tag, String itName) { 59 | 60 | Map tags = repositories.path(name).path("tags") 61 | .request(MediaType.APPLICATION_JSON_TYPE) 62 | .get(new GenericType>() { 63 | }); 64 | 65 | Assert.assertTrue(String.format("Integration test '%s' should push image '%s:%s'.", itName, name, tag), 66 | tags.containsKey(tag)); 67 | 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/net/wouterdanes/docker/maven/StopContainerMojo.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Wouter Danes 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | */ 17 | 18 | package net.wouterdanes.docker.maven; 19 | 20 | import org.apache.maven.plugin.MojoExecutionException; 21 | import org.apache.maven.plugin.MojoFailureException; 22 | import org.apache.maven.plugins.annotations.LifecyclePhase; 23 | import org.apache.maven.plugins.annotations.Mojo; 24 | 25 | import net.wouterdanes.docker.provider.model.BuiltImageInfo; 26 | import net.wouterdanes.docker.remoteapi.exception.DockerException; 27 | 28 | /** 29 | * This class is responsible for stopping the docker containers that were started by the plugin. The goal 30 | * is called "stop-containers" and it's executed in the "post-integration-test" phase. 31 | */ 32 | @Mojo(name = "stop-containers", threadSafe = true, defaultPhase = LifecyclePhase.POST_INTEGRATION_TEST) 33 | public class StopContainerMojo extends AbstractPreVerifyDockerMojo { 34 | 35 | @Override 36 | public void doExecute() throws MojoExecutionException, MojoFailureException { 37 | cleanUpStartedContainers(); 38 | 39 | for (BuiltImageInfo image : getBuiltImages()) { 40 | if (image.shouldKeepAfterStopping()) { 41 | getLog().info(String.format("Keeping image %s", image.getImageId())); 42 | continue; 43 | } 44 | 45 | getLog().info(String.format("Removing image '%s' (%s) ...", image.getImageId(), image.getStartId())); 46 | 47 | try { 48 | getDockerProvider().removeImage(image.getImageId()); 49 | } catch (DockerException e) { 50 | getLog().error("Failed to remove image", e); 51 | } 52 | } 53 | } 54 | 55 | @Override 56 | protected String getMojoGoalName() { 57 | return "stop-containers"; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/net/wouterdanes/docker/remoteapi/model/Credentials.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Wouter Danes 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | */ 17 | 18 | package net.wouterdanes.docker.remoteapi.model; 19 | 20 | import org.codehaus.jackson.annotate.JsonProperty; 21 | 22 | import static java.util.Optional.ofNullable; 23 | import static org.apache.commons.lang3.Validate.notBlank; 24 | 25 | public class Credentials { 26 | 27 | public static final String DEFAULT_SERVER_NAME = "https://index.docker.io/v1/"; 28 | 29 | @JsonProperty("username") 30 | private final String userName; 31 | @JsonProperty 32 | private final String password; 33 | @JsonProperty 34 | private final String email; 35 | @JsonProperty("serveraddress") 36 | private final String serverAddress; 37 | 38 | public Credentials(String userName, String password, String email, String serverAddress) { 39 | notBlank(userName, "Username was null or empty"); 40 | notBlank(password, "Password was null or empty"); 41 | notBlank(email, "Emails was null or empty"); 42 | 43 | this.userName = userName; 44 | this.password = password; 45 | this.email = email; 46 | this.serverAddress = ofNullable(serverAddress).orElse(DEFAULT_SERVER_NAME); 47 | } 48 | 49 | public String getUserName() { 50 | return userName; 51 | } 52 | 53 | public String getEmail() { 54 | return email; 55 | } 56 | 57 | public String getPassword() { 58 | return password; 59 | } 60 | 61 | public String getServerAddress() { 62 | return serverAddress; 63 | } 64 | 65 | @Override 66 | public String toString() { 67 | return "Credentials [userName=" + userName 68 | + ", password=********" 69 | + ", email=" + email 70 | + ", serverAddress=" + serverAddress 71 | + "]"; 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/net/wouterdanes/docker/provider/model/PushableImage.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Lachlan Coote 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | */ 17 | 18 | package net.wouterdanes.docker.provider.model; 19 | 20 | import java.util.Objects; 21 | import java.util.Optional; 22 | 23 | import static org.apache.commons.lang3.Validate.notBlank; 24 | import static org.apache.commons.lang3.Validate.notNull; 25 | 26 | /** 27 | * Holds information about an image (or tag thereof) to be pushed at a later stage. 28 | */ 29 | public class PushableImage { 30 | 31 | private final String imageId; 32 | private final Optional nameAndTag; 33 | 34 | public PushableImage(final String imageId, final Optional nameAndTag) { 35 | notBlank(imageId, "Image id was null or empty"); 36 | notNull(nameAndTag.orElse(""), "Name and tag was null or empty"); 37 | 38 | this.imageId = imageId; 39 | this.nameAndTag = nameAndTag; 40 | } 41 | 42 | public String getImageId() { 43 | return imageId; 44 | } 45 | 46 | public Optional getNameAndTag() { 47 | return nameAndTag; 48 | } 49 | 50 | @Override 51 | public int hashCode() { 52 | return Objects.hash(imageId, nameAndTag); 53 | } 54 | 55 | @Override 56 | public boolean equals(Object obj) { 57 | if (this == obj) { 58 | return true; 59 | } 60 | 61 | if (!(obj instanceof PushableImage)) { 62 | return false; 63 | } 64 | 65 | PushableImage other = (PushableImage) obj; 66 | return imageId.equals(other.getImageId()) && nameAndTag.equals(other.getNameAndTag()); 67 | } 68 | 69 | @Override 70 | public String toString() { 71 | return "PushableImage[" 72 | + "imageId=" + imageId 73 | + ", nameAndTag=" + nameAndTag.orElse("") 74 | + "]"; 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /src/test/java/net/wouterdanes/docker/remoteapi/BuiltImageInfoTest.java: -------------------------------------------------------------------------------- 1 | package net.wouterdanes.docker.remoteapi; 2 | 3 | import net.wouterdanes.docker.provider.model.BuiltImageInfo; 4 | import net.wouterdanes.docker.provider.model.ImageBuildConfiguration; 5 | import org.junit.Test; 6 | 7 | import java.util.Optional; 8 | 9 | import static org.junit.Assert.assertEquals; 10 | 11 | public class BuiltImageInfoTest { 12 | 13 | private static final boolean KEEP = true; 14 | private static final boolean DONT_KEEP = false; 15 | 16 | private static final boolean PUSH = true; 17 | private static final boolean DONT_PUSH = false; 18 | 19 | private static final String IMAGEID = "x56543d5"; 20 | private static final String STARTID = "start"; 21 | private static final String REGISTRYID = "registry"; 22 | 23 | private static final Optional NON_NULL_REGISTRY = Optional.ofNullable(REGISTRYID); 24 | private static final Optional NULL_REGISTRY = Optional.empty(); 25 | 26 | @Test 27 | public void testConstruction() { 28 | assertExpected(NON_NULL_REGISTRY, DONT_KEEP, makeTarget(REGISTRYID, DONT_KEEP, DONT_PUSH)); 29 | assertExpected(NON_NULL_REGISTRY, KEEP, makeTarget(REGISTRYID, DONT_KEEP, PUSH)); 30 | assertExpected(NON_NULL_REGISTRY, KEEP, makeTarget(REGISTRYID, KEEP, PUSH)); 31 | assertExpected(NON_NULL_REGISTRY, KEEP, makeTarget(REGISTRYID, KEEP, DONT_PUSH)); 32 | 33 | assertExpected(NULL_REGISTRY, DONT_KEEP, makeTarget(null, DONT_KEEP, DONT_KEEP)); 34 | } 35 | 36 | private BuiltImageInfo makeTarget(String registry, boolean keep, boolean push) { 37 | return new BuiltImageInfo(IMAGEID, makeConfiguration(registry, keep, push)); 38 | } 39 | 40 | private ImageBuildConfiguration makeConfiguration(String registry, boolean keep, boolean push) { 41 | ImageBuildConfiguration config = new ImageBuildConfiguration(); 42 | config.setId(STARTID); 43 | config.setKeep(keep); 44 | config.setPush(push); 45 | config.setRegistry(registry); 46 | return config; 47 | } 48 | 49 | private void assertExpected(Optional expectedRegistry, 50 | boolean expectedKeep, 51 | BuiltImageInfo actual) { 52 | assertEquals(IMAGEID, actual.getImageId()); 53 | assertEquals(STARTID, actual.getStartId()); 54 | assertEquals(expectedRegistry, actual.getRegistry()); 55 | assertEquals(expectedKeep, actual.shouldKeepAfterStopping()); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/net/wouterdanes/docker/provider/DockerProviderSupplier.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Wouter Danes 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | */ 17 | 18 | package net.wouterdanes.docker.provider; 19 | 20 | import java.util.HashMap; 21 | import java.util.Map; 22 | import java.util.Optional; 23 | import java.util.function.Supplier; 24 | 25 | /** 26 | * This class creates a docker provider based on the passed name when the get() method is called. Used in for example 27 | * {@link Optional#orElseGet(Supplier)}} 28 | */ 29 | public class DockerProviderSupplier implements Supplier { 30 | 31 | private final String providerName; 32 | private static volatile Map> providers = new HashMap<>(); 33 | 34 | static { 35 | providers.put("remote", RemoteDockerProvider.class); 36 | providers.put("local", LocalDockerProvider.class); 37 | } 38 | 39 | public DockerProviderSupplier(final String providerName) { 40 | this.providerName = providerName; 41 | } 42 | 43 | public static void registerProvider(String name, Class providerClass) { 44 | assert providerClass != null; 45 | providers.put(name, providerClass); 46 | } 47 | 48 | public static void removeProvider(String name) { 49 | assert name != null; 50 | providers.remove(name); 51 | } 52 | 53 | @Override 54 | public DockerProvider get() { 55 | if (providers.containsKey(providerName)) { 56 | Class providerClass = providers.get(providerName); 57 | try { 58 | return providerClass.newInstance(); 59 | } catch (InstantiationException | IllegalAccessException e) { 60 | throw new IllegalStateException("Can't instantiate provider", e); 61 | } 62 | } 63 | throw new IllegalStateException(String.format("No provider known by name '%s'", providerName)); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/test/java/net/wouterdanes/docker/remoteapi/PushableImageTest.java: -------------------------------------------------------------------------------- 1 | package net.wouterdanes.docker.remoteapi; 2 | 3 | import net.wouterdanes.docker.provider.model.PushableImage; 4 | import org.junit.Before; 5 | import org.junit.Test; 6 | 7 | import java.util.Optional; 8 | 9 | import static org.junit.Assert.*; 10 | 11 | public class PushableImageTest { 12 | 13 | private static final String IMAGE1 = "red"; 14 | private static final String IMAGE2 = "blue"; 15 | 16 | private static final String TAG1 = "oldest"; 17 | private static final String TAG2 = "localhost:5000/fred/bluey:middlest"; 18 | 19 | private PushableImage targetWithTag; 20 | private PushableImage targetWithoutTag; 21 | 22 | @Before 23 | public void setUp() throws Exception { 24 | targetWithTag = make(IMAGE1, TAG1); 25 | targetWithoutTag = make(IMAGE1, null); 26 | } 27 | 28 | @Test 29 | public void testConstruction() { 30 | assertExpected(Optional.ofNullable(TAG1), targetWithTag); 31 | assertExpected(Optional.empty(), targetWithoutTag); 32 | } 33 | 34 | @Test 35 | public void testHashCode() { 36 | assertEquals(make(IMAGE1, TAG1).hashCode(), targetWithTag.hashCode()); 37 | assertEquals(make(IMAGE1, null).hashCode(), targetWithoutTag.hashCode()); 38 | } 39 | 40 | @Test 41 | public void testEquals() { 42 | // trivial cases 43 | assertTrue(targetWithTag.equals(targetWithTag)); 44 | assertNotNull(targetWithTag); 45 | assertFalse(targetWithTag.equals(new Object())); 46 | 47 | // when equivalent 48 | assertTrue(targetWithTag.equals(make(IMAGE1, TAG1))); 49 | assertTrue(targetWithoutTag.equals(make(IMAGE1, null))); 50 | 51 | // when not equivalent 52 | // - Image not same 53 | assertFalse(targetWithTag.equals(make(IMAGE2, TAG1))); 54 | // - Reg not same 55 | assertFalse(targetWithTag.equals(make(IMAGE1, TAG2))); 56 | assertFalse(targetWithTag.equals(targetWithoutTag)); 57 | assertFalse(targetWithoutTag.equals(targetWithTag)); 58 | 59 | } 60 | 61 | private PushableImage make(String imageId, String registry) { 62 | return new PushableImage(imageId, Optional.ofNullable(registry)); 63 | } 64 | 65 | private void assertExpected(Optional expectedRegistry, 66 | PushableImage actual) { 67 | assertEquals(PushableImageTest.IMAGE1, actual.getImageId()); 68 | assertEquals(expectedRegistry, actual.getNameAndTag()); 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /src/test/java/net/wouterdanes/docker/maven/CommitContainerMojoTest.java: -------------------------------------------------------------------------------- 1 | package net.wouterdanes.docker.maven; 2 | 3 | import net.wouterdanes.docker.provider.AbstractFakeDockerProvider; 4 | import net.wouterdanes.docker.provider.DockerProviderSupplier; 5 | import net.wouterdanes.docker.provider.model.ContainerCommitConfiguration; 6 | import org.junit.After; 7 | import org.junit.Before; 8 | import org.junit.Test; 9 | import org.mockito.Mockito; 10 | 11 | import java.util.ArrayList; 12 | import java.util.HashMap; 13 | import java.util.List; 14 | import java.util.UUID; 15 | 16 | import static org.mockito.Matchers.any; 17 | import static org.mockito.Mockito.mock; 18 | 19 | public class CommitContainerMojoTest { 20 | 21 | private final String fakeProviderKey = UUID.randomUUID().toString(); 22 | private CommitContainerMojo mojo; 23 | 24 | @Before 25 | public void setUp() throws Exception { 26 | 27 | FakeDockerProvider.instance = mock(FakeDockerProvider.class); 28 | DockerProviderSupplier.registerProvider(fakeProviderKey, FakeDockerProvider.class); 29 | 30 | mojo = new CommitContainerMojo(); 31 | mojo.setPluginContext(new HashMap()); 32 | 33 | mojo.setProviderName(fakeProviderKey); 34 | } 35 | 36 | @After 37 | public void tearDown() throws Exception { 38 | 39 | DockerProviderSupplier.removeProvider(fakeProviderKey); 40 | 41 | } 42 | 43 | @Test 44 | public void testNoConfigurationSpecified() throws Exception { 45 | 46 | mojo.execute(); 47 | Mockito.verify(FakeDockerProvider.instance, Mockito.never()).commitContainer(any(ContainerCommitConfiguration.class)); 48 | 49 | mojo.setConfiguration(new ArrayList<>()); 50 | mojo.execute(); 51 | Mockito.verify(FakeDockerProvider.instance, Mockito.never()).commitContainer(any(ContainerCommitConfiguration.class)); 52 | } 53 | 54 | @Test 55 | public void testNoIdSpecified() throws Exception { 56 | 57 | List list = new ArrayList<>(); 58 | list.add(new ContainerCommitConfiguration()); 59 | mojo.setConfiguration(list); 60 | 61 | mojo.execute(); 62 | 63 | Mockito.verify(FakeDockerProvider.instance, Mockito.never()).commitContainer(any(ContainerCommitConfiguration.class)); 64 | } 65 | 66 | public static class FakeDockerProvider extends AbstractFakeDockerProvider { 67 | 68 | private static FakeDockerProvider instance; 69 | 70 | @Override 71 | protected AbstractFakeDockerProvider getInstance() { 72 | return instance; 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/it/container-logs-it/src/test/java/net/wouterdanes/ApplicationIT.groovy: -------------------------------------------------------------------------------- 1 | package net.wouterdanes 2 | 3 | import groovy.json.JsonBuilder 4 | import groovy.json.JsonSlurper 5 | import org.slf4j.Logger 6 | import org.slf4j.LoggerFactory 7 | import spock.lang.Specification 8 | 9 | import javax.ws.rs.client.ClientBuilder 10 | import javax.ws.rs.client.WebTarget 11 | import javax.ws.rs.core.MediaType 12 | 13 | import static javax.ws.rs.client.Entity.entity 14 | 15 | class ApplicationIT extends Specification { 16 | 17 | static Logger log = LoggerFactory.getLogger(ApplicationIT) 18 | 19 | WebTarget app1; 20 | WebTarget app2; 21 | 22 | void setup() { 23 | 24 | def appBase1 = System.getProperty('app.base.1') 25 | app1 = ClientBuilder.newClient().target(appBase1) 26 | 27 | def appBase2 = System.getProperty('app.base.2') 28 | app2 = ClientBuilder.newClient().target(appBase2) 29 | 30 | } 31 | 32 | def "Posts can be added"() { 33 | 34 | when: 35 | def message = new JsonBuilder([ 36 | body: 'TEST BODY' 37 | ]) 38 | def response = app1.path('posts') 39 | .request().buildPost(entity(message.toString(), MediaType.APPLICATION_JSON_TYPE)) 40 | .invoke() 41 | 42 | then: 43 | response.status == 201 44 | 45 | cleanup: 46 | response.close() 47 | 48 | } 49 | 50 | def "Posts can be looked up"() { 51 | 52 | when: 53 | def response = app1.path('posts').request(MediaType.APPLICATION_JSON_TYPE).get() 54 | 55 | then: 56 | response.status == 404 || response.status == 200 57 | 58 | cleanup: 59 | response.close() 60 | 61 | } 62 | 63 | def "The last post posted is the first post returned from another node"() { 64 | 65 | when: 66 | def messageBody = UUID.randomUUID().toString() 67 | 68 | def message = new JsonBuilder([ 69 | body: messageBody 70 | ]).toString() 71 | 72 | def payload = entity(message, MediaType.APPLICATION_JSON_TYPE) 73 | 74 | app1.path('posts').request().post(payload).close() 75 | 76 | def response = app2.path('posts').request(MediaType.APPLICATION_JSON_TYPE).get() 77 | def result = response.readEntity(String) 78 | 79 | def json = new JsonSlurper().parseText(result) 80 | 81 | then: 82 | json[0].message.body == messageBody 83 | 84 | } 85 | 86 | def "The message service returns the APP_MESSAGE enviroment variable"() { 87 | 88 | when: 89 | def app1Response = app1.path('message').request().get(String) 90 | def app2Response = app2.path('message').request().get(String) 91 | 92 | then: 93 | app1Response == 'Hello, world!' 94 | app2Response == 'I am, so I message' 95 | 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/it/sample-api-mongo-it/src/test/java/net/wouterdanes/ApplicationIT.groovy: -------------------------------------------------------------------------------- 1 | package net.wouterdanes 2 | 3 | import groovy.json.JsonBuilder 4 | import groovy.json.JsonSlurper 5 | import org.slf4j.Logger 6 | import org.slf4j.LoggerFactory 7 | import spock.lang.Specification 8 | 9 | import javax.ws.rs.client.ClientBuilder 10 | import javax.ws.rs.client.WebTarget 11 | import javax.ws.rs.core.MediaType 12 | 13 | import static javax.ws.rs.client.Entity.entity 14 | 15 | class ApplicationIT extends Specification { 16 | 17 | static Logger log = LoggerFactory.getLogger(ApplicationIT) 18 | 19 | WebTarget app1; 20 | WebTarget app2; 21 | 22 | void setup() { 23 | 24 | def appBase1 = System.getProperty('app.base.1') 25 | app1 = ClientBuilder.newClient().target(appBase1) 26 | 27 | def appBase2 = System.getProperty('app.base.2') 28 | app2 = ClientBuilder.newClient().target(appBase2) 29 | 30 | } 31 | 32 | def "Posts can be added"() { 33 | 34 | when: 35 | def message = new JsonBuilder([ 36 | body: 'TEST BODY' 37 | ]) 38 | def response = app1.path('posts') 39 | .request().buildPost(entity(message.toString(), MediaType.APPLICATION_JSON_TYPE)) 40 | .invoke() 41 | 42 | then: 43 | response.status == 201 44 | 45 | cleanup: 46 | response.close() 47 | 48 | } 49 | 50 | def "Posts can be looked up"() { 51 | 52 | when: 53 | def response = app1.path('posts').request(MediaType.APPLICATION_JSON_TYPE).get() 54 | 55 | then: 56 | response.status == 404 || response.status == 200 57 | 58 | cleanup: 59 | response.close() 60 | 61 | } 62 | 63 | def "The last post posted is the first post returned from another node"() { 64 | 65 | when: 66 | def messageBody = UUID.randomUUID().toString() 67 | 68 | def message = new JsonBuilder([ 69 | body: messageBody 70 | ]).toString() 71 | 72 | def payload = entity(message, MediaType.APPLICATION_JSON_TYPE) 73 | 74 | app1.path('posts').request().post(payload).close() 75 | 76 | def response = app2.path('posts').request(MediaType.APPLICATION_JSON_TYPE).get() 77 | def result = response.readEntity(String) 78 | 79 | def json = new JsonSlurper().parseText(result) 80 | 81 | then: 82 | json[0].message.body == messageBody 83 | 84 | } 85 | 86 | def "The message service returns the APP_MESSAGE enviroment variable"() { 87 | 88 | when: 89 | def app1Response = app1.path('message').request().get(String) 90 | def app2Response = app2.path('message').request().get(String) 91 | 92 | then: 93 | app1Response == 'Hello, world!' 94 | app2Response == 'I am, so I message' 95 | 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/test/java/net/wouterdanes/docker/provider/RemoteDockerProviderTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Wouter Danes 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | */ 17 | 18 | package net.wouterdanes.docker.provider; 19 | 20 | import java.net.URI; 21 | 22 | import org.junit.Before; 23 | import org.junit.Test; 24 | 25 | import junit.framework.Assert; 26 | 27 | public class RemoteDockerProviderTest { 28 | @Before 29 | public void setUp() throws Exception { 30 | System.getProperties().remove(RemoteDockerProvider.DOCKER_HOST_PROPERTY); 31 | System.getProperties().remove(RemoteDockerProvider.DOCKER_PORT_PROPERTY); 32 | } 33 | 34 | @Test 35 | public void testThatDockerHostAndDockerPortOverride() throws Exception { 36 | 37 | System.setProperty(RemoteDockerProvider.DOCKER_HOST_PROPERTY, "lalahost"); 38 | System.setProperty(RemoteDockerProvider.DOCKER_PORT_PROPERTY, "1337"); 39 | 40 | RemoteDockerProvider provider = new RemoteDockerProvider(); 41 | 42 | Assert.assertEquals(String.format(provider.getClass().getName() + "{host='lalahost', port=1337}"), provider.toString()); 43 | 44 | } 45 | 46 | @Test 47 | public void testThatEnvironmentVariableOrDefaultIsPickedUp() throws Exception { 48 | 49 | String expectedHost = "127.0.0.1"; 50 | int expectedPort = 2375; 51 | 52 | // Can't really mock this easily, so i went with parsing my own system env here 53 | // TODO: Add PowerMock 54 | String env = System.getenv(RemoteDockerProvider.DOCKER_HOST_SYSTEM_ENV); 55 | try { 56 | URI dockerUrl = URI.create(env); 57 | if ("tcp".equalsIgnoreCase(dockerUrl.getScheme())) { 58 | expectedHost = dockerUrl.getHost(); 59 | expectedPort = dockerUrl.getPort(); 60 | } 61 | } catch (NullPointerException | IllegalArgumentException ignored) { 62 | } 63 | 64 | RemoteDockerProvider provider = new RemoteDockerProvider(); 65 | 66 | String expectedValue = String.format(provider.getClass().getName() + "{host='%s', port=%s}", expectedHost, expectedPort); 67 | Assert.assertEquals(expectedValue, provider.toString()); 68 | 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/test/java/net/wouterdanes/docker/maven/PushImageMojoTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Wouter Danes 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | */ 17 | 18 | package net.wouterdanes.docker.maven; 19 | 20 | import net.wouterdanes.docker.provider.AbstractFakeDockerProvider; 21 | import net.wouterdanes.docker.provider.DockerProviderSupplier; 22 | import net.wouterdanes.docker.provider.model.ImageBuildConfiguration; 23 | import org.apache.maven.plugin.MojoFailureException; 24 | import org.junit.After; 25 | import org.junit.Before; 26 | import org.junit.Test; 27 | import org.mockito.Matchers; 28 | 29 | import java.util.HashMap; 30 | import java.util.Optional; 31 | import java.util.UUID; 32 | 33 | import static org.mockito.Mockito.*; 34 | 35 | public class PushImageMojoTest { 36 | 37 | private final String fakeProviderKey = UUID.randomUUID().toString(); 38 | private PushImageMojo mojo; 39 | 40 | @Before 41 | public void setUp() throws Exception { 42 | 43 | FakeDockerProvider.instance = mock(FakeDockerProvider.class); 44 | DockerProviderSupplier.registerProvider(fakeProviderKey, FakeDockerProvider.class); 45 | 46 | mojo = new PushImageMojo(); 47 | mojo.setPluginContext(new HashMap()); 48 | 49 | mojo.setProviderName(fakeProviderKey); 50 | } 51 | 52 | @After 53 | public void tearDown() throws Exception { 54 | 55 | DockerProviderSupplier.removeProvider(fakeProviderKey); 56 | 57 | } 58 | 59 | @Test(expected = MojoFailureException.class) 60 | public void testThatImagesThatHaveNoNameCauseAnError() throws Exception { 61 | 62 | mojo.enqueueForPushing("some-image-id", Optional.empty()); 63 | mojo.enqueueForPushing("another-image-id", new ImageBuildConfiguration()); 64 | mojo.execute(); 65 | 66 | } 67 | 68 | @Test 69 | public void testThatNoImagesArePushedWhenThereAreNoImagesMarkedToPush() throws Exception { 70 | 71 | mojo.execute(); 72 | 73 | verify(FakeDockerProvider.instance, never()).pushImage(Matchers.any()); 74 | 75 | } 76 | 77 | public static class FakeDockerProvider extends AbstractFakeDockerProvider { 78 | 79 | private static FakeDockerProvider instance; 80 | 81 | @Override 82 | protected AbstractFakeDockerProvider getInstance() { 83 | return instance; 84 | } 85 | } 86 | } -------------------------------------------------------------------------------- /src/main/java/net/wouterdanes/docker/maven/TagImageMojo.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Lachlan Coote 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | */ 17 | 18 | package net.wouterdanes.docker.maven; 19 | 20 | import net.wouterdanes.docker.provider.model.BuiltImageInfo; 21 | import net.wouterdanes.docker.provider.model.ImageTagConfiguration; 22 | import org.apache.maven.plugin.MojoExecutionException; 23 | import org.apache.maven.plugin.MojoFailureException; 24 | import org.apache.maven.plugins.annotations.InstantiationStrategy; 25 | import org.apache.maven.plugins.annotations.LifecyclePhase; 26 | import org.apache.maven.plugins.annotations.Mojo; 27 | import org.apache.maven.plugins.annotations.Parameter; 28 | 29 | import java.util.List; 30 | import java.util.Optional; 31 | 32 | import static java.util.Optional.ofNullable; 33 | 34 | /** 35 | * This class is responsible for tagging docking images in the install phase of the maven build. The goal is called 36 | * "tag-images" 37 | */ 38 | @Mojo(defaultPhase = LifecyclePhase.INSTALL, name = "tag-images", threadSafe = true, 39 | instantiationStrategy = InstantiationStrategy.PER_LOOKUP) 40 | public class TagImageMojo extends AbstractDockerMojo { 41 | 42 | @Parameter(required = true) 43 | private List images; 44 | 45 | public void setImages(final List images) { 46 | this.images = images; 47 | } 48 | 49 | @Override 50 | protected void doExecute() throws MojoExecutionException, MojoFailureException { 51 | for (ImageTagConfiguration config : images) { 52 | if (!config.getTags().isEmpty()) { 53 | applyTagsToImage(config); 54 | } 55 | } 56 | } 57 | 58 | private void applyTagsToImage(ImageTagConfiguration config) throws MojoFailureException { 59 | String imageId = config.getId(); 60 | boolean push = config.isPush(); 61 | Optional registry = Optional.ofNullable(config.getRegistry()); 62 | 63 | Optional builtInfo = getBuiltImageForStartId(imageId); 64 | if (builtInfo.isPresent()) { 65 | imageId = builtInfo.get().getImageId(); 66 | registry = ofNullable(registry.orElse(builtInfo.get().getRegistry().orElse(null))); 67 | } 68 | 69 | for (String nameAndTag : config.getTags()) { 70 | attachTag(imageId, nameAndTag); 71 | if (push) { 72 | enqueueForPushing(imageId, Optional.ofNullable(nameAndTag), registry); 73 | } 74 | } 75 | } 76 | 77 | } -------------------------------------------------------------------------------- /src/main/java/net/wouterdanes/docker/maven/PushImageMojo.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Wouter Danes, Lachlan Coote 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | */ 17 | 18 | package net.wouterdanes.docker.maven; 19 | 20 | 21 | import net.wouterdanes.docker.provider.model.PushableImage; 22 | import net.wouterdanes.docker.remoteapi.exception.DockerException; 23 | import org.apache.maven.plugin.MojoExecutionException; 24 | import org.apache.maven.plugin.MojoFailureException; 25 | import org.apache.maven.plugins.annotations.InstantiationStrategy; 26 | import org.apache.maven.plugins.annotations.LifecyclePhase; 27 | import org.apache.maven.plugins.annotations.Mojo; 28 | 29 | import java.util.Iterator; 30 | 31 | /** 32 | * This class is responsible for pushing docking images in the deploy phase of the maven build. The goal 33 | * is called "push-images" 34 | */ 35 | @Mojo(defaultPhase = LifecyclePhase.DEPLOY, name = "push-images", threadSafe = true, 36 | instantiationStrategy = InstantiationStrategy.PER_LOOKUP) 37 | public class PushImageMojo extends AbstractDockerMojo { 38 | 39 | @Override 40 | public void doExecute() throws MojoExecutionException, MojoFailureException { 41 | ensureThatAllPushableImagesHaveAName(); 42 | for (PushableImage image : getImagesToPush()) { 43 | getLog().info(String.format("Pushing image '%s' with tag '%s'", 44 | image.getImageId(), image.getNameAndTag().orElse(""))); 45 | try { 46 | getDockerProvider().pushImage(image.getNameAndTag().get()); 47 | } catch (DockerException e) { 48 | String message = String.format("Cannot push image '%s' with tag '%s'", 49 | image.getImageId(), image.getNameAndTag().orElse("")); 50 | handleDockerException(message, e); 51 | } 52 | } 53 | } 54 | 55 | private void ensureThatAllPushableImagesHaveAName() throws MojoFailureException { 56 | Iterator imagesWithoutNameAndTag = getImagesToPush().parallelStream() 57 | .filter(image -> !image.getNameAndTag().isPresent()).iterator(); 58 | 59 | if (imagesWithoutNameAndTag.hasNext()) { 60 | imagesWithoutNameAndTag.forEachRemaining(image -> { 61 | String message = String.format("Image '%s' needs to be pushed but doesn't have a name.", image.getImageId()); 62 | getLog().error(message); 63 | }); 64 | throw new MojoFailureException("There are images that need to be pushed without a name."); 65 | } 66 | } 67 | 68 | } -------------------------------------------------------------------------------- /src/it/container-logs-it/src/main/java/net/wouterdanes/Application.java: -------------------------------------------------------------------------------- 1 | package net.wouterdanes; 2 | 3 | import com.mongodb.*; 4 | import com.mongodb.util.JSON; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import ratpack.guice.Guice; 8 | import ratpack.handling.ChainAction; 9 | import ratpack.handling.Handler; 10 | import ratpack.launch.HandlerFactory; 11 | import ratpack.launch.LaunchConfig; 12 | 13 | import java.util.Date; 14 | import java.util.stream.StreamSupport; 15 | 16 | /** 17 | * Main entrypoint for this application 18 | */ 19 | public class Application implements HandlerFactory { 20 | 21 | private static final Logger log = LoggerFactory.getLogger(Application.class); 22 | private static final String ENV_APP_MESSAGE = "APP_MESSAGE"; 23 | 24 | @Override 25 | public Handler create(final LaunchConfig launchConfig) throws Exception { 26 | return Guice.handler(launchConfig, bindingsSpec -> { 27 | String mongoHost = System.getProperty("mongo.host", "localhost"); 28 | log.info("Mongo host = {}", mongoHost); 29 | DBCollection collection = new MongoClient(mongoHost) 30 | .getDB("discuss") 31 | .getCollection("posts"); 32 | bindingsSpec.bind(DBCollection.class, collection); 33 | }, new Routes()); 34 | } 35 | 36 | private class Routes extends ChainAction { 37 | 38 | @Override 39 | protected void execute() throws Exception { 40 | 41 | get("message", ctx -> ctx.render(System.getenv(ENV_APP_MESSAGE))); 42 | 43 | prefix("posts", (Handler) ctx -> ctx.byMethod(posts -> { 44 | posts.post(context -> { 45 | 46 | String body = context.getRequest().getBody().getText(); 47 | 48 | DBCollection collection = context.get(DBCollection.class); 49 | 50 | DBObject object = new BasicDBObjectBuilder() 51 | .add("dateTime", new Date()) 52 | .add("message", JSON.parse(body)) 53 | .get(); 54 | 55 | collection.insert(object); 56 | 57 | context.getResponse().status(201); 58 | context.getResponse().send(); 59 | 60 | }); 61 | 62 | posts.get(context -> { 63 | 64 | DBObject fieldSpec = new BasicDBObjectBuilder() 65 | .add("_id", 0) 66 | .add("dateTime", 1) 67 | .add("message", 1) 68 | .get(); 69 | 70 | DBCollection collection = context.get(DBCollection.class); 71 | DBCursor cursor = collection.find(new BasicDBObject(), fieldSpec).sort(new BasicDBObject("dateTime", -1)); 72 | 73 | if (!cursor.hasNext()) { 74 | context.clientError(404); 75 | return; 76 | } 77 | 78 | String result = "[" + 79 | StreamSupport.stream(cursor.spliterator(), true) 80 | .map(JSON::serialize) 81 | .reduce((s, s2) -> s + "," + s2) 82 | .get() 83 | + "]"; 84 | 85 | context.getResponse().send("application/json", result); 86 | 87 | }); 88 | } 89 | )); 90 | 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/it/sample-api-mongo-it/src/main/java/net/wouterdanes/Application.java: -------------------------------------------------------------------------------- 1 | package net.wouterdanes; 2 | 3 | import com.mongodb.*; 4 | import com.mongodb.util.JSON; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import ratpack.guice.Guice; 8 | import ratpack.handling.ChainAction; 9 | import ratpack.handling.Handler; 10 | import ratpack.launch.HandlerFactory; 11 | import ratpack.launch.LaunchConfig; 12 | 13 | import java.util.Date; 14 | import java.util.stream.StreamSupport; 15 | 16 | /** 17 | * Main entrypoint for this application 18 | */ 19 | public class Application implements HandlerFactory { 20 | 21 | private static final Logger log = LoggerFactory.getLogger(Application.class); 22 | private static final String ENV_APP_MESSAGE = "APP_MESSAGE"; 23 | 24 | @Override 25 | public Handler create(final LaunchConfig launchConfig) throws Exception { 26 | return Guice.handler(launchConfig, bindingsSpec -> { 27 | String mongoHost = System.getProperty("mongo.host", "localhost"); 28 | log.info("Mongo host = {}", mongoHost); 29 | DBCollection collection = new MongoClient(mongoHost) 30 | .getDB("discuss") 31 | .getCollection("posts"); 32 | bindingsSpec.bind(DBCollection.class, collection); 33 | }, new Routes()); 34 | } 35 | 36 | private class Routes extends ChainAction { 37 | 38 | @Override 39 | protected void execute() throws Exception { 40 | 41 | get("message", ctx -> ctx.render(System.getenv(ENV_APP_MESSAGE))); 42 | 43 | prefix("posts", (Handler) ctx -> ctx.byMethod(posts -> { 44 | posts.post(context -> { 45 | 46 | String body = context.getRequest().getBody().getText(); 47 | 48 | DBCollection collection = context.get(DBCollection.class); 49 | 50 | DBObject object = new BasicDBObjectBuilder() 51 | .add("dateTime", new Date()) 52 | .add("message", JSON.parse(body)) 53 | .get(); 54 | 55 | collection.insert(object); 56 | 57 | context.getResponse().status(201); 58 | context.getResponse().send(); 59 | 60 | }); 61 | 62 | posts.get(context -> { 63 | 64 | DBObject fieldSpec = new BasicDBObjectBuilder() 65 | .add("_id", 0) 66 | .add("dateTime", 1) 67 | .add("message", 1) 68 | .get(); 69 | 70 | DBCollection collection = context.get(DBCollection.class); 71 | DBCursor cursor = collection.find(new BasicDBObject(), fieldSpec).sort(new BasicDBObject("dateTime", -1)); 72 | 73 | if (!cursor.hasNext()) { 74 | context.clientError(404); 75 | return; 76 | } 77 | 78 | String result = "[" + 79 | StreamSupport.stream(cursor.spliterator(), true) 80 | .map(JSON::serialize) 81 | .reduce((s, s2) -> s + "," + s2) 82 | .get() 83 | + "]"; 84 | 85 | context.getResponse().send("application/json", result); 86 | 87 | }); 88 | } 89 | )); 90 | 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/net/wouterdanes/docker/provider/LocalDockerProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Wouter Danes 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | */ 17 | 18 | package net.wouterdanes.docker.provider; 19 | 20 | import java.util.ArrayList; 21 | import java.util.Collections; 22 | import java.util.List; 23 | import java.util.Set; 24 | import java.util.regex.Matcher; 25 | import java.util.regex.Pattern; 26 | 27 | import net.wouterdanes.docker.provider.model.ContainerStartConfiguration; 28 | import net.wouterdanes.docker.provider.model.ExposedPort; 29 | import net.wouterdanes.docker.remoteapi.model.ContainerInspectionResult; 30 | import net.wouterdanes.docker.remoteapi.model.ContainerStartRequest; 31 | 32 | /** 33 | * A Docker provider for the remote http api on a locally running docker. 34 | * This provider won't publish ports to the host, but will give host + port combinations directly to the container. 35 | * You should only use this provider if you can directly access the created containers via their IP address on the 36 | * docker host. 37 | */ 38 | public class LocalDockerProvider extends RemoteApiBasedDockerProvider { 39 | 40 | private static final Pattern TCP_PORT_MATCHER = Pattern.compile("(?[0-9]+)/tcp"); 41 | 42 | public LocalDockerProvider() { 43 | super(); 44 | } 45 | 46 | @Override 47 | public ContainerInspectionResult startContainer(final ContainerStartConfiguration configuration) { 48 | ContainerStartRequest startRequest = new ContainerStartRequest() 49 | .withLinks(configuration.getLinks()); 50 | 51 | return super.startContainer(configuration, startRequest); 52 | } 53 | 54 | @Override 55 | public List getExposedPorts(final String containerId) { 56 | ContainerInspectionResult containerInspectionResult = getContainersService().inspectContainer(containerId); 57 | if (containerInspectionResult.getNetworkSettings().getPorts().isEmpty()) { 58 | return Collections.emptyList(); 59 | } 60 | Set ports = containerInspectionResult.getConfig().getExposedPorts().keySet(); 61 | String containerIp = containerInspectionResult.getNetworkSettings().getIpAddress(); 62 | List exposedPorts = new ArrayList<>(ports.size()); 63 | for (String port : ports) { 64 | Matcher matcher = TCP_PORT_MATCHER.matcher(port); 65 | if (matcher.matches()) { 66 | int tcpPort = Integer.parseInt(matcher.group("port")); 67 | exposedPorts.add(new ExposedPort(port, tcpPort, containerIp)); 68 | } 69 | } 70 | return exposedPorts; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/broken-it/push-with-explicit-registry-it/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | net.wouterdanes.docker.it 7 | push-with-explicit-registry-it 8 | 1.0-SNAPSHOT 9 | 10 | A simple IT verifying the basic use case. 11 | 12 | 13 | UTF-8 14 | 1.7 15 | 1.7 16 | 17 | 18 | 19 | 20 | 21 | @project.groupId@ 22 | @project.artifactId@ 23 | @project.version@ 24 | 25 | 26 | build 27 | 28 | build-images 29 | 30 | 31 | 32 | 33 | nginx 34 | ${project.basedir}/src/test/resources/Dockerfile 35 | corgis:latest 36 | true 37 | true 38 | ${docker.registry} 39 | 40 | 41 | 42 | 43 | 44 | push 45 | 46 | push-images 47 | 48 | 49 | 50 | 51 | 52 | maven-compiler-plugin 53 | 3.1 54 | 55 | ${maven.compiler.source} 56 | ${maven.compiler.target} 57 | ${project.build.sourceEncoding} 58 | 59 | 60 | 61 | maven-deploy-plugin 62 | 2.8.1 63 | 64 | true 65 | 66 | 67 | 68 | maven-enforcer-plugin 69 | 1.3.1 70 | 71 | 72 | enforce-maven-version 73 | 74 | enforce 75 | 76 | 77 | 78 | 79 | @maven.required.version@ 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /src/main/java/net/wouterdanes/docker/provider/RemoteDockerProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Wouter Danes 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | */ 17 | 18 | package net.wouterdanes.docker.provider; 19 | 20 | import java.util.ArrayList; 21 | import java.util.Collections; 22 | import java.util.List; 23 | import java.util.Map; 24 | 25 | import net.wouterdanes.docker.provider.model.ContainerStartConfiguration; 26 | import net.wouterdanes.docker.provider.model.ExposedPort; 27 | import net.wouterdanes.docker.remoteapi.model.ContainerInspectionResult; 28 | import net.wouterdanes.docker.remoteapi.model.ContainerStartRequest; 29 | 30 | /** 31 | * This class is responsible for providing a docker interface with a remote (not running on localhost) docker host. It 32 | * can be configured by setting an environment variable {@value #DOCKER_HOST_SYSTEM_ENV }, like in the client. Or you 33 | * can specify the host and port on the command line like such: 34 | *
-D{@value #DOCKER_HOST_PROPERTY}=[host] -D{@value #DOCKER_PORT_PROPERTY}=[port]
35 | * 36 | * The provider defaults to {@value #TCP_PROTOCOL}://{@value #DEFAULT_DOCKER_HOST}:{@value #DEFAULT_DOCKER_PORT} 37 | */ 38 | public class RemoteDockerProvider extends RemoteApiBasedDockerProvider { 39 | 40 | public RemoteDockerProvider() { 41 | super(); 42 | } 43 | 44 | @Override 45 | public ContainerInspectionResult startContainer(final ContainerStartConfiguration configuration) { 46 | ContainerStartRequest startRequest = new ContainerStartRequest() 47 | .withAllPortsPublished() 48 | .withLinks(configuration.getLinks()); 49 | 50 | return super.startContainer(configuration, startRequest); 51 | } 52 | 53 | @Override 54 | public List getExposedPorts(final String containerId) { 55 | ContainerInspectionResult containerInspectionResult = getContainersService().inspectContainer(containerId); 56 | if (containerInspectionResult.getNetworkSettings().getPorts().isEmpty()) { 57 | return Collections.emptyList(); 58 | } 59 | Map> ports = 60 | containerInspectionResult.getNetworkSettings().getPorts(); 61 | List exposedPorts = new ArrayList<>(); 62 | for (Map.Entry> port : ports.entrySet()) { 63 | String exposedPort = port.getKey(); 64 | int hostPort = port.getValue().get(0).getHostPort(); 65 | exposedPorts.add(new ExposedPort(exposedPort, hostPort, getHost())); 66 | } 67 | return exposedPorts; 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /src/test/java/net/wouterdanes/docker/provider/AbstractFakeDockerProvider.java: -------------------------------------------------------------------------------- 1 | package net.wouterdanes.docker.provider; 2 | 3 | import net.wouterdanes.docker.provider.model.ContainerCommitConfiguration; 4 | import net.wouterdanes.docker.provider.model.ContainerStartConfiguration; 5 | import net.wouterdanes.docker.provider.model.ExposedPort; 6 | import net.wouterdanes.docker.provider.model.ImageBuildConfiguration; 7 | import net.wouterdanes.docker.remoteapi.model.ContainerInspectionResult; 8 | import net.wouterdanes.docker.remoteapi.model.Credentials; 9 | import org.apache.maven.plugin.logging.Log; 10 | import org.eclipse.aether.RepositorySystem; 11 | import org.eclipse.aether.RepositorySystemSession; 12 | import org.eclipse.aether.repository.RemoteRepository; 13 | 14 | import java.util.List; 15 | 16 | /** 17 | * Utility class to create mock docker providers, extend this and implement the getInstance() method, then create 18 | * a static field in the extending class that can be modified by the test and that hold the actual mock. 19 | */ 20 | public abstract class AbstractFakeDockerProvider implements DockerProvider { 21 | 22 | private final AbstractFakeDockerProvider proxy; 23 | 24 | public AbstractFakeDockerProvider() { 25 | proxy = getInstance(); 26 | } 27 | 28 | protected abstract AbstractFakeDockerProvider getInstance(); 29 | 30 | @Override 31 | public ContainerInspectionResult startContainer(final ContainerStartConfiguration configuration) { 32 | return proxy.startContainer(configuration); 33 | } 34 | 35 | @Override 36 | public void stopContainer(final String containerId) { 37 | proxy.stopContainer(containerId); 38 | } 39 | 40 | @Override 41 | public void deleteContainer(final String containerId) { 42 | proxy.deleteContainer(containerId); 43 | } 44 | 45 | @Override 46 | public List getExposedPorts(final String containerId) { 47 | return proxy.getExposedPorts(containerId); 48 | } 49 | 50 | @Override 51 | public String buildImage(final ImageBuildConfiguration image) { 52 | return proxy.buildImage(image); 53 | } 54 | 55 | @Override 56 | public String commitContainer(ContainerCommitConfiguration configuration) { 57 | return proxy.commitContainer(configuration); 58 | } 59 | 60 | @Override 61 | public void removeImage(final String imageId) { 62 | proxy.removeImage(imageId); 63 | } 64 | 65 | @Override 66 | public void pushImage(final String nameAndTag) { 67 | proxy.pushImage(nameAndTag); 68 | } 69 | 70 | @Override 71 | public void tagImage(final String imageId, final String nameAndTag) { 72 | proxy.tagImage(imageId, nameAndTag); 73 | } 74 | 75 | @Override 76 | public void setCredentials(Credentials credentials) { 77 | proxy.setCredentials(credentials); 78 | } 79 | 80 | @Override 81 | public String getLogs(final String containerId) { 82 | return proxy.getLogs(containerId); 83 | } 84 | 85 | @Override 86 | public void setLogger(final Log logger) { 87 | proxy.setLogger(logger); 88 | } 89 | 90 | @Override 91 | public void setRepositorySystem(RepositorySystem repositorySystem) { 92 | // NOOP 93 | } 94 | 95 | @Override 96 | public void setRepositorySystemSession(RepositorySystemSession repositorySystemSession) { 97 | // NOOP 98 | } 99 | 100 | @Override 101 | public void setRemoteRepositories(List remoteRepositories) { 102 | // NOOP 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/main/java/net/wouterdanes/docker/maven/CommitContainerMojo.java: -------------------------------------------------------------------------------- 1 | package net.wouterdanes.docker.maven; 2 | 3 | import net.wouterdanes.docker.provider.model.ContainerCommitConfiguration; 4 | import net.wouterdanes.docker.provider.model.ImageBuildConfiguration; 5 | import net.wouterdanes.docker.remoteapi.exception.DockerException; 6 | import org.apache.maven.plugin.MojoExecutionException; 7 | import org.apache.maven.plugin.MojoFailureException; 8 | import org.apache.maven.plugins.annotations.InstantiationStrategy; 9 | import org.apache.maven.plugins.annotations.LifecyclePhase; 10 | import org.apache.maven.plugins.annotations.Mojo; 11 | import org.apache.maven.plugins.annotations.Parameter; 12 | 13 | import java.util.List; 14 | import java.util.Optional; 15 | 16 | @Mojo(name = "commit-containers", defaultPhase = LifecyclePhase.POST_INTEGRATION_TEST, threadSafe = true, 17 | instantiationStrategy = InstantiationStrategy.PER_LOOKUP) 18 | public class CommitContainerMojo extends AbstractPreVerifyDockerMojo { 19 | @Parameter(required = true) 20 | private List containers; 21 | 22 | public void setConfiguration(final List containers) { 23 | this.containers = containers; 24 | } 25 | 26 | @Override 27 | protected void doExecute() throws MojoExecutionException, MojoFailureException { 28 | if (containers == null || containers.isEmpty()) { 29 | getLog().warn("No containers specified."); 30 | return; 31 | } 32 | for (ContainerCommitConfiguration container : containers) { 33 | commitContainer(container); 34 | } 35 | } 36 | 37 | protected void commitContainer(ContainerCommitConfiguration container) throws MojoFailureException { 38 | getLog().info(String.format("Creating image for configuration '%s'", container)); 39 | String containerId = container.getId(); 40 | Optional containerInfo = getInfoForContainerStartId(containerId); 41 | if (containerInfo.isPresent()) { 42 | try { 43 | //replace container name by its actual id. 44 | String startedContainerId = containerInfo.get().getContainerInfo().getId(); 45 | container.setId(startedContainerId); 46 | 47 | String imageId = getDockerProvider().commitContainer(container); 48 | getLog().info(String.format("Image '%s' created from container '%s'", imageId, container.getId())); 49 | 50 | //Register the resulting image so it can be pushed 51 | ImageBuildConfiguration imageBuildConfiguration = new ImageBuildConfiguration(); 52 | imageBuildConfiguration.setId(containerId); 53 | imageBuildConfiguration.setNameAndTag(container.getRepo() + ":" + container.getTag()); 54 | imageBuildConfiguration.setPush(container.isPush()); 55 | registerBuiltImage(imageId, imageBuildConfiguration); 56 | 57 | } catch (DockerException e) { 58 | String errorMessage = String.format("Image '%s:%s' could not be created from container '%s'", container.getRepo(), container.getTag(), container.getId()); 59 | handleDockerException(errorMessage, e); 60 | } 61 | } else { 62 | String message = String.format("No container found for id '%s'", containerId); 63 | registerPluginError(new DockerPluginError("commit-containers", message)); 64 | getLog().warn(message); 65 | } 66 | } 67 | 68 | @Override 69 | protected String getMojoGoalName() { 70 | return "commit-containers"; 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/net/wouterdanes/docker/remoteapi/model/ContainerStartRequest.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Wouter Danes 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | */ 17 | 18 | package net.wouterdanes.docker.remoteapi.model; 19 | 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | import java.util.Map; 23 | 24 | import org.codehaus.jackson.annotate.JsonProperty; 25 | 26 | /** 27 | * See 28 | * http://docs.docker.io/reference/api/docker_remote_api_v1.10/#start-a-container 29 | */ 30 | @SuppressWarnings("unused") 31 | public class ContainerStartRequest { 32 | 33 | @JsonProperty("Binds") 34 | private List binds; 35 | @JsonProperty("LxcConf") 36 | private Map lxcConf; 37 | @JsonProperty("PortBindings") 38 | private Map>> portBindings; 39 | @JsonProperty("PublishAllPorts") 40 | private boolean publishAllPorts = false; 41 | @JsonProperty("Privileged") 42 | private boolean privileged = false; 43 | @JsonProperty("Links") 44 | private List links = new ArrayList<>(); 45 | 46 | public ContainerStartRequest withBinds(List binds) { 47 | this.binds = binds; 48 | return this; 49 | } 50 | 51 | public ContainerStartRequest withLxcConf(Map lxcConf) { 52 | this.lxcConf = lxcConf; 53 | return this; 54 | } 55 | 56 | public ContainerStartRequest withPortBindings(Map>> portBindings) { 57 | this.portBindings = portBindings; 58 | return this; 59 | } 60 | 61 | public ContainerStartRequest withAllPortsPublished() { 62 | this.publishAllPorts = true; 63 | return this; 64 | } 65 | 66 | public ContainerStartRequest withLink(String containerName, String alias) { 67 | addLink(containerName, alias); 68 | return this; 69 | } 70 | 71 | public ContainerStartRequest withLinks(List links) { 72 | for (ContainerLink link : links) { 73 | addLink(link.getContainerId(), link.getContainerAlias()); 74 | } 75 | return this; 76 | } 77 | 78 | public ContainerStartRequest makePrivileged() { 79 | this.privileged = true; 80 | return this; 81 | } 82 | 83 | public List getBinds() { 84 | return binds; 85 | } 86 | 87 | public Map getLxcConf() { 88 | return lxcConf; 89 | } 90 | 91 | public Map>> getPortBindings() { 92 | return portBindings; 93 | } 94 | 95 | public boolean isPublishAllPorts() { 96 | return publishAllPorts; 97 | } 98 | 99 | public boolean isPrivileged() { 100 | return privileged; 101 | } 102 | 103 | public List getLinks() { 104 | return links; 105 | } 106 | 107 | private void addLink(final String containerName, final String alias) { 108 | links.add(String.format("%s:%s", containerName, alias)); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/main/java/net/wouterdanes/docker/provider/model/ImageBuildConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Wouter Danes 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | */ 17 | 18 | package net.wouterdanes.docker.provider.model; 19 | 20 | import com.google.gson.Gson; 21 | import org.apache.maven.plugins.annotations.Parameter; 22 | 23 | import java.io.File; 24 | import java.util.HashMap; 25 | import java.util.List; 26 | import java.util.Map; 27 | 28 | /** 29 | * This class is responsible for holding the configuration of a single docker image to be built by the 30 | * {@link net.wouterdanes.docker.maven.BuildImageMojo} 31 | */ 32 | public class ImageBuildConfiguration { 33 | 34 | @Parameter(required = true) 35 | private File dockerFile; 36 | 37 | @Parameter(required = true) 38 | private String id; 39 | 40 | @Parameter 41 | private String nameAndTag; 42 | 43 | @Parameter(defaultValue = "false") 44 | private boolean keep; 45 | 46 | @Parameter(defaultValue = "false") 47 | private boolean push; 48 | 49 | @Parameter 50 | private String registry; 51 | 52 | @Parameter 53 | private List artifacts; 54 | 55 | @Parameter 56 | private List mavenArtifacts; 57 | 58 | @Parameter 59 | protected Map buildArguments = new HashMap(); 60 | 61 | public String getId() { 62 | return id; 63 | } 64 | 65 | public void setId(final String id) { 66 | this.id = id; 67 | } 68 | 69 | public String getNameAndTag() { 70 | return nameAndTag; 71 | } 72 | 73 | public void setNameAndTag(final String nameAndTag) { 74 | this.nameAndTag = nameAndTag; 75 | } 76 | 77 | public boolean isKeep() { 78 | return keep; 79 | } 80 | 81 | public void setKeep(final boolean keep) { 82 | this.keep = keep; 83 | } 84 | 85 | public boolean isPush() { 86 | return push; 87 | } 88 | 89 | public void setPush(boolean push) { 90 | this.push = push; 91 | } 92 | 93 | public String getRegistry() { 94 | return registry; 95 | } 96 | 97 | public void setRegistry(String registry) { 98 | this.registry = registry; 99 | } 100 | 101 | public List getArtifacts() { 102 | return artifacts; 103 | } 104 | 105 | public void setArtifacts(List artifacts) { 106 | this.artifacts = artifacts; 107 | } 108 | 109 | public List getMavenArtifacts() { 110 | return mavenArtifacts; 111 | } 112 | 113 | public void setMavenArtifacts(List mavenArtifacts) { 114 | this.mavenArtifacts = mavenArtifacts; 115 | } 116 | 117 | public String getBuildArguments() { 118 | Gson gson = new Gson(); 119 | return gson.toJson(buildArguments); 120 | } 121 | 122 | public void setBuildArguments(Map buildArguments) { 123 | this.buildArguments = buildArguments; 124 | } 125 | 126 | public File getDockerFile() { 127 | return dockerFile; 128 | } 129 | 130 | public void setDockerFile(File dockerFile) { 131 | this.dockerFile = dockerFile; 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/broken-it/tag-and-push-it/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | net.wouterdanes.docker.it 7 | tag-and-push-it 8 | 1.0-SNAPSHOT 9 | 10 | A simple IT verifying the basic use case. 11 | 12 | 13 | UTF-8 14 | 1.7 15 | 1.7 16 | 17 | 18 | 19 | 20 | 21 | @project.groupId@ 22 | @project.artifactId@ 23 | @project.version@ 24 | 25 | 26 | build 27 | 28 | build-images 29 | 30 | 31 | 32 | 33 | nginx 34 | ${project.basedir}/src/test/resources/Dockerfile 35 | dross:snapshot 36 | true 37 | true 38 | ${docker.registry} 39 | 40 | 41 | 42 | 43 | 44 | tag 45 | 46 | tag-images 47 | 48 | 49 | 50 | 51 | nginx 52 | 53 | dross:release 54 | dross:4.1 55 | 56 | true 57 | 58 | 59 | 60 | 61 | 62 | push 63 | 64 | push-images 65 | 66 | 67 | 68 | 69 | 70 | maven-compiler-plugin 71 | 3.1 72 | 73 | ${maven.compiler.source} 74 | ${maven.compiler.target} 75 | ${project.build.sourceEncoding} 76 | 77 | 78 | 79 | maven-deploy-plugin 80 | 2.8.1 81 | 82 | true 83 | 84 | 85 | 86 | maven-enforcer-plugin 87 | 1.3.1 88 | 89 | 90 | enforce-maven-version 91 | 92 | enforce 93 | 94 | 95 | 96 | 97 | @maven.required.version@ 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /src/main/java/net/wouterdanes/docker/remoteapi/model/ContainerCreateRequest.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Wouter Danes 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | */ 17 | 18 | package net.wouterdanes.docker.remoteapi.model; 19 | 20 | import java.util.ArrayList; 21 | import java.util.Arrays; 22 | import java.util.Collections; 23 | import java.util.List; 24 | import java.util.Map; 25 | 26 | import org.codehaus.jackson.annotate.JsonProperty; 27 | 28 | /** 29 | * See 30 | * http://docs.docker.io/reference/api/docker_remote_api_v1.10/#create-a-container 31 | */ 32 | @SuppressWarnings("unused") 33 | public class ContainerCreateRequest { 34 | 35 | @JsonProperty("Hostname") 36 | private String hostname; 37 | @JsonProperty("User") 38 | private String user; 39 | @JsonProperty("Memory") 40 | private Long memory; 41 | @JsonProperty("Cmd") 42 | private List cmd; 43 | @JsonProperty("Image") 44 | private String image; 45 | @JsonProperty("Env") 46 | private List env; 47 | @JsonProperty("MacAddress") 48 | private String macAddress; 49 | 50 | public String getHostname() { 51 | return hostname; 52 | } 53 | 54 | public String getUser() { 55 | return user; 56 | } 57 | 58 | public Long getMemory() { 59 | return memory; 60 | } 61 | 62 | public List getCmd() { 63 | return cmd; 64 | } 65 | 66 | public String getImage() { 67 | return image; 68 | } 69 | 70 | public List getEnv() { 71 | return env != null ? Collections.unmodifiableList(env) : Collections.emptyList(); 72 | } 73 | 74 | public String getMacAddress() { 75 | return macAddress; 76 | } 77 | 78 | public ContainerCreateRequest withEnv(Map env) { 79 | if (env != null && env.size() > 0) { 80 | this.env = new ArrayList<>(); 81 | for (Map.Entry entry : env.entrySet()) { 82 | this.env.add(String.format("%s=%s", entry.getKey(), entry.getValue())); 83 | } 84 | } 85 | return this; 86 | } 87 | 88 | public ContainerCreateRequest withHostname(String hostname) { 89 | this.hostname = hostname; 90 | return this; 91 | } 92 | 93 | public ContainerCreateRequest withUser(String user) { 94 | this.user = user; 95 | return this; 96 | } 97 | 98 | public ContainerCreateRequest withMemory(long memory) { 99 | this.memory = memory; 100 | return this; 101 | } 102 | 103 | public ContainerCreateRequest withCommand(String command) { 104 | this.cmd = Arrays.asList(command); 105 | return this; 106 | } 107 | 108 | public ContainerCreateRequest withCommands(List commands) { 109 | this.cmd = new ArrayList<>(commands); 110 | return this; 111 | } 112 | 113 | public ContainerCreateRequest withMacAddress(String macAddress) { 114 | this.macAddress = macAddress; 115 | return this; 116 | } 117 | 118 | public ContainerCreateRequest fromImage(String image) { 119 | this.image = image; 120 | return this; 121 | } 122 | 123 | } 124 | -------------------------------------------------------------------------------- /src/test/java/net/wouterdanes/docker/provider/DockerExceptionThrowingDockerProvider.java: -------------------------------------------------------------------------------- 1 | package net.wouterdanes.docker.provider; 2 | 3 | import java.util.List; 4 | 5 | import org.apache.maven.plugin.logging.Log; 6 | 7 | import net.wouterdanes.docker.provider.model.ContainerCommitConfiguration; 8 | import net.wouterdanes.docker.provider.model.ContainerStartConfiguration; 9 | import net.wouterdanes.docker.provider.model.ExposedPort; 10 | import net.wouterdanes.docker.provider.model.ImageBuildConfiguration; 11 | import net.wouterdanes.docker.remoteapi.exception.DockerException; 12 | import net.wouterdanes.docker.remoteapi.model.ContainerInspectionResult; 13 | import net.wouterdanes.docker.remoteapi.model.Credentials; 14 | import org.eclipse.aether.RepositorySystem; 15 | import org.eclipse.aether.RepositorySystemSession; 16 | import org.eclipse.aether.repository.RemoteRepository; 17 | 18 | /** 19 | * A Mock {@link net.wouterdanes.docker.provider.DockerProvider} that only throws 20 | * {@link net.wouterdanes.docker.remoteapi.exception.DockerException}s 21 | * 22 | * If you want to use this provider in your tests, simply write: 23 | * DockerExceptionThrowingDockerProvider.class.newInstance(); 24 | */ 25 | public class DockerExceptionThrowingDockerProvider implements DockerProvider { 26 | 27 | public static final String PROVIDER_KEY = DockerExceptionThrowingDockerProvider.class.getName(); 28 | 29 | static { 30 | DockerProviderSupplier.registerProvider(PROVIDER_KEY, DockerExceptionThrowingDockerProvider.class); 31 | } 32 | 33 | @Override 34 | public void setCredentials(final Credentials credentials) { 35 | // NOOP 36 | } 37 | 38 | @Override 39 | public ContainerInspectionResult startContainer(final ContainerStartConfiguration configuration) { 40 | throwBadException(); 41 | return null; 42 | } 43 | 44 | @Override 45 | public void stopContainer(final String containerId) { 46 | throwBadException(); 47 | } 48 | 49 | @Override 50 | public void deleteContainer(final String containerId) { 51 | throwBadException(); 52 | } 53 | 54 | @Override 55 | public List getExposedPorts(final String containerId) { 56 | throwBadException(); 57 | return null; 58 | } 59 | 60 | @Override 61 | public String buildImage(final ImageBuildConfiguration image) { 62 | throwBadException(); 63 | return null; 64 | } 65 | 66 | @Override 67 | public String commitContainer(ContainerCommitConfiguration configuration) { 68 | throwBadException(); 69 | return null; 70 | } 71 | 72 | @Override 73 | public void removeImage(final String imageId) { 74 | throwBadException(); 75 | } 76 | 77 | @Override 78 | public void pushImage(final String nameAndTag) { 79 | throwBadException(); 80 | } 81 | 82 | @Override 83 | public void tagImage(final String imageId, final String nameAndTag) { 84 | throwBadException(); 85 | } 86 | 87 | @Override 88 | public String getLogs(final String containerId) { 89 | throwBadException(); 90 | return null; 91 | } 92 | 93 | @Override 94 | public void setLogger(final Log logger) { 95 | // NOOP 96 | } 97 | 98 | @Override 99 | public void setRepositorySystem(RepositorySystem repositorySystem) { 100 | // NOOP 101 | } 102 | 103 | @Override 104 | public void setRepositorySystemSession(RepositorySystemSession repositorySystemSession) { 105 | // NOOP 106 | } 107 | 108 | @Override 109 | public void setRemoteRepositories(List remoteRepositories) { 110 | // NOOP 111 | } 112 | 113 | private static void throwBadException() { 114 | throw new DockerException("Bad stuff"); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/main/java/net/wouterdanes/docker/maven/BuildImageMojo.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Wouter Danes 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | */ 17 | 18 | package net.wouterdanes.docker.maven; 19 | 20 | import net.wouterdanes.docker.provider.model.ImageBuildConfiguration; 21 | import net.wouterdanes.docker.remoteapi.exception.DockerException; 22 | import org.apache.maven.plugin.MojoExecutionException; 23 | import org.apache.maven.plugin.MojoFailureException; 24 | import org.apache.maven.plugins.annotations.InstantiationStrategy; 25 | import org.apache.maven.plugins.annotations.LifecyclePhase; 26 | import org.apache.maven.plugins.annotations.Mojo; 27 | import org.apache.maven.plugins.annotations.Parameter; 28 | 29 | import java.util.HashSet; 30 | import java.util.List; 31 | import java.util.Set; 32 | 33 | /** 34 | * This class is responsible for building docker images specified in the POM file. It runs by default during the 35 | * package phase of a maven project. 36 | */ 37 | @Mojo(name = "build-images", defaultPhase = LifecyclePhase.PACKAGE, threadSafe = true, 38 | instantiationStrategy = InstantiationStrategy.PER_LOOKUP) 39 | public class BuildImageMojo extends AbstractPreVerifyDockerMojo { 40 | 41 | @Parameter(required = true) 42 | private List images; 43 | 44 | public void setImages(final List images) { 45 | this.images = images; 46 | } 47 | 48 | @Override 49 | protected void doExecute() throws MojoExecutionException, MojoFailureException { 50 | if (images == null || images.isEmpty()) { 51 | getLog().warn("No images to build specified."); 52 | return; 53 | } 54 | 55 | validateAllImages(); 56 | 57 | for (ImageBuildConfiguration image : images) { 58 | try { 59 | logImageConfig(image); 60 | String imageId = getDockerProvider().buildImage(image); 61 | getLog().info(String.format("Image '%s' has Id '%s'", image.getId(), imageId)); 62 | registerBuiltImage(imageId, image); 63 | } catch (DockerException e) { 64 | String errorMessage = String.format("Cannot build image '%s'", image.getId()); 65 | handleDockerException(errorMessage, e); 66 | } 67 | } 68 | } 69 | 70 | private void logImageConfig(final ImageBuildConfiguration image) { 71 | StringBuilder builder = new StringBuilder(String.format("Building image '%s'", image.getId())); 72 | if (image.getNameAndTag() != null) { 73 | builder.append(String.format(", with name and tag '%s'", image.getNameAndTag())); 74 | } 75 | builder.append(".."); 76 | getLog().info(builder.toString()); 77 | } 78 | 79 | private void validateAllImages() throws MojoExecutionException { 80 | Set ids = new HashSet<>(images.size()); 81 | for (ImageBuildConfiguration image : images) { 82 | if (ids.contains(image.getId())) { 83 | throw new MojoExecutionException(String.format("Image ID '%s' used twice, Image IDs must be unique!", 84 | image.getId())); 85 | } 86 | ids.add(image.getId()); 87 | } 88 | } 89 | 90 | @Override 91 | protected String getMojoGoalName() { 92 | return "build-images"; 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /src/test/java/net/wouterdanes/docker/remoteapi/CredentialsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Wouter Danes 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | */ 17 | 18 | package net.wouterdanes.docker.remoteapi; 19 | 20 | import net.wouterdanes.docker.remoteapi.model.Credentials; 21 | import org.codehaus.jackson.JsonNode; 22 | import org.codehaus.jackson.map.ObjectMapper; 23 | import org.junit.Before; 24 | import org.junit.Test; 25 | 26 | import java.io.IOException; 27 | import java.util.Base64; 28 | 29 | import static org.junit.Assert.assertEquals; 30 | 31 | public class CredentialsTest { 32 | 33 | private static final String USERNAME = "jim"; 34 | private static final String PASSWORD = "123456"; 35 | private static final String EMAIL = "jim_rulz87@hotmail.com"; 36 | private static final String SERVERNAME = "http://myspace.com/jim.the.impaler"; 37 | 38 | private BaseService miscService; 39 | 40 | @Before 41 | public void setUp() { 42 | miscService = new BaseService("don't care", "doesn't matter") { 43 | }; 44 | } 45 | 46 | @Test 47 | public void testAuthHeaderWithServerName() { 48 | Credentials credsWithServer = new Credentials(USERNAME, PASSWORD, EMAIL, SERVERNAME); 49 | miscService.setCredentials(credsWithServer); 50 | 51 | String encodedResult = miscService.getRegistryAuthHeaderValue(); 52 | DecodedAuthHeader decodedResult = decodeAuthHeader(encodedResult); 53 | 54 | assertEquals(USERNAME, decodedResult.getUserName()); 55 | assertEquals(PASSWORD, decodedResult.getPassword()); 56 | assertEquals(EMAIL, decodedResult.getEmail()); 57 | assertEquals(SERVERNAME, decodedResult.getServerAddr()); 58 | } 59 | 60 | @Test 61 | public void testAuthHeaderWithoutServerName() { 62 | Credentials credsWithoutServer = new Credentials(USERNAME, PASSWORD, EMAIL, null); 63 | miscService.setCredentials(credsWithoutServer); 64 | 65 | String encodedResult = miscService.getRegistryAuthHeaderValue(); 66 | DecodedAuthHeader decodedResult = decodeAuthHeader(encodedResult); 67 | 68 | assertEquals(USERNAME, decodedResult.getUserName()); 69 | assertEquals(PASSWORD, decodedResult.getPassword()); 70 | assertEquals(EMAIL, decodedResult.getEmail()); 71 | assertEquals(Credentials.DEFAULT_SERVER_NAME, decodedResult.getServerAddr()); 72 | } 73 | 74 | @Test 75 | public void testAuthHeaderWithoutCredentials() { 76 | String encodedResult = miscService.getRegistryAuthHeaderValue(); 77 | assertEquals("null", encodedResult); 78 | } 79 | 80 | protected DecodedAuthHeader decodeAuthHeader(String encodedValue) { 81 | try { 82 | byte[] jsonBytes = Base64.getDecoder().decode(encodedValue); 83 | JsonNode node = new ObjectMapper().readTree(jsonBytes); 84 | return new DecodedAuthHeader(node); 85 | } catch (IOException e) { 86 | throw new AssertionError(e); 87 | } 88 | } 89 | 90 | private static class DecodedAuthHeader { 91 | 92 | private final JsonNode node; 93 | 94 | public DecodedAuthHeader(JsonNode node) { 95 | this.node = node; 96 | } 97 | 98 | public String getUserName() { 99 | return getTextField("username"); 100 | } 101 | 102 | public String getPassword() { 103 | return getTextField("password"); 104 | } 105 | 106 | public String getEmail() { 107 | return getTextField("email"); 108 | } 109 | 110 | public String getServerAddr() { 111 | return getTextField("serveraddress"); 112 | } 113 | 114 | private String getTextField(String fieldName) { 115 | return node.get(fieldName).getTextValue(); 116 | } 117 | 118 | } 119 | 120 | } 121 | -------------------------------------------------------------------------------- /src/main/java/net/wouterdanes/docker/remoteapi/util/HttpsHelper.java: -------------------------------------------------------------------------------- 1 | package net.wouterdanes.docker.remoteapi.util; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.IOException; 5 | import java.nio.charset.Charset; 6 | import java.nio.file.Files; 7 | import java.nio.file.Path; 8 | import java.nio.file.Paths; 9 | import java.security.KeyFactory; 10 | import java.security.KeyPair; 11 | import java.security.KeyStore; 12 | import java.security.KeyStoreException; 13 | import java.security.NoSuchAlgorithmException; 14 | import java.security.PrivateKey; 15 | import java.security.PublicKey; 16 | import java.security.cert.Certificate; 17 | import java.security.cert.CertificateException; 18 | import java.security.spec.InvalidKeySpecException; 19 | import java.security.spec.PKCS8EncodedKeySpec; 20 | import java.security.spec.X509EncodedKeySpec; 21 | import java.util.UUID; 22 | 23 | import org.bouncycastle.cert.X509CertificateHolder; 24 | import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; 25 | import org.bouncycastle.openssl.PEMKeyPair; 26 | import org.bouncycastle.openssl.PEMParser; 27 | 28 | /** 29 | * Helper methods to parse and load Docker certificate files for encrypted https connection to the docker daemon 30 | */ 31 | public final class HttpsHelper { 32 | 33 | public static final String KEYSTORE_PWD = UUID.randomUUID().toString(); 34 | 35 | private HttpsHelper() {} 36 | 37 | public static KeyStore createKeyStore(final String certPath) 38 | throws IOException, NoSuchAlgorithmException, InvalidKeySpecException, CertificateException, 39 | KeyStoreException { 40 | KeyPair keyPair = loadPrivateKey(certPath); 41 | Certificate privCert = loadCertificate(certPath); 42 | 43 | KeyStore keyStore = KeyStore.getInstance("JKS"); 44 | keyStore.load(null); 45 | 46 | keyStore.setKeyEntry("docker", keyPair.getPrivate(), KEYSTORE_PWD.toCharArray(), new Certificate[]{privCert}); 47 | return keyStore; 48 | } 49 | 50 | public static KeyStore createTrustStore(final String certPath) 51 | throws IOException, CertificateException, KeyStoreException, NoSuchAlgorithmException { 52 | Path caPath = Paths.get(certPath, "ca.pem"); 53 | BufferedReader reader = Files.newBufferedReader(caPath, Charset.defaultCharset()); 54 | 55 | PEMParser parser = new PEMParser(reader); 56 | X509CertificateHolder object = (X509CertificateHolder) parser.readObject(); 57 | Certificate caCert = new JcaX509CertificateConverter().setProvider("BC").getCertificate(object); 58 | 59 | KeyStore trustStore = KeyStore.getInstance("JKS"); 60 | trustStore.load(null); 61 | trustStore.setCertificateEntry("ca", caCert); 62 | return trustStore; 63 | } 64 | 65 | private static Certificate loadCertificate(final String certPath) throws IOException, CertificateException { 66 | Path cert = Paths.get(certPath, "cert.pem"); 67 | BufferedReader reader = Files.newBufferedReader(cert, Charset.defaultCharset()); 68 | PEMParser parser = new PEMParser(reader); 69 | 70 | X509CertificateHolder object = (X509CertificateHolder) parser.readObject(); 71 | return new JcaX509CertificateConverter().setProvider("BC").getCertificate(object); 72 | } 73 | 74 | private static KeyPair loadPrivateKey(final String certPath) 75 | throws IOException, NoSuchAlgorithmException, InvalidKeySpecException { 76 | 77 | Path path = Paths.get(certPath, "key.pem"); 78 | BufferedReader reader = Files.newBufferedReader(path, Charset.defaultCharset()); 79 | 80 | PEMParser parser = new PEMParser(reader); 81 | Object object = parser.readObject(); 82 | 83 | PEMKeyPair keyPair = (PEMKeyPair) object; 84 | 85 | byte[] privateEncoded = keyPair.getPrivateKeyInfo().getEncoded(); 86 | byte[] publicEncoded = keyPair.getPublicKeyInfo().getEncoded(); 87 | 88 | KeyFactory factory = KeyFactory.getInstance("RSA"); 89 | 90 | X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(publicEncoded); 91 | PublicKey publicKey = factory.generatePublic(publicKeySpec); 92 | 93 | PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(privateEncoded); 94 | PrivateKey privateKey = factory.generatePrivate(privateKeySpec); 95 | 96 | return new KeyPair(publicKey, privateKey); 97 | 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/net/wouterdanes/docker/provider/model/ContainerStartConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Wouter Danes 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | */ 17 | 18 | package net.wouterdanes.docker.provider.model; 19 | 20 | import java.util.ArrayList; 21 | import java.util.Collections; 22 | import java.util.List; 23 | import java.util.Map; 24 | 25 | import net.wouterdanes.docker.remoteapi.model.ContainerLink; 26 | 27 | /** 28 | * This class is responsible for holding the start configuration of a docker container
See 30 | * http://docs.docker.io/reference/api/docker_remote_api_v1.10/#start-a-container 31 | */ 32 | public class ContainerStartConfiguration { 33 | 34 | public static final int DEFAULT_STARTUP_TIMEOUT = 5 * 60; 35 | 36 | private String image; 37 | private String id; 38 | private List links; 39 | private Map env; 40 | 41 | /** 42 | * Regular expression to look for that indicates the container has started up 43 | */ 44 | private String waitForStartup; 45 | 46 | /** 47 | * The maximum time to wait for this container to start (seconds), default is 30 sec. 48 | */ 49 | private int startupTimeout; 50 | 51 | /** 52 | * Hostname to give to this container 53 | */ 54 | private String hostname; 55 | 56 | /** 57 | * Supply an optional mac address for the container 58 | */ 59 | private String macAddress; 60 | 61 | /** 62 | * Set the image name or id to use and returns the object so you can chain from/with statements. 63 | * 64 | * @param image the image name or id 65 | * @return this object 66 | */ 67 | public ContainerStartConfiguration fromImage(String image) { 68 | this.image = image; 69 | return this; 70 | } 71 | 72 | public ContainerStartConfiguration withId(String id) { 73 | this.id = id; 74 | return this; 75 | } 76 | 77 | public ContainerStartConfiguration withLinks(ContainerLink... links) { 78 | if (this.links == null) { 79 | this.links = new ArrayList<>(links.length); 80 | } 81 | Collections.addAll(this.links, links); 82 | return this; 83 | } 84 | 85 | public ContainerStartConfiguration withLink(ContainerLink link) { 86 | return withLinks(link); 87 | } 88 | 89 | public ContainerStartConfiguration waitForStartup(String pattern) { 90 | this.waitForStartup = pattern; 91 | return this; 92 | } 93 | 94 | public ContainerStartConfiguration withStartupTimeout(int timeout) { 95 | this.startupTimeout = timeout; 96 | return this; 97 | } 98 | 99 | public ContainerStartConfiguration withEnv(Map env) { 100 | this.env = env; 101 | return this; 102 | } 103 | 104 | public ContainerStartConfiguration withHostname(String hostname) { 105 | this.hostname = hostname; 106 | return this; 107 | } 108 | 109 | public ContainerStartConfiguration withMacAddress(String macAddress) { 110 | this.macAddress = macAddress; 111 | return this; 112 | } 113 | 114 | public String getImage() { 115 | return image; 116 | } 117 | 118 | public String getId() { 119 | return id != null ? id : image; 120 | } 121 | 122 | public List getLinks() { 123 | return links != null ? Collections.unmodifiableList(links) : Collections.emptyList(); 124 | } 125 | 126 | public Map getEnv() { 127 | return env != null ? Collections.unmodifiableMap(env) : Collections.emptyMap(); 128 | } 129 | 130 | public String getHostname() { 131 | return hostname; 132 | } 133 | 134 | public String getMacAddress() { 135 | return macAddress; 136 | } 137 | 138 | public String getWaitForStartup() { 139 | return waitForStartup; 140 | } 141 | 142 | public int getStartupTimeout() { 143 | return startupTimeout != 0 ? startupTimeout : DEFAULT_STARTUP_TIMEOUT; 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/main/java/net/wouterdanes/docker/remoteapi/model/ImageDescriptor.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Wouter Danes 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | */ 17 | 18 | package net.wouterdanes.docker.remoteapi.model; 19 | 20 | import org.apache.commons.lang3.Validate; 21 | 22 | import java.util.Optional; 23 | import java.util.regex.Matcher; 24 | import java.util.regex.Pattern; 25 | 26 | /** 27 | * Creates an image descriptor based on a passed image id or qualifier in the form ([registry]/[repo]/[image]:[tag]) 28 | */ 29 | public class ImageDescriptor { 30 | 31 | private static final Pattern IMAGE_QUALIFIER = Pattern.compile("^" 32 | + "((?[\\w\\.\\-]+(:\\d+)?)/)??" // registry 33 | + "((?[\\w]+)/)?" // repository 34 | + "(?[\\w\\.\\-]+)" // image 35 | + "(:(?[\\w\\.\\-]+))?" // tag 36 | + "$"); 37 | 38 | private final String id; 39 | private final Optional registry; 40 | private final Optional repository; 41 | private final String image; 42 | private final Optional tag; 43 | 44 | public ImageDescriptor(String id) { 45 | Validate.notBlank(id, "Id was null or empty"); 46 | 47 | this.id = id; 48 | 49 | Matcher matcher = IMAGE_QUALIFIER.matcher(id); 50 | if (matcher.matches()) { 51 | // because Optional.orNull(x) cannot handle null values of x 52 | this.registry = Optional.ofNullable(matcher.group("registry")); 53 | this.repository = Optional.ofNullable(matcher.group("repository")); 54 | this.image = matcher.group("image"); 55 | this.tag = Optional.ofNullable(matcher.group("tag")); 56 | } else { 57 | this.registry = Optional.empty(); 58 | this.repository = Optional.empty(); 59 | this.image = id; 60 | this.tag = Optional.empty(); 61 | } 62 | } 63 | 64 | public String getId() { 65 | return id; 66 | } 67 | 68 | public Optional getRegistry() { 69 | return registry; 70 | } 71 | 72 | public Optional getRepository() { 73 | return repository; 74 | } 75 | 76 | public String getImage() { 77 | return image; 78 | } 79 | 80 | public Optional getTag() { 81 | return tag; 82 | } 83 | 84 | public String getRegistryRepositoryAndImage() { 85 | StringBuilder buf = new StringBuilder(); 86 | appendRegistry(buf); 87 | appendRepository(buf); 88 | appendImage(buf); 89 | return buf.toString(); 90 | } 91 | 92 | public String getRepositoryAndImage() { 93 | StringBuilder buf = new StringBuilder(); 94 | appendRepository(buf); 95 | appendImage(buf); 96 | return buf.toString(); 97 | } 98 | 99 | public String getRepositoryImageAndTag() { 100 | StringBuilder buf = new StringBuilder(); 101 | appendRepository(buf); 102 | appendImage(buf); 103 | appendTag(buf); 104 | return buf.toString(); 105 | } 106 | 107 | private void appendRegistry(StringBuilder buf) { 108 | if (registry.isPresent()) { 109 | buf.append(registry.get()); 110 | buf.append('/'); 111 | } 112 | } 113 | 114 | private void appendRepository(StringBuilder buf) { 115 | if (repository.isPresent()) { 116 | buf.append(repository.get()); 117 | buf.append('/'); 118 | } 119 | } 120 | 121 | private void appendImage(StringBuilder buf) { 122 | buf.append(image); 123 | } 124 | 125 | private void appendTag(StringBuilder buf) { 126 | if (tag.isPresent()) { 127 | buf.append(':'); 128 | buf.append(tag.get()); 129 | } 130 | } 131 | 132 | @Override 133 | public String toString() { 134 | return "ImageDescriptor[id=" + id 135 | + ", registry=" + registry.orElse("") 136 | + ", repository=" + repository.orElse("") 137 | + ", image=" + image 138 | + ", tag=" + tag.orElse("") 139 | + "]"; 140 | } 141 | 142 | 143 | } 144 | -------------------------------------------------------------------------------- /src/it/buildargs-it/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | net.wouterdanes.docker.it 7 | buildargs-it 8 | 1.0-SNAPSHOT 9 | 10 | A simple IT verifying the buildargs implementation. 11 | 12 | 13 | UTF-8 14 | 1.7 15 | 1.7 16 | 17 | 18 | 19 | 20 | junit 21 | junit 22 | 4.8.2 23 | test 24 | 25 | 26 | org.apache.httpcomponents 27 | httpclient 28 | 4.3.3 29 | test 30 | 31 | 32 | 33 | 34 | 35 | 36 | org.apache.maven.plugins 37 | maven-failsafe-plugin 38 | 2.17 39 | 40 | 41 | run-tests 42 | 43 | integration-test 44 | verify 45 | 46 | 47 | 48 | http://${docker.containers.buildargs.ports.80/tcp.host}:${docker.containers.buildargs.ports.80/tcp.port} 49 | 50 | 51 | 52 | 53 | 54 | 55 | @project.groupId@ 56 | @project.artifactId@ 57 | @project.version@ 58 | 59 | 60 | build 61 | 62 | build-images 63 | 64 | 65 | 66 | 67 | buildargs 68 | ${project.basedir}/src/test/resources/Dockerfile 69 | buildargs:tahahag 70 | 71 | nginx 72 | 73 | true 74 | 75 | 76 | 77 | 78 | 79 | start 80 | 81 | 82 | 83 | buildargs 84 | buildargs 85 | 86 | 87 | 88 | 89 | start-containers 90 | 91 | 92 | 93 | stop 94 | 95 | 96 | 97 | stop-containers 98 | 99 | 100 | 101 | verify 102 | 103 | verify 104 | 105 | 106 | 107 | 108 | 109 | maven-compiler-plugin 110 | 3.1 111 | 112 | ${maven.compiler.source} 113 | ${maven.compiler.target} 114 | ${project.build.sourceEncoding} 115 | 116 | 117 | 118 | maven-deploy-plugin 119 | 2.8.1 120 | 121 | true 122 | 123 | 124 | 125 | maven-enforcer-plugin 126 | 1.3.1 127 | 128 | 129 | enforce-maven-version 130 | 131 | enforce 132 | 133 | 134 | 135 | 136 | @maven.required.version@ 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | -------------------------------------------------------------------------------- /src/it/skip-using-property-it/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | net.wouterdanes.docker.it 7 | simple-it 8 | 1.0-SNAPSHOT 9 | 10 | A simple IT verifying the basic use case. 11 | 12 | 13 | UTF-8 14 | 1.7 15 | 1.7 16 | true 17 | 18 | 19 | 20 | 21 | junit 22 | junit 23 | 4.8.2 24 | test 25 | 26 | 27 | org.apache.httpcomponents 28 | httpclient 29 | 4.3.3 30 | test 31 | 32 | 33 | 34 | 35 | 36 | 37 | org.apache.maven.plugins 38 | maven-failsafe-plugin 39 | 2.17 40 | 41 | 42 | run-tests 43 | 44 | integration-test 45 | verify 46 | 47 | 48 | 49 | http://${docker.containers.cache.ports.80/tcp.host}:${docker.containers.cache.ports.80/tcp.port} 50 | 51 | 52 | 53 | 54 | 55 | 56 | @project.groupId@ 57 | @project.artifactId@ 58 | @project.version@ 59 | 60 | 61 | build 62 | 63 | build-images 64 | 65 | 66 | 67 | 68 | nginx 69 | ${project.basedir}/src/test/resources/Dockerfile 70 | nginx-name:tahahag 71 | true 72 | 73 | 74 | 75 | 76 | 77 | start 78 | 79 | 80 | 81 | Debian 82 | debian:wheezy 83 | 84 | 85 | BusyBox 86 | busybox 87 | 88 | 89 | cache 90 | nginx 91 | 92 | 93 | 94 | 95 | start-containers 96 | 97 | 98 | 99 | stop 100 | 101 | stop-containers 102 | 103 | 104 | 105 | verify 106 | 107 | verify 108 | 109 | 110 | 111 | 112 | 113 | maven-compiler-plugin 114 | 3.1 115 | 116 | ${maven.compiler.source} 117 | ${maven.compiler.target} 118 | ${project.build.sourceEncoding} 119 | 120 | 121 | 122 | maven-deploy-plugin 123 | 2.8.1 124 | 125 | true 126 | 127 | 128 | 129 | maven-enforcer-plugin 130 | 1.3.1 131 | 132 | 133 | enforce-maven-version 134 | 135 | enforce 136 | 137 | 138 | 139 | 140 | @maven.required.version@ 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | -------------------------------------------------------------------------------- /src/main/java/net/wouterdanes/docker/provider/DockerProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Wouter Danes 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | */ 17 | 18 | package net.wouterdanes.docker.provider; 19 | 20 | import net.wouterdanes.docker.provider.model.ContainerCommitConfiguration; 21 | import net.wouterdanes.docker.provider.model.ContainerStartConfiguration; 22 | import net.wouterdanes.docker.provider.model.ExposedPort; 23 | import net.wouterdanes.docker.provider.model.ImageBuildConfiguration; 24 | import net.wouterdanes.docker.remoteapi.model.ContainerInspectionResult; 25 | import net.wouterdanes.docker.remoteapi.model.Credentials; 26 | import org.apache.maven.plugin.logging.Log; 27 | import org.eclipse.aether.RepositorySystem; 28 | import org.eclipse.aether.RepositorySystemSession; 29 | import org.eclipse.aether.repository.RemoteRepository; 30 | 31 | import java.util.List; 32 | 33 | /** 34 | * This interface represents an implementation that provides Docker functionality. Examples are: 35 | *
    36 | *
  • Local Docker via Unix Socket
  • 37 | *
  • Local Docker via TCP Socket
  • 38 | *
  • Remote (Boot2Docker) via TCP Socket
  • 39 | *
  • tutum.co
  • 40 | *
41 | */ 42 | public interface DockerProvider { 43 | 44 | /** 45 | * Sets (or un-sets) the credentials to be used when communicating with the Docker host. 46 | * @param credentials the user credentials, may be null 47 | */ 48 | void setCredentials(Credentials credentials); 49 | 50 | /** 51 | * Starts a docker container and returns the ID of the started container 52 | * @param configuration the configuration parameters 53 | * @return the ID of the started container 54 | */ 55 | ContainerInspectionResult startContainer(ContainerStartConfiguration configuration); 56 | 57 | /** 58 | * Stops a docker container 59 | * @param containerId the Id of the container to stop 60 | */ 61 | void stopContainer(String containerId); 62 | 63 | /** 64 | * Delete a docker container 65 | * @param containerId the Id of the container to delete 66 | */ 67 | void deleteContainer(String containerId); 68 | 69 | /** 70 | * Returns a list of ports exposed by the container, including information on how to reach them 71 | * @param containerId the Id of the container 72 | * @return {@link List} of {@link net.wouterdanes.docker.provider.model.ExposedPort}s 73 | */ 74 | List getExposedPorts(String containerId); 75 | 76 | /** 77 | * Builds a new Docker Image based on the passed configuration and returns the id of the newly created image. 78 | * @param image the image configuration to use 79 | * @return the id of the new Docker Image 80 | */ 81 | String buildImage(ImageBuildConfiguration image); 82 | 83 | /** 84 | * Create a new image from a container's changes 85 | * @param configuration the configuration parameters 86 | * @return the ID of the created image 87 | */ 88 | String commitContainer(ContainerCommitConfiguration configuration); 89 | 90 | /** 91 | * Removes an image from docker 92 | * @param imageId the Id of the images to remove 93 | */ 94 | void removeImage(String imageId); 95 | 96 | /** 97 | * Pushes an image from docker to a registry. 98 | * @param nameAndTag optional name and tag to be associated with pushed image 99 | */ 100 | void pushImage(String nameAndTag); 101 | 102 | /** 103 | * Associates an image with a new repo/tag. 104 | * @param imageId the Id of the image to tag 105 | * @param nameAndTag the repo/tag to assign 106 | */ 107 | void tagImage(String imageId, String nameAndTag); 108 | 109 | /** 110 | * Returns the logs of the specified container 111 | * @param containerId the Id of the container 112 | * @return the container's logs 113 | */ 114 | String getLogs(String containerId); 115 | 116 | /** 117 | * Sets the logger to use. 118 | * @param logger the Maven logger to use 119 | */ 120 | void setLogger(Log logger); 121 | 122 | /** 123 | * Sets the repositorySystem to use. 124 | * @param repositorySystem the Maven repositorySystem to use 125 | */ 126 | void setRepositorySystem(RepositorySystem repositorySystem); 127 | 128 | /** 129 | * Sets the repositorySystemSession to use. 130 | * @param repositorySystemSession the Maven repositorySystemSession to use 131 | */ 132 | void setRepositorySystemSession(RepositorySystemSession repositorySystemSession); 133 | 134 | /** 135 | * Sets the remoteRepositories to use. 136 | * @param remoteRepositories the Maven remoteRepositories to use 137 | */ 138 | void setRemoteRepositories(List remoteRepositories); 139 | } 140 | -------------------------------------------------------------------------------- /src/it/simple-it/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | net.wouterdanes.docker.it 7 | simple-it 8 | 1.0-SNAPSHOT 9 | 10 | A simple IT verifying the basic use case. 11 | 12 | 13 | UTF-8 14 | 1.7 15 | 1.7 16 | 17 | 18 | 19 | 20 | junit 21 | junit 22 | 4.8.2 23 | test 24 | 25 | 26 | org.apache.httpcomponents 27 | httpclient 28 | 4.3.3 29 | test 30 | 31 | 32 | 33 | 34 | 35 | 36 | org.apache.maven.plugins 37 | maven-failsafe-plugin 38 | 2.17 39 | 40 | 41 | run-tests 42 | 43 | integration-test 44 | verify 45 | 46 | 47 | 48 | 49 | http://${docker.containers.cache.ports.80/tcp.host}:${docker.containers.cache.ports.80/tcp.port} 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | @project.groupId@ 58 | @project.artifactId@ 59 | @project.version@ 60 | 61 | 62 | build 63 | 64 | build-images 65 | 66 | 67 | 68 | 69 | nginx 70 | ${project.basedir}/src/test/resources/Dockerfile 71 | 72 | 73 | ${project.basedir}/src/test/resources/1234567890abcdefghij1234567890abcdefghij1234567890abcdefghij1234567890abcdefghij1234567890abcdefghij 74 | 75 | 76 | nginx-name:tahahag 77 | true 78 | 79 | 80 | 81 | 82 | 83 | start 84 | 85 | 86 | 87 | cache 88 | nginx 89 | 90 | 91 | 92 | 93 | start-containers 94 | 95 | 96 | 97 | stop 98 | 99 | 100 | 101 | stop-containers 102 | 103 | 104 | 105 | verify 106 | 107 | verify 108 | 109 | 110 | 111 | 112 | 113 | maven-compiler-plugin 114 | 3.1 115 | 116 | ${maven.compiler.source} 117 | ${maven.compiler.target} 118 | ${project.build.sourceEncoding} 119 | 120 | 121 | 122 | maven-deploy-plugin 123 | 2.8.1 124 | 125 | true 126 | 127 | 128 | 129 | maven-enforcer-plugin 130 | 1.3.1 131 | 132 | 133 | enforce-maven-version 134 | 135 | enforce 136 | 137 | 138 | 139 | 140 | @maven.required.version@ 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | -------------------------------------------------------------------------------- /src/it/skip-docker-it/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | net.wouterdanes.docker.it 7 | simple-it 8 | 1.0-SNAPSHOT 9 | 10 | A simple IT verifying the basic use case. 11 | 12 | 13 | UTF-8 14 | 1.7 15 | 1.7 16 | 17 | 18 | 19 | 20 | junit 21 | junit 22 | 4.8.2 23 | test 24 | 25 | 26 | org.apache.httpcomponents 27 | httpclient 28 | 4.3.3 29 | test 30 | 31 | 32 | 33 | 34 | 35 | 36 | org.apache.maven.plugins 37 | maven-failsafe-plugin 38 | 2.17 39 | 40 | 41 | run-tests 42 | 43 | integration-test 44 | verify 45 | 46 | 47 | 48 | http://${docker.containers.cache.ports.80/tcp.host}:${docker.containers.cache.ports.80/tcp.port} 49 | 50 | 51 | 52 | 53 | 54 | 55 | @project.groupId@ 56 | @project.artifactId@ 57 | @project.version@ 58 | 59 | 60 | build 61 | 62 | build-images 63 | 64 | 65 | true 66 | 67 | 68 | nginx 69 | ${project.basedir}/src/test/resources/Dockerfile 70 | nginx-name:tahahag 71 | true 72 | 73 | 74 | 75 | 76 | 77 | start 78 | 79 | true 80 | 81 | 82 | Debian 83 | debian:wheezy 84 | 85 | 86 | BusyBox 87 | busybox 88 | 89 | 90 | cache 91 | nginx 92 | 93 | 94 | 95 | 96 | start-containers 97 | 98 | 99 | 100 | stop 101 | 102 | true 103 | 104 | 105 | stop-containers 106 | 107 | 108 | 109 | verify 110 | 111 | verify 112 | 113 | 114 | 115 | 116 | 117 | maven-compiler-plugin 118 | 3.1 119 | 120 | ${maven.compiler.source} 121 | ${maven.compiler.target} 122 | ${project.build.sourceEncoding} 123 | 124 | 125 | 126 | maven-deploy-plugin 127 | 2.8.1 128 | 129 | true 130 | 131 | 132 | 133 | maven-enforcer-plugin 134 | 1.3.1 135 | 136 | 137 | enforce-maven-version 138 | 139 | enforce 140 | 141 | 142 | 143 | 144 | @maven.required.version@ 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # version 5.0.0 2 | - **NOTE**: You now need at least Docker 1.9 to use this plugin, due to the support for buildargs. Some CVE's also got fixed, so you probably want to upgrade that Docker Engine regardless. :-) 3 | - You can now maintain your docker registry credentials in maven's settings.xml. Thanks to [Cedric Thiebault](https://github.com/cthiebault) for the pull request! 4 | - Added the ability to set a Mac address for a started container. Thanks to [Mark Collin](https://github.com/Ardesco) for making the pull request! 5 | - Added the ability to pass build args to the Dockerfile when building an image. This should help with parametrizing your Dockerfiles while still having them be useful outside of Maven. Thanks to [Mark Collin](https://github.com/Ardesco) for making the pull request! 6 | - **BUGFIX**: Plugin now properly removes created volumes when deleting a container. I apologize profoundly to any disks who were hurt in the last 4 versions. 7 | - **BUGFIX**: Sometimes file names are long, sometimes they are so long that certain maven plugins fall over.. The plugin should now be able to handle adding files with paths longer than 100 bytes. Especially useful when you're all about organizing your hard drive into many folders! 8 | - Plugin should be more resilient when docker logging drivers lie about the amount of characters to grab from the stream. (Don't ask...) 9 | 10 | # version 4.2.1 11 | - Fixed backwards compatibility with docker daemons older than 1.10, the daemon should now be able to figure out the built image IDs again. Sorry! 12 | 13 | # version 4.2.0 14 | - You can now add maven dependencies to your docker images. Thanks to [Mark Collin](https://github.com/Ardesco) for making the pull request! 15 | - Fixed building an image under Docker 1.10, the output of a quiet build had significantly changed, so the plugin got all confused and basically gave up. Poor plugin. :( The plugin should now be able to decrypt the fairly cryptic statement "sha256:{insert random uuid}" 16 | 17 | # version 4.1.1 18 | - Files added to the tar-ball sent to the docker daemon are no longer fully loaded into memory. The plugin should no longer need hundreds of MegaBytes of memory to work with big files. :-) Sorry! And thanks to [Cedric Thiebault](https://github.com/cthiebault) for fixing! 19 | 20 | # version 4.1.0 21 | - Now passes authentication header to build image task to allow pulling images from a private repository. 22 | - Now allows you to persist the logs for all containers to a certain folder. 23 | 24 | # version 4.0.0 25 | - Now needs Java8 to run, Java7 has been deprecated for a while 26 | - The plugin now uses the "stop" instead of "kill" command to stop containers in the `post-integration-test` phase. 27 | - Added the ability to commit containers after running them in the `post-integration-test` phase. 28 | 29 | # version 3.1.0 30 | - Now outputs the daemon responses while building an image. 31 | 32 | # version 3.0.1 33 | - Development: Fixed the VerifyHostnameSetIT which generated a NPE because of a non-existent logger. 34 | - Tagging is now "forced", untagging a previous image with the same tag. 35 | - Fixed the exception message when tagging an image fails. 36 | 37 | # version 3.0 38 | - Docker images are now constructed somewhat differently. You have you specify the Dockerfile as a special entry. Files 39 | in the Dockerfile are now called artifacts and you can specify where in the tar they will be placed. It's now possible to 40 | point to whole folders and have those added to the tar ball in the build phase. Just put the path to the folder in 41 | `` tags. 42 | - Added the ability to set the hostname of a container 43 | 44 | # version 2.3 45 | - Docker 1.3: Added support for HTTPS encryption, fixing connection with Boot2docker 1.3. 46 | 47 | # version 2.2 48 | - Changed the default port for the http interface to the IANA port for docker: 2375. (thanks Erno Tukia!) 49 | - Fixed pulling images from a private repository. (thanks Erno Tukia!) 50 | 51 | # version 2.1.1 52 | - Bug fix: Image names can now contain hyphens and dots and get properly pushed. (thanks Erno Tukia!) 53 | 54 | # version 2.1 55 | - Fixed a bug in `start-container` goal that crashed the plugin if a container had `waitForStartup` set, but failed to 56 | start 57 | - `push-images` goal no longer allows you to try to push an image without a name. 58 | - DEV: the build now starts a docker registry in docker so you can integration test against a registry too. 59 | - A container now waits with starting up before linked containers have finished starting up. 60 | - Environment variables can now be specified for a starting container (thanks Scott Stevelinck!) 61 | 62 | # version 2.0 63 | - The plugin now targets v1.12 of the Docker remote API, which shipped with version 1.0 of Docker. So from this version 64 | on, you will need at least version 1.0 of the docker daemon. 65 | - The plugin now enables you to scan the stderr & stdout of a container for a certain phrase that indicates successful 66 | initialization of the container. This phrase can be any regular expression and it is globally matched. 67 | - The plugin now allows you to link containers that you start. 68 | 69 | # version 1.6 70 | - The plugin now forces at least Maven 3.1.1, because else the plugin will not function well 71 | - The plugin now checks for duplicate image IDs, failing the build if the same image ID is used twice 72 | - The plugin now checks for duplicate container IDs, failing the build if the same container ID is used twice 73 | - Fixed Push & Tag for private repositories that got broken in 1.5 74 | - Upgraded maven plugin/api dependencies to the version that comes with Maven 3.1.1 75 | -------------------------------------------------------------------------------- /src/broken-it/push-with-creds-it/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | net.wouterdanes.docker.it 7 | push-with-creds-it 8 | 1.0-SNAPSHOT 9 | 10 | A simple IT verifying the basic use case. 11 | 12 | 13 | UTF-8 14 | 1.7 15 | 1.7 16 | 17 | 18 | 19 | 20 | junit 21 | junit 22 | 4.8.2 23 | test 24 | 25 | 26 | org.apache.httpcomponents 27 | httpclient 28 | 4.3.3 29 | test 30 | 31 | 32 | 33 | 34 | 35 | 36 | org.apache.maven.plugins 37 | maven-failsafe-plugin 38 | 2.17 39 | 40 | 41 | run-tests 42 | 43 | integration-test 44 | verify 45 | 46 | 47 | 48 | http://${docker.containers.cache.ports.80/tcp.host}:${docker.containers.cache.ports.80/tcp.port} 49 | 50 | 51 | 52 | 53 | 54 | 55 | @project.groupId@ 56 | @project.artifactId@ 57 | @project.version@ 58 | 59 | 60 | build 61 | 62 | build-images 63 | 64 | 65 | 66 | 67 | nginx 68 | ${project.basedir}/src/test/resources/Dockerfile 69 | drek:latest 70 | true 71 | true 72 | ${docker.registry} 73 | 74 | 75 | nginxier 76 | ${project.basedir}/src/test/resources/Dockerfile 77 | ${docker.registry}/nginxier:latest 78 | true 79 | true 80 | 81 | 82 | 83 | 84 | 85 | start 86 | 87 | 88 | 89 | cache 90 | nginx 91 | 92 | 93 | 94 | 95 | start-containers 96 | 97 | 98 | 99 | stop 100 | 101 | stop-containers 102 | 103 | 104 | 105 | verify 106 | 107 | verify 108 | 109 | 110 | 111 | push 112 | 113 | push-images 114 | 115 | 116 | 117 | 118 | 119 | maven-compiler-plugin 120 | 3.1 121 | 122 | ${maven.compiler.source} 123 | ${maven.compiler.target} 124 | ${project.build.sourceEncoding} 125 | 126 | 127 | 128 | maven-deploy-plugin 129 | 2.8.1 130 | 131 | true 132 | 133 | 134 | 135 | maven-enforcer-plugin 136 | 1.3.1 137 | 138 | 139 | enforce-maven-version 140 | 141 | enforce 142 | 143 | 144 | 145 | 146 | @maven.required.version@ 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | -------------------------------------------------------------------------------- /src/main/java/net/wouterdanes/docker/remoteapi/ImagesService.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Wouter Danes 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | */ 17 | 18 | package net.wouterdanes.docker.remoteapi; 19 | 20 | import com.google.gson.JsonElement; 21 | import com.google.gson.JsonObject; 22 | import com.google.gson.JsonStreamParser; 23 | import net.wouterdanes.docker.remoteapi.model.ImageDescriptor; 24 | 25 | import javax.ws.rs.WebApplicationException; 26 | import javax.ws.rs.client.WebTarget; 27 | import javax.ws.rs.core.MediaType; 28 | import javax.ws.rs.core.Response; 29 | import java.io.BufferedReader; 30 | import java.io.InputStream; 31 | import java.io.InputStreamReader; 32 | import java.util.Optional; 33 | 34 | /** 35 | * This class is responsible for talking to the Docker Remote API "images" endpoint.
See 37 | * http://docs.docker.io/reference/api/docker_remote_api_v1.12/#22-images 38 | */ 39 | public class ImagesService extends BaseService { 40 | 41 | public ImagesService(String dockerApiRoot) { 42 | super(dockerApiRoot, "/images"); 43 | } 44 | 45 | public void deleteImage(final String imageId) { 46 | try { 47 | getServiceEndPoint() 48 | .path(imageId) 49 | .request(MediaType.APPLICATION_JSON_TYPE) 50 | .delete(String.class); 51 | } catch (WebApplicationException e) { 52 | throw makeImageTargetingException("Cannot remove image", e); 53 | } 54 | } 55 | 56 | public String pullImage(final String image) { 57 | ImageDescriptor descriptor = new ImageDescriptor(image); 58 | 59 | WebTarget target = getServiceEndPoint() 60 | .path("create"); 61 | 62 | target = target.queryParam("fromImage", descriptor.getRegistryRepositoryAndImage()); 63 | 64 | if (descriptor.getTag().isPresent()) { 65 | target = target.queryParam("tag", descriptor.getTag().get()); 66 | } 67 | 68 | Response response = target.request() 69 | .header(REGISTRY_AUTH_HEADER, getRegistryAuthHeaderValue()) 70 | .accept(MediaType.APPLICATION_JSON_TYPE) 71 | .post(null); 72 | 73 | InputStream inputStream = (InputStream) response.getEntity(); 74 | 75 | parseStreamToDisplayImageDownloadStatus(inputStream); 76 | 77 | return response.readEntity(String.class); 78 | } 79 | 80 | private static void parseStreamToDisplayImageDownloadStatus(final InputStream inputStream) { 81 | InputStreamReader isr = new InputStreamReader(inputStream); 82 | BufferedReader reader = new BufferedReader(isr); 83 | 84 | JsonStreamParser parser = new JsonStreamParser(reader); 85 | 86 | while (parser.hasNext()) { 87 | JsonElement element = parser.next(); 88 | JsonObject object = element.getAsJsonObject(); 89 | if (object.has("status")) { 90 | System.out.print("."); 91 | } 92 | if (object.has("error")) { 93 | System.err.println("ERROR: " + object.get("error").getAsString()); 94 | } 95 | } 96 | System.out.println(""); 97 | } 98 | 99 | public String pushImage(String nameAndTag) { 100 | try { 101 | WebTarget target = createPushRequestFromTag(nameAndTag); 102 | 103 | return target.request() 104 | .header(REGISTRY_AUTH_HEADER, getRegistryAuthHeaderValue()) 105 | .accept(MediaType.APPLICATION_JSON_TYPE) 106 | .post(null, String.class); 107 | 108 | } catch (WebApplicationException e) { 109 | throw makeImageTargetingException(nameAndTag, e); 110 | 111 | } 112 | } 113 | 114 | public void tagImage(final String imageId, final String nameAndTag) { 115 | ImageDescriptor descriptor = new ImageDescriptor(nameAndTag); 116 | 117 | WebTarget target = getServiceEndPoint() 118 | .path(imageId) 119 | .path("tag") 120 | .queryParam("repo", descriptor.getRegistryRepositoryAndImage()) 121 | .queryParam("force", 1); 122 | 123 | Optional targetTag = descriptor.getTag(); 124 | if (targetTag.isPresent()) { 125 | target = target.queryParam("tag", targetTag.get()); 126 | } 127 | 128 | Response response = target.request() 129 | .accept(MediaType.APPLICATION_JSON_TYPE) 130 | .post(null); 131 | 132 | Response.StatusType statusInfo = response.getStatusInfo(); 133 | 134 | response.close(); 135 | 136 | checkImageTargetingResponse(imageId, statusInfo); 137 | } 138 | 139 | private WebTarget createPushRequestFromTag(final String nameAndTag) { 140 | ImageDescriptor descriptor = new ImageDescriptor(nameAndTag); 141 | WebTarget target = getServiceEndPoint() 142 | .path(descriptor.getRegistryRepositoryAndImage()) 143 | .path("push"); 144 | 145 | if (descriptor.getTag().isPresent()) { 146 | target = target.queryParam("tag", descriptor.getTag().get()); 147 | } 148 | 149 | return target; 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /src/test/java/net/wouterdanes/docker/remoteapi/ImageDescriptorTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Wouter Danes 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | */ 17 | 18 | package net.wouterdanes.docker.remoteapi; 19 | 20 | import org.junit.Test; 21 | 22 | import net.wouterdanes.docker.remoteapi.model.ImageDescriptor; 23 | import static junit.framework.Assert.assertEquals; 24 | 25 | public class ImageDescriptorTest { 26 | 27 | @Test 28 | public void testThatElementsAreDetected() throws Exception { 29 | assertDescriptor("ubuntu:precise", "ubuntu:precise", null, null, "ubuntu", "precise", 30 | "ubuntu", "ubuntu", "ubuntu:precise"); 31 | assertDescriptor("ubuntu", "ubuntu", null, null, "ubuntu", null, 32 | "ubuntu", "ubuntu", "ubuntu"); 33 | assertDescriptor("tutum.co/ubuntu", "tutum.co/ubuntu", "tutum.co", null, "ubuntu", null, 34 | "tutum.co/ubuntu", "ubuntu", "ubuntu"); 35 | assertDescriptor("tutum.co/ubuntu:precise", "tutum.co/ubuntu:precise", "tutum.co", null, "ubuntu", "precise", 36 | "tutum.co/ubuntu", "ubuntu", "ubuntu:precise"); 37 | assertDescriptor("wouter/ubuntu", "wouter/ubuntu", null, "wouter", "ubuntu", null, 38 | "wouter/ubuntu", "wouter/ubuntu", "wouter/ubuntu"); 39 | assertDescriptor("tutum.co/wouter/ubuntu", "tutum.co/wouter/ubuntu", "tutum.co", "wouter", "ubuntu", null, 40 | "tutum.co/wouter/ubuntu", "wouter/ubuntu", "wouter/ubuntu"); 41 | assertDescriptor("tutum.co/wouter/ubuntu:precise", 42 | "tutum.co/wouter/ubuntu:precise", "tutum.co", "wouter", "ubuntu", "precise", 43 | "tutum.co/wouter/ubuntu", "wouter/ubuntu", "wouter/ubuntu:precise"); 44 | assertDescriptor("wouter/ubuntu:precise", "wouter/ubuntu:precise", null, "wouter", "ubuntu", "precise", 45 | "wouter/ubuntu", "wouter/ubuntu", "wouter/ubuntu:precise"); 46 | assertDescriptor("abde1231adc", "abde1231adc", null, null, "abde1231adc", null, 47 | "abde1231adc", "abde1231adc", "abde1231adc"); 48 | 49 | // support registries with port numbers 50 | assertDescriptor("tutum.co:5000/ubuntu", 51 | "tutum.co:5000/ubuntu", "tutum.co:5000", null, "ubuntu", null, 52 | "tutum.co:5000/ubuntu", "ubuntu", "ubuntu"); 53 | assertDescriptor("tutum.co:5000/ubuntu:precise", 54 | "tutum.co:5000/ubuntu:precise", "tutum.co:5000", null, "ubuntu", "precise", 55 | "tutum.co:5000/ubuntu", "ubuntu", "ubuntu:precise"); 56 | assertDescriptor("tutum.co:5000/wouter/ubuntu", 57 | "tutum.co:5000/wouter/ubuntu", "tutum.co:5000", "wouter", "ubuntu", null, 58 | "tutum.co:5000/wouter/ubuntu", "wouter/ubuntu", "wouter/ubuntu"); 59 | assertDescriptor("tutum.co:5000/wouter/ubuntu:precise", 60 | "tutum.co:5000/wouter/ubuntu:precise", "tutum.co:5000", "wouter", "ubuntu", "precise", 61 | "tutum.co:5000/wouter/ubuntu", "wouter/ubuntu", "wouter/ubuntu:precise"); 62 | // more complicated URL 63 | assertDescriptor("index_01-reg.tutum.co:5000/wouter/ubuntu:precise", 64 | "index_01-reg.tutum.co:5000/wouter/ubuntu:precise", "index_01-reg.tutum.co:5000", "wouter", "ubuntu", "precise", 65 | "index_01-reg.tutum.co:5000/wouter/ubuntu", "wouter/ubuntu", "wouter/ubuntu:precise"); 66 | 67 | // check for image names with "." and "-" 68 | assertDescriptor("localhost:5000/library/ubuntu-precise", "localhost:5000/library/ubuntu-precise", 69 | "localhost:5000", "library", "ubuntu-precise", null, "localhost:5000/library/ubuntu-precise", 70 | "library/ubuntu-precise", "library/ubuntu-precise"); 71 | assertDescriptor("localhost:5000/library/ubuntu.precise", "localhost:5000/library/ubuntu.precise", 72 | "localhost:5000", "library", "ubuntu.precise", null, "localhost:5000/library/ubuntu.precise", 73 | "library/ubuntu.precise", "library/ubuntu.precise"); 74 | 75 | // check for tags "." and "-" in them 76 | assertDescriptor("ubuntu:14.04", "ubuntu:14.04", null, null, "ubuntu", "14.04", 77 | "ubuntu", "ubuntu", "ubuntu:14.04"); 78 | assertDescriptor("ubuntu:mark-2", "ubuntu:mark-2", null, null, "ubuntu", "mark-2", 79 | "ubuntu", "ubuntu", "ubuntu:mark-2"); 80 | } 81 | 82 | private static void assertDescriptor(String qualifier, 83 | String id, String registry, String repository, 84 | String image, String tag, String registryRepoAndImage, String repoAndImage, String repoImageAndTag) { 85 | ImageDescriptor descriptor = new ImageDescriptor(qualifier); 86 | assertEquals("Id should be correct", id, descriptor.getId()); 87 | assertEquals("Registry should be correct", registry, descriptor.getRegistry().orElse(null)); 88 | assertEquals("Repository should be correct", repository, descriptor.getRepository().orElse(null)); 89 | assertEquals("Image should be correct", image, descriptor.getImage()); 90 | assertEquals("Tag should be correct", tag, descriptor.getTag().orElse(null)); 91 | assertEquals("Repository+Image should be correct", repoAndImage, descriptor.getRepositoryAndImage()); 92 | assertEquals("Repository+Image+Tag should be correct", repoImageAndTag, descriptor.getRepositoryImageAndTag()); 93 | assertEquals("Registry, Repository+Image should be correct", registryRepoAndImage, descriptor.getRegistryRepositoryAndImage()); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/main/java/net/wouterdanes/docker/remoteapi/ContainersService.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Wouter Danes 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | */ 17 | 18 | package net.wouterdanes.docker.remoteapi; 19 | 20 | import net.wouterdanes.docker.remoteapi.exception.ContainerNotFoundException; 21 | import net.wouterdanes.docker.remoteapi.exception.DockerException; 22 | import net.wouterdanes.docker.remoteapi.model.ContainerCreateRequest; 23 | import net.wouterdanes.docker.remoteapi.model.ContainerCreateResponse; 24 | import net.wouterdanes.docker.remoteapi.model.ContainerInspectionResult; 25 | import net.wouterdanes.docker.remoteapi.model.ContainerStartRequest; 26 | 27 | import javax.ws.rs.HttpMethod; 28 | import javax.ws.rs.WebApplicationException; 29 | import javax.ws.rs.client.Entity; 30 | import javax.ws.rs.core.MediaType; 31 | import javax.ws.rs.core.Response; 32 | import java.nio.ByteBuffer; 33 | import java.nio.ByteOrder; 34 | import java.nio.charset.Charset; 35 | 36 | /** 37 | * This class is responsible for talking to the Docker Remote API "containers" endpoint.
See 39 | * http://docs.docker.io/reference/api/docker_remote_api_v1.12/#21-containers 40 | */ 41 | public class ContainersService extends BaseService { 42 | 43 | public ContainersService(String dockerApiRoot) { 44 | super(dockerApiRoot, "/containers"); 45 | } 46 | 47 | public String createContainer(ContainerCreateRequest containerCreateRequest) { 48 | String createResponseStr; 49 | try { 50 | createResponseStr = getServiceEndPoint() 51 | .path("/create") 52 | .request(MediaType.APPLICATION_JSON_TYPE) 53 | .post(Entity.entity(toJson(containerCreateRequest), MediaType.APPLICATION_JSON_TYPE), String.class); 54 | } catch (WebApplicationException e) { 55 | throw makeImageTargetingException(containerCreateRequest.getImage(), e); 56 | } 57 | ContainerCreateResponse createResponse = toObject(createResponseStr, ContainerCreateResponse.class); 58 | return createResponse.getId(); 59 | } 60 | 61 | public void deleteContainer(String id) { 62 | Response response = getServiceEndPoint() 63 | .path(id) 64 | .queryParam("v", 1) 65 | .request() 66 | .delete(); 67 | 68 | Response.StatusType statusInfo = response.getStatusInfo(); 69 | response.close(); 70 | 71 | checkContainerTargetingResponse(id, statusInfo); 72 | } 73 | 74 | public String getLogs(final String containerId) { 75 | byte[] bytes = getServiceEndPoint() 76 | .path(containerId) 77 | .path("logs") 78 | .queryParam("stdout", 1) 79 | .queryParam("stderr", 1) 80 | .request("application/vnd.docker.raw-stream") 81 | .get(byte[].class); 82 | 83 | // To see how docker returns the logs and why it's parsed like this: 84 | // http://docs.docker.com/v1.2/reference/api/docker_remote_api_v1.14/#attach-to-a-container 85 | StringBuilder logs = new StringBuilder(); 86 | ByteBuffer bb = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN); 87 | 88 | while (bb.hasRemaining()) { 89 | bb.position(bb.position() + 4); 90 | int frameLength = Math.min(bb.getInt(), bb.remaining()); 91 | byte[] frame = new byte[frameLength]; 92 | bb.get(frame); 93 | logs.append(new String(frame, Charset.forName("UTF-8"))); 94 | } 95 | 96 | return logs.toString(); 97 | } 98 | 99 | public ContainerInspectionResult inspectContainer(final String containerId) { 100 | String json = getServiceEndPoint() 101 | .path(containerId) 102 | .path("json") 103 | .request(MediaType.APPLICATION_JSON_TYPE) 104 | .get(String.class); 105 | 106 | return toObject(json, ContainerInspectionResult.class); 107 | } 108 | 109 | public void killContainer(String id) { 110 | Response response = getServiceEndPoint() 111 | .path(id) 112 | .path("/kill") 113 | .request() 114 | .method(HttpMethod.POST); 115 | 116 | Response.StatusType statusInfo = response.getStatusInfo(); 117 | response.close(); 118 | 119 | checkContainerTargetingResponse(id, statusInfo); 120 | } 121 | 122 | public void startContainer(String id, ContainerStartRequest configuration) { 123 | Response response = getServiceEndPoint() 124 | .path(id) 125 | .path("/start") 126 | .request() 127 | .post(Entity.entity(toJson(configuration), MediaType.APPLICATION_JSON_TYPE)); 128 | 129 | Response.StatusType statusInfo = response.getStatusInfo(); 130 | response.close(); 131 | 132 | checkContainerTargetingResponse(id, statusInfo); 133 | } 134 | 135 | public void stopContainer(String id) { 136 | Response response = getServiceEndPoint() 137 | .path(id) 138 | .path("/stop") 139 | .queryParam("t", 10) 140 | .request() 141 | .method(HttpMethod.POST); 142 | 143 | Response.StatusType statusInfo = response.getStatusInfo(); 144 | response.close(); 145 | 146 | checkContainerTargetingResponse(id, statusInfo); 147 | } 148 | 149 | private static void checkContainerTargetingResponse(final String id, final Response.StatusType statusInfo) { 150 | switch (statusInfo.getStatusCode()) { 151 | case 404: 152 | throw new ContainerNotFoundException(id); 153 | case 500: 154 | throw new DockerException(statusInfo.getReasonPhrase()); 155 | } 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /src/test/java/net/wouterdanes/docker/maven/BuildImageMojoTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Wouter Danes 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | */ 17 | 18 | package net.wouterdanes.docker.maven; 19 | 20 | import net.wouterdanes.docker.provider.AbstractFakeDockerProvider; 21 | import net.wouterdanes.docker.provider.DockerExceptionThrowingDockerProvider; 22 | import net.wouterdanes.docker.provider.DockerProviderSupplier; 23 | import net.wouterdanes.docker.provider.model.ImageBuildConfiguration; 24 | import net.wouterdanes.docker.provider.model.PushableImage; 25 | import org.apache.maven.plugin.MojoExecutionException; 26 | import org.apache.maven.plugin.MojoFailureException; 27 | import org.junit.After; 28 | import org.junit.Before; 29 | import org.junit.Test; 30 | import org.mockito.Mockito; 31 | 32 | import java.util.*; 33 | 34 | import static org.junit.Assert.*; 35 | import static org.mockito.Matchers.any; 36 | 37 | public class BuildImageMojoTest { 38 | 39 | private static final String FAKE_PROVIDER_KEY = UUID.randomUUID().toString(); 40 | private static final String IMAGEID = UUID.randomUUID().toString(); 41 | private static final String STARTID = UUID.randomUUID().toString(); 42 | private static final String NAMEANDTAG = UUID.randomUUID().toString(); 43 | private static final String REGISTRY = UUID.randomUUID().toString(); 44 | private static final String REGISTRYANDNAMEANDTAG = REGISTRY + "/" + NAMEANDTAG; 45 | 46 | private BuildImageMojo mojo = new BuildImageMojo(); 47 | 48 | private ImageBuildConfiguration mockImage; 49 | 50 | @Before 51 | public void setUp() throws Exception { 52 | mojo.setPluginContext(new HashMap()); 53 | 54 | FakeDockerProvider.instance = Mockito.mock(FakeDockerProvider.class); 55 | Mockito.when(FakeDockerProvider.instance.buildImage(any(ImageBuildConfiguration.class))).thenReturn(IMAGEID); 56 | 57 | DockerProviderSupplier.registerProvider(FAKE_PROVIDER_KEY, FakeDockerProvider.class); 58 | DockerExceptionThrowingDockerProvider.class.newInstance(); 59 | 60 | // valid by default 61 | mockImage = Mockito.mock(ImageBuildConfiguration.class); 62 | Mockito.when(mockImage.getId()).thenReturn(STARTID); 63 | Mockito.when(mockImage.getNameAndTag()).thenReturn(NAMEANDTAG); 64 | 65 | List images = Collections.singletonList(mockImage); 66 | 67 | mojo.setImages(images); 68 | } 69 | 70 | @After 71 | public void tearDown() throws Exception { 72 | DockerProviderSupplier.removeProvider(FAKE_PROVIDER_KEY); 73 | } 74 | 75 | @Test 76 | public void testThatTheMojoBuildsAtLeastOneImageWhenAllImagesAreValid() throws Exception { 77 | executeMojo(FAKE_PROVIDER_KEY); 78 | 79 | Mockito.verify(FakeDockerProvider.instance, Mockito.atLeastOnce()).buildImage(any(ImageBuildConfiguration.class)); 80 | 81 | assertTrue(mojo.getPluginErrors().isEmpty()); 82 | assertImageNotEnqueuedForPush(); 83 | } 84 | 85 | @Test 86 | public void testThatTheMojoLogsAnErrorWhenBuildingAnImageFails() throws Exception { 87 | executeMojo(DockerExceptionThrowingDockerProvider.PROVIDER_KEY); 88 | 89 | assertFalse(mojo.getPluginErrors().isEmpty()); 90 | assertImageNotEnqueuedForPush(); 91 | } 92 | 93 | @Test 94 | public void testThatTheMojoEnqueuesImageWithoutRegistryForPush() throws Exception { 95 | Mockito.when(mockImage.isPush()).thenReturn(true); 96 | 97 | executeMojo(FAKE_PROVIDER_KEY); 98 | 99 | assertTrue(mojo.getPluginErrors().isEmpty()); 100 | assertImageEnqueuedForPush(NAMEANDTAG); 101 | } 102 | 103 | @Test 104 | public void testThatTheMojoEnqueuesImageWithRegistryForPush() throws Exception { 105 | Mockito.when(mockImage.isPush()).thenReturn(true); 106 | Mockito.when(mockImage.getRegistry()).thenReturn(REGISTRY); 107 | 108 | executeMojo(FAKE_PROVIDER_KEY); 109 | 110 | assertTrue(mojo.getPluginErrors().isEmpty()); 111 | assertImageEnqueuedForPush(REGISTRYANDNAMEANDTAG); 112 | } 113 | 114 | @Test 115 | public void testThatTheMojoEnqueuesImageWithoutNameForPush() throws Exception { 116 | Mockito.when(mockImage.isPush()).thenReturn(true); 117 | Mockito.when(mockImage.getNameAndTag()).thenReturn(null); 118 | 119 | executeMojo(FAKE_PROVIDER_KEY); 120 | 121 | assertTrue(mojo.getPluginErrors().isEmpty()); 122 | assertImageEnqueuedForPush(null); 123 | } 124 | 125 | @Test(expected = MojoExecutionException.class) 126 | public void testThatTheMojoThrowsAnExceptionWhenDuplicateImageIdsExist() throws Exception { 127 | List images = new ArrayList<>(2); 128 | images.add(mockImage); 129 | images.add(mockImage); 130 | 131 | mojo.setImages(images); 132 | 133 | mojo.execute(); 134 | assertImageEnqueuedForPush(null); 135 | } 136 | 137 | private void executeMojo(String provider) throws MojoExecutionException, MojoFailureException { 138 | mojo.setProviderName(provider); 139 | mojo.execute(); 140 | } 141 | 142 | private void assertImageNotEnqueuedForPush() { 143 | assertTrue(mojo.getImagesToPush().isEmpty()); 144 | } 145 | 146 | private void assertImageEnqueuedForPush(String expectedNameAndTag) { 147 | assertEquals(1, mojo.getImagesToPush().size()); 148 | 149 | PushableImage actual = mojo.getImagesToPush().get(0); 150 | assertEquals(BuildImageMojoTest.IMAGEID, actual.getImageId()); 151 | assertEquals(expectedNameAndTag, actual.getNameAndTag().orElse(null)); 152 | } 153 | 154 | public static class FakeDockerProvider extends AbstractFakeDockerProvider { 155 | 156 | private static FakeDockerProvider instance; 157 | 158 | @Override 159 | protected AbstractFakeDockerProvider getInstance() { 160 | return instance; 161 | } 162 | } 163 | 164 | } 165 | --------------------------------------------------------------------------------