├── src ├── test │ ├── resources │ │ └── testTargetDir │ │ │ ├── fileA.txt │ │ │ └── fileB.txt │ └── groovy │ │ └── se │ │ └── transmode │ │ └── gradle │ │ └── plugins │ │ └── docker │ │ ├── DockerPluginTest.groovy │ │ ├── DockerTaskBaseTest.groovy │ │ ├── DockerTaskTest.groovy │ │ ├── image │ │ └── DockerfileTest.groovy │ │ └── DockerRunTaskTest.groovy ├── main │ ├── resources │ │ ├── nginx.Dockerfile │ │ └── META-INF │ │ │ └── gradle-plugins │ │ │ ├── docker.properties │ │ │ └── se.transmode.docker.properties │ ├── groovy │ │ └── se │ │ │ └── transmode │ │ │ └── gradle │ │ │ └── plugins │ │ │ └── docker │ │ │ ├── DockerPluginExtension.groovy │ │ │ ├── DockerRunTask.groovy │ │ │ ├── DockerTaskBase.groovy │ │ │ ├── client │ │ │ └── NativeDockerClient.groovy │ │ │ ├── LegacyDockerfileMethods.groovy │ │ │ ├── DockerPlugin.groovy │ │ │ ├── DockerTask.groovy │ │ │ └── image │ │ │ └── Dockerfile.groovy │ └── java │ │ └── se │ │ └── transmode │ │ └── gradle │ │ └── plugins │ │ └── docker │ │ ├── JavaBaseImage.java │ │ └── client │ │ ├── DockerClient.java │ │ └── JavaDockerClient.java └── integTest │ ├── resources │ └── nginx.Dockerfile │ └── groovy │ └── se │ └── transmode │ └── gradle │ └── plugins │ └── docker │ ├── ExampleProjects.groovy │ └── CreateDockerfileTest.groovy ├── examples ├── application │ ├── settings.gradle │ ├── Dockerfile.expected │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── README.md │ ├── build.gradle │ ├── src │ │ └── main │ │ │ └── java │ │ │ └── se │ │ │ └── transmode │ │ │ └── example │ │ │ └── docker │ │ │ └── JettyMain.java │ ├── gradlew.bat │ └── gradlew ├── spark │ ├── settings.gradle │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── src │ │ └── main │ │ │ └── java │ │ │ └── se │ │ │ └── transmode │ │ │ └── example │ │ │ └── docker │ │ │ └── SparkStart.java │ ├── build.gradle │ ├── README.md │ ├── gradlew.bat │ └── gradlew └── simple │ ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties │ ├── Dockerfile.expected │ ├── README.md │ ├── input │ └── README.md │ ├── build.gradle │ ├── gradlew.bat │ └── gradlew ├── gradle ├── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── idea.gradle ├── buildscript.gradle └── publish.gradle ├── .drone.yml ├── .gitignore ├── gradle.properties ├── .travis.yml ├── codequality └── HEADER ├── TODO.md ├── .idea └── runConfigurations │ └── allTests.xml ├── gradlew.bat ├── CHANGELOG.md ├── gradlew ├── README.md └── LICENSE /src/test/resources/testTargetDir/fileA.txt: -------------------------------------------------------------------------------- 1 | This is a test file. -------------------------------------------------------------------------------- /src/test/resources/testTargetDir/fileB.txt: -------------------------------------------------------------------------------- 1 | This is a test file. -------------------------------------------------------------------------------- /examples/application/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'docker-jetty' 2 | -------------------------------------------------------------------------------- /examples/spark/settings.gradle: -------------------------------------------------------------------------------- 1 | // Docker needs this to be all-lowercase 2 | rootProject.name = "docker-spark" 3 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Transmode/gradle-docker/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /examples/simple/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Transmode/gradle-docker/HEAD/examples/simple/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /examples/spark/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Transmode/gradle-docker/HEAD/examples/spark/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /examples/application/Dockerfile.expected: -------------------------------------------------------------------------------- 1 | FROM openjdk:7-jre 2 | EXPOSE 8080 3 | ADD docker-jetty-1.0.tar / 4 | ENTRYPOINT ["/docker-jetty/bin/docker-jetty"] 5 | -------------------------------------------------------------------------------- /.drone.yml: -------------------------------------------------------------------------------- 1 | image: openjdk7 2 | script: 3 | - ./gradlew assemble 4 | - ./gradlew check 5 | notify: 6 | email: 7 | recipients: 8 | - {{notifEmail}} 9 | -------------------------------------------------------------------------------- /examples/application/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Transmode/gradle-docker/HEAD/examples/application/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/idea.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'idea' 2 | 3 | idea { 4 | module { 5 | testSourceDirs += file('src/integTest/groovy') 6 | testSourceDirs += file('src/integTest/resources') 7 | } 8 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle/ 2 | build/ 3 | 4 | .idea/* 5 | !.idea/runConfigurations/ 6 | gradle-docker.iml 7 | gradle-docker.ipr 8 | gradle-docker.iws 9 | out 10 | 11 | *~ 12 | 13 | .classpath 14 | .project 15 | .settings/ 16 | bin/ 17 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.3-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /examples/simple/Dockerfile.expected: -------------------------------------------------------------------------------- 1 | FROM debian:jessie 2 | RUN echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.list 3 | RUN apt-get update 4 | RUN apt-get install -y nginx 5 | ADD add_4.tar / 6 | MAINTAINER John Doe 7 | -------------------------------------------------------------------------------- /examples/simple/README.md: -------------------------------------------------------------------------------- 1 | # Gradle Docker Plugin Example 2 | 3 | This example is intended as an simple illustration on how to build docker images with the gradle-docker plugin. 4 | 5 | ## Requirements 6 | - Docker 0.11+ 7 | 8 | ## Instructions 9 | 10 | ./gradlew nginxDocker 11 | 12 | -------------------------------------------------------------------------------- /examples/simple/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Feb 16 22:55:03 CET 2016 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip 7 | -------------------------------------------------------------------------------- /gradle/buildscript.gradle: -------------------------------------------------------------------------------- 1 | 2 | repositories { 3 | maven { 4 | url = "http://jcenter.bintray.com" 5 | } 6 | } 7 | 8 | dependencies { 9 | classpath 'nl.javadude.gradle.plugins:license-gradle-plugin:0.10.0' 10 | classpath 'org.jfrog.buildinfo:build-info-extractor-gradle:4.7.5' 11 | } 12 | -------------------------------------------------------------------------------- /examples/application/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Feb 16 22:55:03 CET 2016 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip 7 | -------------------------------------------------------------------------------- /examples/spark/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Apr 22 18:09:42 CEST 2016 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.12-bin.zip 7 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | group=se.transmode.gradle 2 | version=1.3-SNAPSHOT 3 | 4 | # use gradle daemon by default 5 | org.gradle.daemon = true 6 | 7 | # ArtifactoryOSS / Bintray 8 | artifactoryUrl = https://oss.jfrog.org/artifactory 9 | artifactoryRepo = oss-snapshot-local 10 | bintrayUser = myUser 11 | bintrayKey = myKey 12 | -------------------------------------------------------------------------------- /examples/spark/src/main/java/se/transmode/example/docker/SparkStart.java: -------------------------------------------------------------------------------- 1 | package se.transmode.example.docker; 2 | import static spark.Spark.get; 3 | 4 | public class SparkStart { 5 | public static void main(String... args) { 6 | get("/", (request, response) -> "

Hello Docker, I'm Spark!

"); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/main/resources/nginx.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu 2 | MAINTAINER Guillaume J. Charmes "guillaume@dotcloud.com" 3 | RUN echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.list 4 | RUN apt-get update 5 | RUN apt-get install -y inotify-tools nginx apache2 openssh-server 6 | COPY file1 / 7 | VOLUME /home/docker /tmp 8 | CMD ["/bin/bash"] 9 | -------------------------------------------------------------------------------- /src/integTest/resources/nginx.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu 2 | MAINTAINER Guillaume J. Charmes "guillaume@dotcloud.com" 3 | LABEL "maintainer"="Guillaume J. Charmes guillaume@dotcloud.com" "version"="1.2.3" 4 | RUN echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.list 5 | RUN apt-get update 6 | RUN apt-get install -y inotify-tools nginx apache2 openssh-server 7 | COPY file1 / 8 | VOLUME /home/docker /tmp 9 | CMD ["/bin/bash"] 10 | -------------------------------------------------------------------------------- /examples/simple/input/README.md: -------------------------------------------------------------------------------- 1 | # Lorem Ipsum 2 | 3 | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. 4 | 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: precise 2 | sudo: required 3 | language: java 4 | jdk: 5 | - openjdk7 6 | addons: 7 | hostname: fake-hostname 8 | script: 9 | - ./gradlew check 10 | - ./gradlew jacocoTestReport 11 | before_cache: 12 | - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock 13 | - rm -fr $HOME/.gradle/caches/*/plugin-resolution/ 14 | cache: 15 | directories: 16 | - $HOME/.gradle/caches/ 17 | - $HOME/.gradle/wrapper/ 18 | after_success: 19 | - bash <(curl -s https://codecov.io/bash) 20 | -------------------------------------------------------------------------------- /examples/application/README.md: -------------------------------------------------------------------------------- 1 | # Gradle Docker Plugin Example 2 | 3 | This example is intended as an illustration on how to build runnable docker images with the gradle-docker plugin in conjunction with Gradle Java and Application plugins. 4 | 5 | ## Example Application 6 | The source code in this example consists of a simple main method that starts a jetty webserver serving static html. 7 | 8 | ## Requirements 9 | - JDK 1.7 10 | - Docker 0.6+ 11 | 12 | ## Instructions 13 | 14 | ./gradlew distDocker 15 | 16 | -------------------------------------------------------------------------------- /codequality/HEADER: -------------------------------------------------------------------------------- 1 | Copyright ${year} ${name} 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/gradle-plugins/docker.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2014 Transmode AB 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 | implementation-class=se.transmode.gradle.plugins.docker.DockerPlugin -------------------------------------------------------------------------------- /src/main/resources/META-INF/gradle-plugins/se.transmode.docker.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2014 Transmode AB 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 | implementation-class=se.transmode.gradle.plugins.docker.DockerPlugin -------------------------------------------------------------------------------- /examples/spark/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | maven { 4 | url = "http://jcenter.bintray.com" 5 | } 6 | } 7 | dependencies { 8 | classpath "se.transmode.gradle:gradle-docker:1.2" 9 | } 10 | } 11 | 12 | apply plugin: "java" 13 | apply plugin: "application" 14 | apply plugin: "docker" 15 | 16 | task wrapper(type: Wrapper) { 17 | gradleVersion = "2.12" 18 | } 19 | 20 | sourceCompatibility = 1.8 21 | group = "se.transmode" 22 | version = "0.1" 23 | mainClassName = "se.transmode.example.docker.SparkStart" 24 | 25 | repositories { 26 | maven { 27 | url = "http://jcenter.bintray.com" 28 | } 29 | } 30 | dependencies { 31 | compile "com.sparkjava:spark-core:2.3" 32 | } 33 | 34 | distDocker { 35 | exposePort 4567 36 | } 37 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | # TODO 2 | 3 | ## Plugin 4 | * Split plugin into a base- and a convention-plugin ("stacked plugin"): 5 | - docker-base adds task types DockerBuild, DockerRun, DockerPublish 6 | - docker imports docker-base and creates convention tasks such as distDocker 7 | (if application plugin used), replaces run with a DockerRun tasks, etc. 8 | * Rename plugin to be accepted by gradle plugin page (e.g. se.transmode.docker) 9 | see: http://plugins.gradle.org/submit 10 | 11 | ## Convention plugin 12 | * Check for applicationDistribution copySpec to determine if the project is an application. 13 | 14 | ## Base plugin 15 | * addFiles should accept copySpec 16 | 17 | ## Docker 18 | * Use the Java API client for Docker (https://github.com/docker-java/docker-java) 19 | - Currently docker-java does not support connecting to the docker host through UNIX sockets 20 | (see https://github.com/docker-java/docker-java#support-for-unix-sockets) 21 | -------------------------------------------------------------------------------- /examples/application/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | apply plugin: 'application' 3 | apply plugin: 'docker' 4 | 5 | buildscript { 6 | repositories { 7 | mavenLocal() 8 | maven { 9 | url = "http://jcenter.bintray.com" 10 | } 11 | } 12 | dependencies { 13 | classpath 'se.transmode.gradle:gradle-docker:1.3-SNAPSHOT' 14 | } 15 | } 16 | 17 | task wrapper(type: Wrapper) { 18 | gradleVersion = '2.10' 19 | } 20 | 21 | sourceCompatibility = 1.7 22 | group = 'example' 23 | version = '1.0' 24 | mainClassName = 'se.transmode.example.docker.JettyMain' 25 | 26 | repositories { 27 | maven { 28 | url = "http://jcenter.bintray.com" 29 | } 30 | } 31 | dependencies { 32 | compile 'org.eclipse.jetty.aggregate:jetty-all:9.0.6.v20130930' 33 | } 34 | docker.baseImage = 'openjdk:7-jre' 35 | distDocker { 36 | dockerfile { 37 | expose 8080 38 | } 39 | dryRun = true 40 | } 41 | -------------------------------------------------------------------------------- /src/main/groovy/se/transmode/gradle/plugins/docker/DockerPluginExtension.groovy: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2014 Transmode AB 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 | package se.transmode.gradle.plugins.docker 17 | 18 | class DockerPluginExtension { 19 | String maintainer 20 | String baseImage 21 | String registry 22 | 23 | // path to the docker binary 24 | String dockerBinary 25 | 26 | // use docker REST api (with docker-java) 27 | Boolean useApi 28 | 29 | // docker host url & credentials 30 | String hostUrl 31 | String apiUsername 32 | String apiEmail 33 | String apiPassword 34 | } 35 | -------------------------------------------------------------------------------- /examples/simple/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'docker' 2 | 3 | buildscript { 4 | repositories { mavenLocal(); 5 | maven { 6 | url = "http://jcenter.bintray.com" 7 | } 8 | } 9 | dependencies { 10 | classpath 'se.transmode.gradle:gradle-docker:1.3-SNAPSHOT' 11 | } 12 | } 13 | 14 | task wrapper(type: Wrapper) { 15 | gradleVersion = '2.10' 16 | } 17 | 18 | group = 'org.acme' 19 | 20 | docker { 21 | baseImage = 'debian:jessie' 22 | maintainer = 'John Doe ' 23 | } 24 | 25 | task docker(type: Docker) { 26 | // The default image tag contains the applicationName 27 | applicationName = 'nginx' 28 | 29 | dockerfile { 30 | run 'echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.list' 31 | run 'apt-get update' 32 | run 'apt-get install -y nginx' 33 | 34 | /* The 'add' instruction accepts a Gradle copySpec closure 35 | The source files are added to a temporary tar archive which 36 | is then added to the image using the Dockerfile 'ADD' instruction. */ 37 | add { 38 | from 'input' 39 | into 'docs' 40 | } 41 | } 42 | dryRun = true 43 | } 44 | -------------------------------------------------------------------------------- /.idea/runConfigurations/allTests.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 10 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /examples/application/src/main/java/se/transmode/example/docker/JettyMain.java: -------------------------------------------------------------------------------- 1 | package se.transmode.example.docker; 2 | 3 | import javax.servlet.http.HttpServletRequest; 4 | import javax.servlet.http.HttpServletResponse; 5 | import javax.servlet.ServletException; 6 | 7 | import java.io.IOException; 8 | 9 | import org.eclipse.jetty.server.Server; 10 | import org.eclipse.jetty.server.Request; 11 | import org.eclipse.jetty.server.handler.AbstractHandler; 12 | 13 | public class JettyMain extends AbstractHandler 14 | { 15 | public void handle(String target, 16 | Request baseRequest, 17 | HttpServletRequest request, 18 | HttpServletResponse response) 19 | throws IOException, ServletException 20 | { 21 | response.setContentType("text/html;charset=utf-8"); 22 | response.setStatus(HttpServletResponse.SC_OK); 23 | baseRequest.setHandled(true); 24 | response.getWriter().println("

"); 25 | response.getWriter().println("

Hello, Docker!

"); 26 | } 27 | 28 | public static void main(String[] args) throws Exception 29 | { 30 | Server server = new Server(8080); 31 | server.setHandler(new JettyMain()); 32 | 33 | server.start(); 34 | server.join(); 35 | } 36 | } 37 | 38 | -------------------------------------------------------------------------------- /src/integTest/groovy/se/transmode/gradle/plugins/docker/ExampleProjects.groovy: -------------------------------------------------------------------------------- 1 | package se.transmode.gradle.plugins.docker 2 | 3 | import groovy.util.logging.Log 4 | import org.gradle.tooling.GradleConnector 5 | import org.gradle.tooling.ProjectConnection 6 | import org.junit.Test 7 | 8 | import static org.hamcrest.Matchers.equalTo 9 | import static org.hamcrest.Matchers.is 10 | import static org.junit.Assert.assertThat 11 | 12 | @Log 13 | class ExampleProjects { 14 | 15 | @Test 16 | public void simpleExample() { 17 | def projectDir = new File('examples/simple') 18 | def expectedDockerfile = new File(projectDir, 'Dockerfile.expected').text 19 | 20 | def actualDockerfile = runGradleTask(projectDir, 'docker') 21 | 22 | assertThat actualDockerfile, is(equalTo(expectedDockerfile)) 23 | } 24 | 25 | @Test 26 | public void applicationExample() { 27 | def projectDir = new File('examples/application') 28 | def expectedDockerfile = new File(projectDir, 'Dockerfile.expected').text 29 | 30 | def actualDockerfile = runGradleTask(projectDir, 'distDocker') 31 | 32 | assertThat actualDockerfile, is(equalTo(expectedDockerfile)) 33 | } 34 | 35 | private String runGradleTask(File projectDir, String taskName) { 36 | ProjectConnection connection = GradleConnector.newConnector() 37 | .forProjectDirectory(projectDir) 38 | .connect(); 39 | try { 40 | connection.newBuild() 41 | .forTasks(taskName) 42 | .run(); 43 | } finally { 44 | connection.close(); 45 | } 46 | return new File(projectDir, 'build/docker/Dockerfile').text 47 | } 48 | } 49 | 50 | -------------------------------------------------------------------------------- /src/main/java/se/transmode/gradle/plugins/docker/JavaBaseImage.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2014 Transmode AB 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 | package se.transmode.gradle.plugins.docker; 17 | 18 | import org.gradle.api.JavaVersion; 19 | 20 | /** 21 | * @author Matthias Grüter, matthias.gruter@transmode.com 22 | */ 23 | public enum JavaBaseImage { 24 | JAVA6("openjdk:6-jre", JavaVersion.VERSION_1_6), 25 | JAVA7("openjdk:7-jre", JavaVersion.VERSION_1_7), 26 | JAVA8("openjdk:8-jre", JavaVersion.VERSION_1_8), 27 | JAVA9("openjdk:9-jre", JavaVersion.VERSION_1_9), 28 | JAVA10("openjdk:10-jre", JavaVersion.VERSION_1_10), 29 | JAVA11("openjdk:11-jre", JavaVersion.VERSION_11); 30 | 31 | final String imageName; 32 | final JavaVersion target; 33 | 34 | JavaBaseImage(String imageName, JavaVersion target) { 35 | this.imageName = imageName; 36 | this.target = target; 37 | } 38 | 39 | public static JavaBaseImage imageFor(JavaVersion target) { 40 | for(JavaBaseImage image: JavaBaseImage.values()) { 41 | if(image.target == target) { 42 | return image; 43 | } 44 | } 45 | throw new IllegalArgumentException("No Java base image for target " + target + " found."); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /examples/spark/README.md: -------------------------------------------------------------------------------- 1 | # Gradle Docker Plugin Example 2 | 3 | This example is intended as an illustration on how to build runnable docker images with the gradle-docker plugin in conjunction with Gradle [Java](https://docs.gradle.org/current/userguide/java_plugin.html) and [Application](https://docs.gradle.org/current/userguide/application_plugin.html) plugins. 4 | 5 | ## Example Application 6 | The source code in this example consists of a simple main method that starts a Jetty web server using [Spark](http://sparkjava.com/), serving static HTML. 7 | 8 | ## Requirements 9 | - JDK 1.8 10 | - Docker 0.6+ 11 | 12 | ## Instructions 13 | ### 1. Create Docker Image 14 | ./gradlew distDocker 15 | 16 | ### 2. Run Image 17 | Provide a port mapping. You will be able to access the website from your host via port 8080. Spark's default port is 4567. 18 | 19 | docker run -p 8080:4567 se.transmode/docker-spark:0.1 20 | 21 | ### 3. Connect to Website From Host 22 | If you are on Windows or OS X, get the name of your Docker machine: 23 | 24 | docker-machine ls 25 | 26 | Let's say your machine is called `my-default`, get the IP address of your machine: 27 | 28 | docker-machine ip my-default 29 | 30 | Assuming the IP of the docker machine is `192.168.99.100`, point your host's browser to 31 | 32 | 192.168.99.100:8080 33 | 34 | ## Troubleshooting 35 | Restart your Docker machine with `docker-machine restart my-default` and then reinitialize Docker with `eval "$(docker-machine env my-default)"` if you get the following error: 36 | 37 | An error occurred trying to connect: Post http://%2F%2F.%2Fpipe%2Fdocker_engine/v1.23/build?buildargs=%7B%7D&cgroupparent=&cpuperiod=0&cpuquota=0&cpusetcpus=&cpusetmems=&cpushares=0&dockerfile=Dockerfile&labels=%7B%7D&memory=0&memswap=0&rm=1&shmsize=0&t=example%2Fappname%3A1.0&ulimits=null: open //./pipe/docker_engine: The system cannot find the file specified. 38 | 39 | -------------------------------------------------------------------------------- /src/test/groovy/se/transmode/gradle/plugins/docker/DockerPluginTest.groovy: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2014 Transmode AB 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 | package se.transmode.gradle.plugins.docker 17 | import org.gradle.api.Project 18 | import org.gradle.testfixtures.ProjectBuilder 19 | import org.junit.Test 20 | 21 | import static org.hamcrest.Matchers.equalTo 22 | import static org.junit.Assert.assertThat 23 | import static org.junit.Assert.assertTrue 24 | 25 | class DockerPluginTest { 26 | 27 | private Project createProjectAndApplyPlugin() { 28 | Project project = ProjectBuilder.builder().build() 29 | project.apply plugin: 'docker' 30 | return project 31 | } 32 | 33 | @Test 34 | public void pluginAddsExtensionToProject() { 35 | Project project = createProjectAndApplyPlugin() 36 | 37 | assertTrue(project.docker instanceof DockerPluginExtension) 38 | } 39 | 40 | @Test 41 | public void taskTypeIsAvailableInPluginNamespace() { 42 | Project project = createProjectAndApplyPlugin() 43 | 44 | assertTrue(project.Docker == DockerTask.class) 45 | } 46 | 47 | @Test 48 | public void pluginInjectsTaskMaintainerFromExtension() { 49 | Project project = createProjectAndApplyPlugin() 50 | def testMaintainer = "PluginTest Maintainer" 51 | project.docker.maintainer = testMaintainer 52 | 53 | def task = project.task('docker', type: DockerTask) 54 | 55 | assertThat task.maintainer, equalTo(testMaintainer) 56 | } 57 | } 58 | 59 | -------------------------------------------------------------------------------- /src/main/groovy/se/transmode/gradle/plugins/docker/DockerRunTask.groovy: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2014 Transmode AB 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 | package se.transmode.gradle.plugins.docker 17 | 18 | import org.gradle.api.DefaultTask 19 | import org.gradle.api.logging.Logger 20 | import org.gradle.api.logging.Logging 21 | import org.gradle.api.tasks.TaskAction; 22 | 23 | import se.transmode.gradle.plugins.docker.client.DockerClient 24 | 25 | class DockerRunTask extends DockerTaskBase { 26 | private static Logger logger = Logging.getLogger(DockerRunTask) 27 | 28 | String containerName 29 | 30 | boolean detached = false 31 | 32 | boolean autoRemove = false 33 | 34 | Map env 35 | 36 | Map ports 37 | 38 | Map volumes 39 | 40 | List volumesFrom 41 | 42 | List links 43 | 44 | DockerRunTask() { 45 | env = [:] 46 | ports = [:] 47 | volumes = [:] 48 | volumesFrom = [] 49 | links = [] 50 | } 51 | 52 | @TaskAction 53 | public void run() { 54 | DockerClient client = getClient() 55 | client.run(getImageTag(), getContainerName(), getDetached(), getAutoRemove(), getEnv(), 56 | getPorts(), getVolumes(), getVolumesFrom(), getLinks()) 57 | } 58 | 59 | void env(String key, String value) { 60 | env.put(key, value) 61 | } 62 | 63 | void publish(String host, String container) { 64 | ports.put(host, container) 65 | } 66 | 67 | void volume(String host, String container) { 68 | volumes.put(host, container) 69 | } 70 | 71 | void volumesFrom(String containerName) { 72 | volumesFrom.add(containerName) 73 | } 74 | 75 | void link(String containerName) { 76 | links.add(containerName) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /src/main/java/se/transmode/gradle/plugins/docker/client/DockerClient.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2014 Transmode AB 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 | package se.transmode.gradle.plugins.docker.client; 17 | 18 | import java.io.File; 19 | import java.util.List; 20 | import java.util.Map; 21 | 22 | public interface DockerClient { 23 | /** 24 | * Build a Docker image from the contents of the given directory. 25 | * 26 | * @param buildDir the directory from which to build the image 27 | * @param tag the tag to apply to the image 28 | * @param pull wether to pull latest image or not, true enables the pull, false disables pull 29 | * @return the output of the command 30 | */ 31 | public String buildImage(File buildDir, String tag, boolean pull); 32 | 33 | /** 34 | * Push the given image to the configured Docker registry. 35 | * 36 | * @param tag the tag of the image to push 37 | * @return the output of the command 38 | */ 39 | public String pushImage(String tag); 40 | 41 | /** 42 | * Run the given image in a container with the given name. 43 | * 44 | * @param tag the image to run 45 | * @param containerName the name of the container to create 46 | * @param detached should the container be run in the background (aka detached) 47 | * @param autoRemove should the container be removed when execution completes 48 | * @param env a map containing a collection of environment variables to set 49 | * @param ports a map containing the ports to publish 50 | * @param volumes a map containing the volumes to bind 51 | * @param volumesFrom a list of the containers whose volumes we should mount 52 | * @param links a list of the containers to which the newly created container should be linked 53 | * @return the output of the command 54 | */ 55 | public String run(String tag, String containerName, boolean detached, boolean autoRemove, 56 | Map env, Map ports, Map volumes, 57 | List volumesFrom, List links); 58 | } 59 | -------------------------------------------------------------------------------- /examples/simple/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /examples/application/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /examples/spark/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /gradle/publish.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'maven-publish' 2 | apply plugin: 'com.jfrog.artifactory' 3 | 4 | publishing { 5 | publications { 6 | maven(MavenPublication) { 7 | from components.java 8 | artifact sourcesJar 9 | artifact javadocJar 10 | 11 | pom.withXml { 12 | asNode().with { 13 | 14 | appendNode('name', 'gradle-docker') 15 | appendNode('description', 'Gradle plugin to build und publish Docker images from the build script.') 16 | appendNode('url', 'https://github.com/Transmode/gradle-docker') 17 | appendNode('inceptionYear', '2013') 18 | 19 | appendNode('licenses').appendNode('license').with { 20 | appendNode('name', 'The Apache Software License, Version 2.0') 21 | appendNode('url', 'http://www.apache.org/licenses/LICENSE-2.0.txt') 22 | appendNode('distribution', 'repo') 23 | } 24 | 25 | appendNode('developers').appendNode('developer').with { 26 | appendNode('id', 'mattgruter') 27 | appendNode('name', 'Matthias Grüter') 28 | appendNode('email', 'matthias.gruter@transmode.com') 29 | appendNode('roles').appendNode('role', 'Developer') 30 | 31 | } 32 | 33 | appendNode('issueManagement').with { 34 | appendNode('system', 'github') 35 | appendNode('url', 'https://github.com/Transmode/gradle-docker/issues') 36 | } 37 | 38 | appendNode('scm').with { 39 | appendNode('connection', 'scm:git@github.com/Transmode/gradle-docker.git') 40 | appendNode('developerConnection', 'scm:git@github.com/Transmode/gradle-docker.git') 41 | appendNode('url', 'scm:git://github.com/Transmode/gradle-docker.git') 42 | } 43 | } 44 | } 45 | } 46 | } 47 | 48 | repositories { 49 | maven { 50 | url 'https://api.bintray.com/maven/transmode/gradle-plugins/gradle-docker' 51 | credentials { 52 | username = bintrayUser 53 | password = bintrayKey 54 | } 55 | } 56 | } 57 | } 58 | 59 | artifactory { 60 | contextUrl = artifactoryUrl 61 | resolve { 62 | repoKey = "repo" 63 | } 64 | publish { 65 | repository { 66 | repoKey = artifactoryRepo 67 | username = bintrayUser 68 | password = bintrayKey 69 | } 70 | defaults { 71 | publications ('maven') 72 | } 73 | } 74 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## Version 1.2 (2014-07-28) 4 | 5 | #### Docker Remote API 6 | It is now possible to use the Docker Remote API instead of the `docker` command line tool. See the [docs](README.md#docker-remote-api) for more information (PR #10). This is particularly useful for users who do not have Docker installed locally. 7 | 8 | #### addFile 9 | * Fixed `addFile` accepting a copySpec as an argument (issue #4). 10 | * `addFile` now accepts the destination path as an optional second argument (default: `/`) 11 | 12 | #### Base image 13 | * Fixed setting of a custom base image both through the plugin extension or a task property (issue #11). 14 | * Fixed default base image detection based on project's `targetCompatibility`. 15 | * Added default base image for Java 8 (PR #9). 16 | 17 | #### External Dockerfile 18 | * Supply an external Dockerfile instead of defining it in the build script. See the [docs](README.md#building-your-dockerfile) (issue #13). 19 | * Mix and match loading external Dockerfiles and extending in the build script. 20 | 21 | #### Gradle 2.0 22 | The plugin is now compatible with Gradle 2.0 (see the [docs](README.md#note-to-gradle-1.x-users) if you are using Gradle 1.x) 23 | 24 | #### Image tagging 25 | * Possible to set docker image tag version to something else than *:latest* (PR #5). 26 | * Fixed setting of the image tag name and version (issue #15). 27 | 28 | Many thanks to the contributors 29 | 30 | * [@aglover](https://github.com/aglover) 31 | * [@Teudimundo](https://github.com/Teudimundo) 32 | * [@sfitts](https://github.com/sfitts) 33 | * [@frvi](https://github.com/frvi) 34 | * [@mattgruter](https://github.com/mattgruter) 35 | 36 | 37 | ## Version 1.1.1 (2014-06-13) 38 | * Possible to build without specifying group. 39 | * Failing gradle build if Docker execution fails. 40 | 41 | Many thanks to the contributors: 42 | 43 | * [@Teudimundo](https://github.com/Teudimundo) 44 | * [@frvi](https://github.com/frvi) 45 | 46 | 47 | ## Version 1.1 (2014-04-11) 48 | * Support for more Dockerfile commands: 49 | - WORKDIR 50 | - VOLUME 51 | - ENV 52 | * Add any raw Dockerfile command. 53 | * Set path to docker binary per task or globally. 54 | * Switched to official dockerfile/java base image as default. 55 | * Fixed path seperator bug for integration testing on Windows. 56 | 57 | Many thanks to the contributors: 58 | 59 | * [@sfitts](https://github.com/sfitts) 60 | * [@kernel164](https://github.com/kernel164) 61 | * [@nicarlsson](https://github.com/nicarlsson) 62 | * [@mattgruter](https://github.com/mattgruter) 63 | 64 | 65 | ## Version 1.0 (2013-12-16) 66 | Initial public release 67 | * Task type "Docker" to: 68 | - create Dockerfiles 69 | - build a Docker image from above Dockerfile 70 | - push the Docker image to the privat or public index 71 | * Convention task "distDocker" to build an image from the gradle applicationDistribution 72 | * Plugin extension to apply configuration to all Docker tasks 73 | -------------------------------------------------------------------------------- /src/integTest/groovy/se/transmode/gradle/plugins/docker/CreateDockerfileTest.groovy: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2014 Transmode AB 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 | package se.transmode.gradle.plugins.docker 17 | import org.gradle.api.Project 18 | import org.gradle.testfixtures.ProjectBuilder 19 | import org.junit.Test 20 | 21 | import static org.hamcrest.Matchers.* 22 | import static org.hamcrest.Matchers.containsInAnyOrder 23 | import static org.junit.Assert.assertThat 24 | 25 | 26 | class CreateDockerfileTest { 27 | @Test 28 | public void compareDockerfileBuiltOldstyle() { 29 | Project project = ProjectBuilder.builder().build() 30 | project.apply plugin: 'docker' 31 | 32 | def task = project.task('docker', type: DockerTask) 33 | 34 | // don't actually execute docker, just build Dockerfile 35 | task.dockerBinary = '/bin/true' 36 | 37 | // example pulled from "http://docs.docker.io/en/latest/use/builder/#dockerfile-examples" 38 | task.baseImage 'ubuntu' 39 | task.maintainer 'Guillaume J. Charmes "guillaume@dotcloud.com"' 40 | task.label (maintainer:'Guillaume J. Charmes guillaume@dotcloud.com', version:'1.2.3') 41 | 42 | task.runCommand 'echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.list' 43 | task.runCommand 'apt-get update' 44 | 45 | task.runCommand 'apt-get install -y inotify-tools nginx apache2 openssh-server' 46 | 47 | task.addInstruction "COPY", "tmp/file1" 48 | 49 | task.volume "/home/docker", "/tmp" 50 | 51 | task.defaultCommand(["/bin/bash"]) 52 | 53 | def expected = [] 54 | this.getClass().getClassLoader().getResource("nginx.Dockerfile").eachLine { expected << it.toString() } 55 | def actual = task.buildDockerfile().instructions.each { it.toString() } 56 | 57 | assertThat actual[0].toString(), equalToIgnoringCase('from ubuntu') 58 | assertThat actual, containsInAnyOrder(*expected) 59 | } 60 | 61 | @Test 62 | public void compareDockerfileBuiltWithDSL() { 63 | Project project = ProjectBuilder.builder().build() 64 | project.apply plugin: 'docker' 65 | 66 | def task = project.task('docker', type: DockerTask) 67 | 68 | // don't actually execute docker, just build Dockerfile 69 | task.dockerBinary = "/bin/true" 70 | 71 | task.maintainer 'Guillaume J. Charmes "guillaume@dotcloud.com"' 72 | task.dockerfile { 73 | from 'ubuntu' 74 | label (maintainer:"Guillaume J. Charmes guillaume@dotcloud.com", version:"1.2.3") 75 | run 'echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.list' 76 | run 'apt-get update' 77 | run 'apt-get install -y inotify-tools nginx apache2 openssh-server' 78 | copy "tmp/file1" 79 | volume "/home/docker", "/tmp" 80 | cmd(['/bin/bash']) 81 | } 82 | 83 | def expected = [] 84 | this.getClass().getClassLoader().getResource("nginx.Dockerfile").eachLine { expected << it.toString() } 85 | def actual = task.buildDockerfile().instructions.each { it.toString() } 86 | 87 | assertThat actual[0], equalToIgnoringCase('from ubuntu') 88 | assertThat actual, containsInAnyOrder(*expected) 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/main/groovy/se/transmode/gradle/plugins/docker/DockerTaskBase.groovy: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2014 Transmode AB 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 | package se.transmode.gradle.plugins.docker 17 | 18 | import org.gradle.api.DefaultTask 19 | import org.gradle.api.logging.Logger 20 | 21 | import com.google.common.annotations.VisibleForTesting; 22 | 23 | import se.transmode.gradle.plugins.docker.client.DockerClient 24 | import se.transmode.gradle.plugins.docker.client.JavaDockerClient 25 | import se.transmode.gradle.plugins.docker.client.NativeDockerClient 26 | 27 | abstract class DockerTaskBase extends DefaultTask { 28 | 29 | @VisibleForTesting 30 | static final String LATEST_VERSION = 'latest' 31 | 32 | // Name of the application being wrapped into a docker image (default: project.name) 33 | String applicationName 34 | // What to tag the created docker image with (default: group/applicationName) 35 | String tag 36 | // Which version to use along with the tag (default: latest) 37 | String tagVersion 38 | // Hostname, port of the docker image registry unless Docker index is used 39 | String registry 40 | 41 | // Should we use Docker's remote API instead of the docker executable 42 | Boolean useApi 43 | 44 | // Full path to the docker executable 45 | String dockerBinary 46 | 47 | // URL of the remote Docker host (default: localhost) 48 | String hostUrl 49 | 50 | // Docker remote API credentials 51 | String apiUsername 52 | String apiPassword 53 | String apiEmail 54 | 55 | DockerTaskBase() { 56 | applicationName = project.name 57 | } 58 | 59 | void setTagVersion(String version) { 60 | tagVersion = version; 61 | } 62 | 63 | void setTagVersionToLatest() { 64 | tagVersion = LATEST_VERSION; 65 | } 66 | 67 | protected String getImageTag() { 68 | String tag 69 | tag = this.tag ?: getDefaultImageTag() 70 | return appendImageTagVersion(tag) 71 | } 72 | 73 | private String getDefaultImageTag() { 74 | String tag 75 | if (registry) { 76 | def group = project.group ? "${project.group}/" : '' 77 | tag = "${-> registry}/${group}${-> applicationName}" 78 | } else if (project.group) { 79 | tag = "${-> project.group}/${-> applicationName}" 80 | } else { 81 | tag = "${-> applicationName}" 82 | } 83 | return tag 84 | } 85 | 86 | private String appendImageTagVersion(String tag) { 87 | def version = tagVersion ?: project.version 88 | if(version == 'unspecified') { 89 | version = LATEST_VERSION 90 | } 91 | return "${tag}:${version}" 92 | 93 | } 94 | 95 | protected DockerClient getClient() { 96 | DockerClient client 97 | if(getUseApi()) { 98 | logger.info("Using the Docker remote API.") 99 | client = JavaDockerClient.create( 100 | getHostUrl(), 101 | getApiUsername(), 102 | getApiPassword(), 103 | getApiEmail()) 104 | } else { 105 | logger.info("Using the native docker binary.") 106 | client = new NativeDockerClient(getDockerBinary()) 107 | } 108 | return client 109 | } 110 | 111 | } 112 | -------------------------------------------------------------------------------- /src/main/groovy/se/transmode/gradle/plugins/docker/client/NativeDockerClient.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Transmode AB 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 | package se.transmode.gradle.plugins.docker.client 17 | import com.google.common.base.Preconditions 18 | import org.gradle.api.GradleException 19 | 20 | class NativeDockerClient implements DockerClient { 21 | 22 | private final String binary; 23 | 24 | NativeDockerClient(String binary) { 25 | Preconditions.checkArgument(binary as Boolean, "Docker binary can not be empty or null.") 26 | this.binary = binary 27 | } 28 | 29 | @Override 30 | String buildImage(File buildDir, String tag, boolean pull) { 31 | Preconditions.checkArgument(tag as Boolean, "Image tag can not be empty or null.") 32 | def cmdLine = [binary, "build", "--pull=${pull}", "-t", tag, buildDir.toString() ] 33 | return executeAndWait(cmdLine) 34 | } 35 | 36 | @Override 37 | String pushImage(String tag) { 38 | Preconditions.checkArgument(tag as Boolean, "Image tag can not be empty or null.") 39 | def cmdLine = [binary, "push", tag] 40 | return executeAndWait(cmdLine) 41 | } 42 | 43 | private static String executeAndWait(List cmdLine) { 44 | def process = cmdLine.execute() 45 | process.waitForProcessOutput(System.out, System.err) 46 | if (process.exitValue()) { 47 | throw new GradleException("Docker execution failed\nCommand line [${cmdLine}]") 48 | } 49 | return "Done" 50 | } 51 | 52 | @Override 53 | String run(String tag, String containerName, boolean detached, boolean autoRemove, 54 | Map env, 55 | Map ports, Map volumes, List volumesFrom, 56 | List links) { 57 | Preconditions.checkArgument(tag as Boolean, "Image tag cannot be empty or null.") 58 | Preconditions.checkArgument(containerName as Boolean, "Image name cannot be empty or null.") 59 | Preconditions.checkArgument(env != null, "Environment map cannot be null.") 60 | Preconditions.checkArgument(ports != null, "Exported port map cannot be null.") 61 | Preconditions.checkArgument(volumes != null, "Volume map cannot be null.") 62 | Preconditions.checkArgument(volumesFrom != null, "Volumes from list cannot be null.") 63 | Preconditions.checkArgument(links != null, "Link list cannot be null.") 64 | Preconditions.checkArgument(!detached || !autoRemove, 65 | "Cannot set both detached and autoRemove options to true."); 66 | 67 | def detachedArg = detached ? '-d' : '' 68 | def removeArg = autoRemove ? '--rm' : '' 69 | def List cmdLine = [binary, "run", detachedArg, removeArg, "--name" , containerName] 70 | cmdLine = appendArguments(cmdLine, env, "--env", '=') 71 | cmdLine = appendArguments(cmdLine, ports, "--publish") 72 | cmdLine = appendArguments(cmdLine, volumes, "--volume") 73 | cmdLine = appendArguments(cmdLine, volumesFrom, "--volumes-from") 74 | cmdLine = appendArguments(cmdLine, links, "--link") 75 | cmdLine.add(tag) 76 | return executeAndWait(cmdLine) 77 | } 78 | 79 | private static List appendArguments(List cmdLine, Map map, String option, 80 | String separator = ':') { 81 | // Add each entry in the map as the indicated argument 82 | map.each { key, value -> 83 | cmdLine.add(option); 84 | cmdLine.add("${key}${separator}${value}") 85 | } 86 | return cmdLine 87 | } 88 | 89 | private static List appendArguments(List cmdLine, List list, String option) { 90 | // Add each entry in the map as the indicated argument 91 | list.each { 92 | cmdLine.add(option); 93 | cmdLine.add(it); 94 | } 95 | return cmdLine 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /src/main/groovy/se/transmode/gradle/plugins/docker/LegacyDockerfileMethods.groovy: -------------------------------------------------------------------------------- 1 | package se.transmode.gradle.plugins.docker 2 | 3 | import groovy.transform.TupleConstructor 4 | import groovy.util.logging.Log 5 | import org.gradle.api.logging.Logger 6 | import org.gradle.api.logging.Logging 7 | import se.transmode.gradle.plugins.docker.image.Dockerfile 8 | 9 | @TupleConstructor 10 | @Log 11 | class LegacyDockerfileMethods implements GroovyInterceptable { 12 | 13 | private static Logger logger = Logging.getLogger(LegacyDockerfileMethods) 14 | Dockerfile dockerfile 15 | 16 | def invokeMethod(String name, args) { 17 | logger.warn('The {} method has been deprecated and is scheduled to be removed. Use the new dockerfile DSL instead.', name) 18 | metaClass.getMetaMethod(name, args).invoke(this, args) 19 | } 20 | 21 | /** 22 | * Set the default command of the Docker image ('CMD' in Dockerfile). Deprecated. 23 | * 24 | * Use the new dockerfile DSL instead: 25 | * dockerfile { 26 | * cmd ['your', 'command'] 27 | * } 28 | * 29 | */ 30 | @Deprecated 31 | void setDefaultCommand(List cmd) { 32 | dockerfile.cmd(cmd) 33 | } 34 | 35 | /** 36 | * Set the default command of the Docker image ('CMD' in Dockerfile). Deprecated. 37 | * 38 | * Use the new dockerfile DSL instead: 39 | * dockerfile { 40 | * cmd ['your', 'command'] 41 | * } 42 | * 43 | */ 44 | @Deprecated 45 | void defaultCommand(List cmd) { 46 | dockerfile.cmd(cmd) 47 | } 48 | 49 | /** 50 | * Set the entry point of the Docker image ('ENTRYPOINT' in Dockerfile. Deprectated. 51 | * 52 | * Use the new dockerfile DSL instead: 53 | * dockerfile { 54 | * entrypoint ['your', 'command'] 55 | * } 56 | * 57 | */ 58 | @Deprecated 59 | void entryPoint(List entryPoint) { 60 | dockerfile.entrypoint(cmd) 61 | } 62 | 63 | /** 64 | * This method is deprecated. Use the new Dockerfile DSL instead: 65 | */ 66 | @Deprecated 67 | void workingDir(String wd) { 68 | dockerfile.workdir(wd) 69 | } 70 | 71 | /** 72 | * This method is deprecated. Use the new Dockerfile DSL instead: 73 | */ 74 | @Deprecated 75 | void runCommand(String command) { 76 | dockerfile.run(command) 77 | } 78 | 79 | /** 80 | * This method is deprecated. Use the new Dockerfile DSL instead: 81 | */ 82 | @Deprecated 83 | void exposePort(Integer... ports) { 84 | dockerfile.expose(*ports) 85 | } 86 | 87 | @Deprecated 88 | void exposePort(String port) { 89 | dockerfile.expose(port) 90 | } 91 | 92 | /** 93 | * This method is deprecated. Use the new Dockerfile DSL instead: 94 | */ 95 | @Deprecated 96 | void switchUser(String userName) { 97 | dockerfile.user(userName) 98 | } 99 | 100 | /** 101 | * This method is deprecated. Use the new Dockerfile DSL instead: 102 | */ 103 | @Deprecated 104 | void setEnvironment(String key, String value) { 105 | dockerfile.env(key, value) 106 | } 107 | 108 | /** 109 | * This method is deprecated. Use the new Dockerfile DSL instead: 110 | */ 111 | @Deprecated 112 | void addInstruction(String cmd, String value) { 113 | dockerfile."${cmd}"(value) 114 | } 115 | 116 | /** 117 | * This method is deprecated. Use the new Dockerfile DSL instead: 118 | */ 119 | @Deprecated 120 | void volume(String... paths) { 121 | dockerfile.volume(*paths) 122 | } 123 | 124 | /** 125 | * This method is deprecated. Use the new Dockerfile DSL instead: 126 | */ 127 | @Deprecated 128 | void addFile(String source, String destination='/') { 129 | dockerfile.add(source, destination) 130 | } 131 | 132 | /** 133 | * This method is deprecated. Use the new Dockerfile DSL instead: 134 | */ 135 | @Deprecated 136 | void addFile(File source, String destination='/') { 137 | dockerfile.add(source, destination) 138 | } 139 | 140 | /** 141 | * This method is deprecated. Use the new Dockerfile DSL instead: 142 | */ 143 | @Deprecated 144 | void addFile(URL source, String destination='/') { 145 | dockerfile.add(source, destination) 146 | } 147 | 148 | /** 149 | * This method is deprecated. Use the new Dockerfile DSL instead: 150 | */ 151 | @Deprecated 152 | void addFile(Closure copySpec) { 153 | dockerfile.add(copySpec) 154 | } 155 | 156 | /** 157 | * This method is deprecated. Use the new Dockerfile DSL instead: 158 | */ 159 | @Deprecated 160 | void label(Map labels) { 161 | dockerfile.label(labels) 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /src/test/groovy/se/transmode/gradle/plugins/docker/DockerTaskBaseTest.groovy: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2014 Transmode AB 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 | package se.transmode.gradle.plugins.docker 17 | 18 | import static org.hamcrest.Matchers.* 19 | import static org.junit.Assert.* 20 | 21 | import org.gradle.api.Project 22 | import org.gradle.testfixtures.ProjectBuilder 23 | import org.hamcrest.Matchers 24 | import org.junit.Test 25 | 26 | import se.transmode.gradle.plugins.docker.client.DockerClient 27 | import se.transmode.gradle.plugins.docker.client.JavaDockerClient; 28 | import se.transmode.gradle.plugins.docker.client.NativeDockerClient 29 | 30 | class DockerTaskBaseTest { 31 | 32 | private static final String PROJECT_GROUP = 'mygroup' 33 | private static final String REGISTRY = 'myregistry' 34 | private static final String PROJECT_VERSION = 'myversion' 35 | private static final String TAG_VERSION = 'tagVersion' 36 | 37 | // Create dummy task sub-class so we can test the functionality provided by the super-class 38 | public static class DummyTask extends DockerTaskBase { 39 | 40 | } 41 | 42 | private Project createProject() { 43 | Project project = ProjectBuilder.builder().build() 44 | // apply java plugin 45 | project.apply plugin: 'java' 46 | // add docker extension 47 | project.extensions.create(DockerPlugin.EXTENSION_NAME, DockerPluginExtension) 48 | // add dummy task 49 | project.task('dummyTask', type: DummyTask) 50 | project.dummyTask.dockerBinary = 'true' 51 | return project 52 | } 53 | 54 | @Test 55 | public void getNativeClient() { 56 | def project = createProject() 57 | DockerClient client = project.dummyTask.getClient() 58 | assertThat(client, isA(NativeDockerClient.class)) 59 | } 60 | 61 | @Test 62 | public void getJavaClient() { 63 | def project = createProject() 64 | project.dummyTask.useApi = true 65 | DockerClient client = project.dummyTask.getClient() 66 | assertThat(client, isA(JavaDockerClient.class)) 67 | } 68 | 69 | @Test 70 | public void getImageTag() { 71 | def project = createProject() 72 | 73 | // Check the default value 74 | def imageTag = project.dummyTask.imageTag 75 | assertThat(imageTag, 76 | equalToIgnoringCase("${project.name}:${DockerTaskBase.LATEST_VERSION}")) 77 | 78 | // A project group should be added to the name 79 | project.group = PROJECT_GROUP 80 | imageTag = project.dummyTask.imageTag 81 | assertThat(imageTag, 82 | equalToIgnoringCase("${PROJECT_GROUP}/${project.name}:${DockerTaskBase.LATEST_VERSION}")) 83 | 84 | // If we set the registry that takes precedence 85 | project.group = null 86 | project.dummyTask.registry = REGISTRY 87 | imageTag = project.dummyTask.imageTag 88 | assertThat(imageTag, 89 | equalToIgnoringCase("${REGISTRY}/${project.name}:${DockerTaskBase.LATEST_VERSION}")) 90 | 91 | // if we set registry and group 92 | project.group = PROJECT_GROUP 93 | project.dummyTask.registry = REGISTRY 94 | imageTag = project.dummyTask.imageTag 95 | assertThat(imageTag, 96 | equalToIgnoringCase("${REGISTRY}/${PROJECT_GROUP}/${project.name}:${DockerTaskBase.LATEST_VERSION}")) 97 | 98 | // If the project has a version that should be used 99 | project.version = PROJECT_VERSION 100 | imageTag = project.dummyTask.imageTag 101 | assertThat(imageTag, 102 | equalToIgnoringCase("${REGISTRY}/${PROJECT_GROUP}/${project.name}:${PROJECT_VERSION}")) 103 | 104 | // If we set an override version that should be used 105 | 106 | project.dummyTask.tagVersion = TAG_VERSION 107 | imageTag = project.dummyTask.imageTag 108 | assertThat(imageTag, 109 | equalToIgnoringCase("${REGISTRY}/${PROJECT_GROUP}/${project.name}:${TAG_VERSION}")) 110 | 111 | // Explicitly setting version to latest should use that 112 | project.dummyTask.setTagVersionToLatest() 113 | imageTag = project.dummyTask.imageTag 114 | assertThat(imageTag, 115 | equalToIgnoringCase("${REGISTRY}/${PROJECT_GROUP}/${project.name}:${DockerTaskBase.LATEST_VERSION}")) 116 | 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/main/groovy/se/transmode/gradle/plugins/docker/DockerPlugin.groovy: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2014 Transmode AB 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 | package se.transmode.gradle.plugins.docker 17 | import org.gradle.api.Plugin 18 | import org.gradle.api.Project 19 | import org.gradle.api.logging.Logger 20 | import org.gradle.api.logging.Logging 21 | import org.gradle.api.plugins.ApplicationPlugin 22 | 23 | class DockerPlugin implements Plugin { 24 | 25 | private static Logger logger = Logging.getLogger(DockerPlugin) 26 | 27 | private static final String DOCKER_BINARY = "docker" 28 | static final String EXTENSION_NAME = "docker" 29 | 30 | DockerPluginExtension extension 31 | 32 | 33 | void apply(Project project) { 34 | this.extension = createExtension(project) 35 | addDockerTaskType(project) 36 | addDockerRunTaskType(project) 37 | project.plugins.withType(ApplicationPlugin).all { 38 | logger.info('Detected application plugin.') 39 | addDistDockerTask(project) 40 | } 41 | configureDockerTasks(project) 42 | configureDockerRunTasks(project) 43 | } 44 | 45 | private void addDistDockerTask(Project project) { 46 | logger.info('Adding docker task "distDocker"'); 47 | project.task('distDocker', type: DockerTask) { 48 | group = 'docker' 49 | description = "Packs the project's JVM application as a Docker image." 50 | 51 | inputs.files project.distTar 52 | 53 | def installDir = "/" + project.distTar.archiveName - ".${project.distTar.extension}" 54 | 55 | doFirst { 56 | applicationName = project.applicationName 57 | dockerfile { 58 | ADD(project.distTar.outputs.files.singleFile) 59 | ENTRYPOINT(["$installDir/bin/${project.applicationName}"]) 60 | } 61 | } 62 | } 63 | } 64 | 65 | private void addDockerTaskType(Project project) { 66 | logger.info("Adding docker task type"); 67 | project.ext.Docker = DockerTask.class 68 | } 69 | 70 | private void addDockerRunTaskType(Project project) { 71 | project.ext.DockerRun = DockerRunTask.class 72 | logger.info("Adding docker run task type"); 73 | } 74 | 75 | private DockerPluginExtension createExtension(Project project) { 76 | def extension = project.extensions.create(EXTENSION_NAME, DockerPluginExtension) 77 | extension.with { 78 | maintainer = '' 79 | dockerBinary = DOCKER_BINARY 80 | registry = '' 81 | useApi = Boolean.FALSE 82 | hostUrl = '' 83 | apiUsername = '' 84 | apiEmail = '' 85 | apiPassword = '' 86 | } 87 | logger.info("Adding docker extension"); 88 | return extension 89 | } 90 | 91 | private void configureDockerTasks(Project project) { 92 | project.tasks.withType(DockerTask.class).all { task -> 93 | logger.info('Applying docker defaults to task {}', task.name); 94 | applyTaskDefaults(task) 95 | } 96 | } 97 | 98 | private void configureDockerRunTasks(Project project) { 99 | project.tasks.withType(DockerRunTask.class).all { task -> 100 | applyRunTaskDefaults(task) 101 | } 102 | logger.info("Applying docker defaults to tasks of type 'DockerRun'"); 103 | } 104 | 105 | private void applyTaskDefaults(task) { 106 | // @todo: don't use conventionMapping as it is an internal mechanism 107 | // see http://forums.gradle.org/gradle/topics/how_do_you_use_a_conventionmapping_to_do_the_following 108 | task.conventionMapping.with { 109 | dockerBinary = { extension.dockerBinary } 110 | maintainer = { extension.maintainer } 111 | registry = { extension.registry } 112 | useApi = { extension.useApi } 113 | hostUrl = { extension.hostUrl } 114 | apiUsername = { extension.apiUsername } 115 | apiPassword = { extension.apiPassword } 116 | apiEmail = { extension.apiEmail } 117 | } 118 | } 119 | 120 | private void applyRunTaskDefaults(task) { 121 | // @todo: don't use conventionMapping as it is an internal mechanism 122 | // see http://forums.gradle.org/gradle/topics/how_do_you_use_a_conventionmapping_to_do_the_following 123 | task.conventionMapping.with { 124 | dockerBinary = { extension.dockerBinary } 125 | registry = { extension.registry } 126 | useApi = { extension.useApi } 127 | hostUrl = { extension.hostUrl } 128 | apiUsername = { extension.apiUsername } 129 | apiPassword = { extension.apiPassword } 130 | apiEmail = { extension.apiEmail } 131 | } 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /examples/spark/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /examples/simple/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /examples/application/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /src/main/groovy/se/transmode/gradle/plugins/docker/DockerTask.groovy: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2014 Transmode AB 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 | package se.transmode.gradle.plugins.docker 17 | 18 | import com.google.common.annotations.VisibleForTesting 19 | import org.gradle.api.Task 20 | import org.gradle.api.logging.Logger 21 | import org.gradle.api.logging.Logging 22 | import org.gradle.api.tasks.TaskAction 23 | import se.transmode.gradle.plugins.docker.client.DockerClient 24 | import se.transmode.gradle.plugins.docker.image.Dockerfile 25 | 26 | class DockerTask extends DockerTaskBase { 27 | 28 | private static Logger logger = Logging.getLogger(DockerTask) 29 | public static final String DEFAULT_IMAGE = 'ubuntu' 30 | 31 | // Name and Email of the image maintainer 32 | String maintainer 33 | // Whether or not to execute docker to build the image (default: false) 34 | Boolean dryRun 35 | // Whether or not to push the image into the registry (default: false) 36 | Boolean push 37 | // Wether or not the plugin will use pull flag when building 38 | boolean pull 39 | 40 | @Delegate(deprecated=true) 41 | LegacyDockerfileMethods legacyMethods 42 | 43 | // fixme: all of this should work: dockerfile = File()/String() and dockerfile { ... } how can I achieve this? 44 | private Dockerfile dockerfile 45 | 46 | @Override 47 | Task configure(Closure configureClosure) { 48 | def resolveClosure = { path -> project.file(path) } 49 | def copyClosure = { Closure copyClosure -> project.copy(copyClosure) } 50 | this.dockerfile = new Dockerfile(stageDir, resolveClosure, copyClosure) 51 | this.legacyMethods = new LegacyDockerfileMethods(dockerfile) 52 | super.configure(configureClosure) 53 | } 54 | 55 | /** 56 | * Configure Dockerfile with a closure, e.g.: 57 | * dockerfile { 58 | * FROM 'ubuntu' 59 | * ADD myFile 60 | * } 61 | * @param closure to configure dockerfile 62 | */ 63 | public void dockerfile(Closure closure) { 64 | dockerfile.with(closure) 65 | } 66 | 67 | /** 68 | * Start off with existing external Dockerfile and extend it 69 | * @param Path to external Dockerfile 70 | */ 71 | public void setDockerfile(String path) { 72 | dockerfile(project.file(path)) 73 | } 74 | 75 | /** 76 | * Start off with existing external Dockerfile and extend it 77 | * @param External Dockerfile 78 | */ 79 | public void setDockerfile(File baseFile) { 80 | logger.info('Creating Dockerfile from file {}.', baseFile) 81 | dockerfile.extendDockerfile(baseFile) 82 | } 83 | 84 | /** 85 | * Name of base docker image 86 | */ 87 | String baseImage 88 | 89 | /** 90 | * Return the base docker image. 91 | * 92 | * If the base image is set in the task, return it. Otherwise return the base image 93 | * defined in the 'docker' extension. If the extension base image is not set determine 94 | * base image based on the 'targetCompatibility' property from the java plugin. 95 | * 96 | * @return Name of base docker image 97 | */ 98 | public String getBaseImage() { 99 | if (baseImage) { 100 | return baseImage 101 | } 102 | def projectBaseImage = project[DockerPlugin.EXTENSION_NAME].baseImage 103 | if (projectBaseImage) { 104 | return projectBaseImage 105 | } else if (project.hasProperty('targetCompatibility')) { 106 | return JavaBaseImage.imageFor(project.targetCompatibility).imageName 107 | } else { 108 | return DEFAULT_IMAGE 109 | } 110 | } 111 | 112 | // Dockerfile instructions (ADD, RUN, etc.) 113 | def instructions 114 | // Dockerfile staging area i.e. context dir 115 | File stageDir 116 | DockerTask() { 117 | instructions = [] 118 | stageDir = new File(project.buildDir, "docker") 119 | } 120 | 121 | void contextDir(String contextDir) { 122 | stageDir = new File(stageDir, contextDir) 123 | } 124 | 125 | protected File createDirIfNotExists(File dir) { 126 | if (!dir.exists()) 127 | dir.mkdirs() 128 | return dir 129 | } 130 | 131 | @VisibleForTesting 132 | protected void setupStageDir() { 133 | logger.info('Setting up staging directory.') 134 | createDirIfNotExists(stageDir) 135 | dockerfile.stagingBacklog.each() { closure -> closure() } 136 | } 137 | 138 | @VisibleForTesting 139 | protected Dockerfile buildDockerfile() { 140 | if (!dockerfile.hasBase()) { 141 | def baseImage = getBaseImage() 142 | logger.info('Creating Dockerfile from base {}.', baseImage) 143 | dockerfile.from(baseImage) 144 | } 145 | // fixme: only add maintainer if not already set in external dockerfile or via dockerfile.maintainer 146 | if (getMaintainer()) { 147 | dockerfile.maintainer(getMaintainer()) 148 | } 149 | return dockerfile.appendAll(instructions) 150 | } 151 | 152 | @TaskAction 153 | void build() { 154 | setupStageDir() 155 | buildDockerfile().writeToFile(new File(stageDir, 'Dockerfile')) 156 | tag = getImageTag() 157 | logger.info('Determining image tag: {}', tag) 158 | 159 | if (!dryRun) { 160 | DockerClient client = getClient() 161 | println client.buildImage(stageDir, tag, pull) 162 | if (push) { 163 | println client.pushImage(tag) 164 | } 165 | } 166 | 167 | } 168 | 169 | } 170 | -------------------------------------------------------------------------------- /src/test/groovy/se/transmode/gradle/plugins/docker/DockerTaskTest.groovy: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2014 Transmode AB 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 | package se.transmode.gradle.plugins.docker 17 | import org.gradle.api.JavaVersion 18 | import org.gradle.api.Project 19 | import org.gradle.api.Task 20 | import org.gradle.testfixtures.ProjectBuilder 21 | import org.junit.Rule 22 | import org.junit.Test 23 | import org.junit.rules.TemporaryFolder 24 | 25 | import static org.hamcrest.Matchers.* 26 | import static org.junit.Assert.assertThat 27 | 28 | class DockerTaskTest { 29 | 30 | private static final String TASK_NAME = 'dockerTask' 31 | private static final ArrayList TEST_ENV = ['foo', 'bar'] 32 | private static final String TEST_TARGET_DIR = 'testTargetDir' 33 | private static final String TEST_MAINTAINER = 'john doe' 34 | private static final ArrayList TEST_INSTRUCTIONS = [ 35 | 'FROM ubuntu:14.04', 36 | 'ADD foo/bar /', 37 | 'RUN echo hello world' 38 | ] 39 | 40 | @Rule 41 | public TemporaryFolder testFolder = new TemporaryFolder() 42 | 43 | private Project createProject() { 44 | def project = ProjectBuilder.builder().build() 45 | project.extensions.create(DockerPlugin.EXTENSION_NAME, DockerPluginExtension) 46 | return project 47 | } 48 | 49 | private Task createTask(Project project) { 50 | return project.task(TASK_NAME, type: DockerTask).configure() 51 | } 52 | 53 | @Test 54 | public void addTaskToProject() { 55 | def task = createTask(createProject()) 56 | assertThat task, is(instanceOf(DockerTask)) 57 | assertThat task.name, is(equalTo(TASK_NAME)) 58 | } 59 | 60 | @Test 61 | public void testExposePort() { 62 | def task = createTask(createProject()) 63 | task.exposePort(99) 64 | assertThat task.buildDockerfile().instructions[1], equalTo('EXPOSE 99') 65 | 66 | } 67 | 68 | @Test 69 | public void testExposeMultiplePorts() { 70 | def task = createTask(createProject()) 71 | task.exposePort(99, 100, 101) 72 | assertThat task.buildDockerfile().instructions[1], equalTo('EXPOSE 99 100 101') 73 | 74 | } 75 | 76 | @Test 77 | public void testExposePortUdp() { 78 | def task = createTask(createProject()) 79 | task.exposePort("162/udp") 80 | assertThat task.buildDockerfile().instructions[1], equalTo('EXPOSE 162/udp') 81 | } 82 | 83 | @Test 84 | public void nonJavaDefaultBaseImage() { 85 | def project = createProject() 86 | def task = createTask(project) 87 | assertThat task.baseImage, is(equalTo(DockerTask.DEFAULT_IMAGE)) 88 | } 89 | 90 | @Test 91 | public void projectBaseImageHasPrecedence() { 92 | def project = createProject() 93 | project[DockerPlugin.EXTENSION_NAME].baseImage = 'dummyImage' 94 | project.apply plugin: 'java' 95 | project.setProperty('targetCompatibility', JavaVersion.VERSION_1_1) 96 | def task = createTask(project) 97 | assertThat task.baseImage, is(equalTo('dummyImage')) 98 | } 99 | 100 | @Test 101 | public void overrideBaseImageInExtension() { 102 | def project = createProject() 103 | def task = createTask(project) 104 | project[DockerPlugin.EXTENSION_NAME].baseImage = "extensionBase" 105 | assertThat task.baseImage, is(equalTo("extensionBase")) 106 | } 107 | 108 | @Test 109 | public void overrideBaseImageInTask() { 110 | def task = createTask(createProject()) 111 | task.baseImage = "taskBase" 112 | assertThat task.baseImage, is(equalTo("taskBase")) 113 | } 114 | 115 | @Test 116 | public void determineBaseImageFromTargetCompatibilityIfNotOverriden() { 117 | def project = createProject() 118 | project.apply plugin: 'java' 119 | def task = createTask(project) 120 | def testVersion = JavaVersion.VERSION_1_6 121 | project.targetCompatibility = testVersion 122 | assertThat project[DockerPlugin.EXTENSION_NAME].baseImage, is(nullValue()) 123 | assertThat task.baseImage, 124 | is(equalTo(JavaBaseImage.imageFor(testVersion).imageName)) 125 | } 126 | 127 | // @fixme: this is an integration test! 128 | @Test 129 | public void testAddFileWithDir() { 130 | def project = createProject() 131 | def task = createTask(project) 132 | 133 | // Get directory to use 134 | URL dir_url = ClassLoader.getSystemResource(TEST_TARGET_DIR) 135 | File dir = new File(dir_url.toURI()) 136 | assertThat(dir.isDirectory(), equalTo(true)) 137 | 138 | // Add the directory and do the work to move it to the staging directory 139 | task.addFile(dir) 140 | task.setupStageDir() 141 | 142 | // Confirm that the directory was copied under the staging dir 143 | File targetDir = new File(task.stageDir, TEST_TARGET_DIR) 144 | assertThat(targetDir.exists(), is(true)) 145 | assertThat(targetDir.isDirectory(), is(true)) 146 | assertThat(targetDir.list().length, is(equalTo(dir.list().length))) 147 | } 148 | 149 | @Test 150 | public void buildDockerfileFromFileAndExtend() { 151 | def project = createProject() 152 | def task = createTask(project) 153 | // write base dockerfile to file 154 | def externalDockerfile = testFolder.newFile('Dockerfile') 155 | externalDockerfile.withWriter { out -> 156 | TEST_INSTRUCTIONS.each { out.writeLine(it) } 157 | } 158 | task.setDockerfile externalDockerfile 159 | // add instructions to dockerfile 160 | task.maintainer = TEST_MAINTAINER 161 | task.setEnvironment(*TEST_ENV) 162 | 163 | def actual = task.buildDockerfile().instructions 164 | assertThat(actual, 165 | contains(*TEST_INSTRUCTIONS, 166 | "ENV ${TEST_ENV.join(' ')}".toString(), 167 | "MAINTAINER ${TEST_MAINTAINER}".toString())) 168 | } 169 | 170 | @Test 171 | public void switchUser() { 172 | def task = createTask(createProject()) 173 | task.switchUser('junit') 174 | assertThat "USER junit".toString(), isIn(task.buildDockerfile().instructions) 175 | } 176 | 177 | @Test 178 | public void defineLabel() { 179 | def task = createTask(createProject()) 180 | task.label(foo: 'bar') 181 | assertThat 'LABEL "foo"="bar"', isIn(task.buildDockerfile().instructions) 182 | } 183 | 184 | @Test 185 | public void defineMultipleLabels() { 186 | def task = createTask(createProject()) 187 | task.label(foo1: 'bar1', foo2: 'bar2') 188 | assertThat 'LABEL "foo1"="bar1" "foo2"="bar2"', isIn(task.buildDockerfile().instructions) 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /src/main/java/se/transmode/gradle/plugins/docker/client/JavaDockerClient.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2014 Transmode AB 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 | package se.transmode.gradle.plugins.docker.client; 17 | 18 | import java.io.File; 19 | import java.io.FileOutputStream; 20 | import java.io.IOException; 21 | import java.io.InputStream; 22 | import java.io.OutputStream; 23 | import java.util.List; 24 | import java.util.Map; 25 | 26 | import org.apache.commons.lang.StringUtils; 27 | import org.gradle.api.GradleException; 28 | import org.gradle.api.logging.Logger; 29 | import org.gradle.api.logging.Logging; 30 | 31 | import com.github.dockerjava.core.DockerClientBuilder; 32 | import com.github.dockerjava.core.DockerClientConfig; 33 | import com.github.dockerjava.core.command.BuildImageResultCallback; 34 | import com.github.dockerjava.core.command.PushImageResultCallback; 35 | import com.google.common.base.Preconditions; 36 | 37 | public class JavaDockerClient implements DockerClient { 38 | 39 | private static Logger log = Logging.getLogger(JavaDockerClient.class); 40 | 41 | private com.github.dockerjava.api.DockerClient dockerClient; 42 | 43 | JavaDockerClient(com.github.dockerjava.api.DockerClient dockerClient) { 44 | this.dockerClient = dockerClient; 45 | } 46 | 47 | public static JavaDockerClient create(String url, String user, String password, String email) { 48 | final DockerClientConfig.DockerClientConfigBuilder configBuilder = DockerClientConfig.createDefaultConfigBuilder(); 49 | if (StringUtils.isEmpty(url)) { 50 | log.info("Connecting to localhost"); 51 | } else { 52 | log.info("Connecting to {}", url); 53 | configBuilder.withUri(url); 54 | } 55 | if (StringUtils.isNotEmpty(user)) { 56 | configBuilder.withUsername(user).withPassword(password).withEmail(email); 57 | } 58 | return new JavaDockerClient(DockerClientBuilder.getInstance(configBuilder).build()); 59 | } 60 | 61 | @Override 62 | public String buildImage(File buildDir, String tag, boolean pull) { 63 | Preconditions.checkNotNull(tag, "Image tag can not be null."); 64 | Preconditions.checkArgument(!tag.isEmpty(), "Image tag can not be empty."); 65 | final BuildImageResultCallback resultCallback = new BuildImageResultCallback(); 66 | dockerClient.buildImageCmd(buildDir).withTag(tag).withPull(pull).exec(resultCallback); 67 | return resultCallback.awaitImageId(); 68 | } 69 | 70 | @Override 71 | public String pushImage(String tag) { 72 | Preconditions.checkNotNull(tag, "Image tag can not be null."); 73 | Preconditions.checkArgument(!tag.isEmpty(), "Image tag can not be empty."); 74 | final PushImageResultCallback pushImageResultCallback = dockerClient.pushImageCmd(tag).exec(new PushImageResultCallback()); 75 | pushImageResultCallback.awaitSuccess(); 76 | return ""; 77 | } 78 | 79 | @Override 80 | public String run(String tag, String containerName, boolean detached, boolean autoRemove, Map env, Map ports, Map volumes, List volumesFrom, List links) { 81 | return null; 82 | } 83 | 84 | /*@Override 85 | public String run(String tag, String containerName, boolean detached, boolean autoRemove, 86 | Map env, Map ports, Map volumes, 87 | List volumesFrom, List links) { 88 | 89 | Preconditions.checkArgument(!StringUtils.isEmpty(tag), 90 | "Image tag cannot be empty or null."); 91 | Preconditions.checkArgument(env != null, "Environment map cannot be null."); 92 | Preconditions.checkArgument(ports != null, "Exported port map cannot be null."); 93 | Preconditions.checkArgument(volumes != null, "Volume map cannot be null."); 94 | Preconditions.checkArgument(volumesFrom != null, "Volumes from list cannot be null."); 95 | Preconditions.checkArgument(links != null, "Link list cannot be null."); 96 | Preconditions.checkArgument(!detached || !autoRemove, 97 | "Cannot set both detached and autoRemove options to true."); 98 | 99 | // Start by creating the container 100 | CreateContainerCmd createCmd = createContainerCmd(tag).withName(containerName); 101 | String[] envList = new String[env.size()]; 102 | int index = 0; 103 | for (Entry entry : env.entrySet()) { 104 | String envSetting = String.format("%s=%s", entry.getKey(), entry.getValue()); 105 | envList[index++] = envSetting; 106 | } 107 | createCmd.withEnv(envList); 108 | ContainerCreateResponse createResponse; 109 | try { 110 | createResponse = createContainerCmd(tag).exec(); 111 | } catch (NotFoundException nfe) { 112 | // TODO have option to pull image 113 | throw nfe; 114 | } 115 | String containerId = createResponse.getId(); 116 | 117 | // Configure start command 118 | StartContainerCmd startCmd = startContainerCmd(containerId); 119 | Bind[] binds = new Bind[volumes.size()]; 120 | index = 0; 121 | for (Entry entry : volumes.entrySet()) { 122 | Volume vol = new Volume(entry.getValue()); 123 | Bind bind = new Bind(entry.getKey(), vol); 124 | binds[index++] = bind; 125 | } 126 | startCmd.withBinds(binds); 127 | startCmd.withVolumesFrom(StringUtils.join(volumesFrom, ",")); 128 | Link[] linkArr = new Link[links.size()]; 129 | index = 0; 130 | for (String linkStr : links) { 131 | String[] values = linkStr.split(":"); 132 | Link link = new Link(values[0], values.length == 2 ? values[1] : values[0]); 133 | linkArr[index++] = link; 134 | } 135 | startCmd.withLinks(linkArr); 136 | 137 | // Start the container 138 | try { 139 | startCmd.exec(); 140 | } catch (Exception e) { 141 | // Want to get rid of container we created 142 | removeContainerCmd(containerId).exec(); 143 | } 144 | 145 | // Should we wait around and/or remove the container on exit 146 | if (autoRemove) { 147 | return removeOnExit(containerId); 148 | } else if (detached) { 149 | return containerId; 150 | } else { 151 | return waitForExit(containerId); 152 | } 153 | } 154 | 155 | private String removeOnExit(String containerId) { 156 | String exitStatus = waitForExit(containerId); 157 | removeContainerCmd(containerId).exec(); 158 | return exitStatus; 159 | } 160 | 161 | private String waitForExit(String containerId) { 162 | // TODO -- show container output if/when we get that option from docker-java 163 | return "Exit status: " + waitContainerCmd(containerId).exec(); 164 | }*/ 165 | } 166 | -------------------------------------------------------------------------------- /src/main/groovy/se/transmode/gradle/plugins/docker/image/Dockerfile.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Transmode AB 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 | package se.transmode.gradle.plugins.docker.image 17 | 18 | import com.google.common.io.Files 19 | import org.gradle.api.logging.Logger 20 | import org.gradle.api.logging.Logging 21 | 22 | class Dockerfile { 23 | private static Logger log = Logging.getLogger(Dockerfile) 24 | 25 | private List instructions 26 | private List baseInstructions 27 | private File contextDir 28 | // Actions needed to setup the build stage before building an image 29 | List stagingBacklog 30 | 31 | final private Closure copyCallback 32 | final private Object resolvePathCallback 33 | 34 | Dockerfile(File contextDir) { 35 | this(contextDir, { String path -> new File(path) }, { -> }) 36 | } 37 | 38 | Dockerfile(File contextDir, resolvePathCallback, copyCallback) { 39 | this.contextDir = contextDir 40 | this.resolvePathCallback = resolvePathCallback 41 | this.copyCallback = copyCallback 42 | this.baseInstructions = [] 43 | this.instructions = [] 44 | this.stagingBacklog = [] 45 | } 46 | 47 | Dockerfile append(def instruction) { 48 | this.instructions.add(instruction.toString()) 49 | return this 50 | } 51 | 52 | Dockerfile appendAll(List instructions) { 53 | this.instructions.addAll(instructions*.toString()) 54 | return this 55 | } 56 | 57 | void writeToFile(File destination) { 58 | destination.withWriter { out -> 59 | instructions.each() { line -> 60 | out.writeLine(line) 61 | } 62 | } 63 | } 64 | 65 | /** 66 | * Default method if method not found to support all Dockerfile instructions. 67 | * 68 | * Example: foo('bar', 42) becomes "FOO bar 42" 69 | */ 70 | def methodMissing(String name, args) { 71 | // fixme: uppercase methods don't seem to work without parentheses (e.g. "RUN 'echo'") 72 | // see discussion on the Groovy User list: http://groovy.329449.n5.nabble.com/Optional-parentheses-for-methods-with-all-uppercase-name-tp5731174.html 73 | if (name.toLowerCase() != name) { 74 | return callWithLowerCaseName(name, args) 75 | } 76 | log.debug('No explicit method declaration for "{}({})" found. Using default implementation.', name, args.join(', ')) 77 | this.append("${name.toUpperCase()} ${args.join(' ')}") 78 | } 79 | 80 | def callWithLowerCaseName(String name, args) { 81 | name = name.toLowerCase() 82 | return this."$name"(*args) 83 | } 84 | 85 | // todo: consider removing "extendDockerfile" as method and add it as a parameter to dockerfile DockerTask.dockerfile as it is not a real Dockerfile instruction 86 | /** 87 | * Add instructions from an external Dockerfile. 88 | * 89 | * @param baseFile -- Path to external Dockerfile 90 | */ 91 | void extendDockerfile(File baseFile) { 92 | baseInstructions = baseFile as String[] 93 | } 94 | 95 | /** 96 | * Set base image (i.e. FROM ) 97 | * 98 | * @param baseImage -- Name of the base image 99 | */ 100 | void from(String baseImage) { 101 | baseInstructions = ["FROM ${baseImage}"] 102 | } 103 | 104 | void cmd(List cmd) { 105 | this.append('CMD ["' + cmd.join('", "') + '"]') 106 | } 107 | 108 | void entrypoint(List cmd) { 109 | this.append('ENTRYPOINT ["' + cmd.join('", "') + '"]') 110 | } 111 | 112 | private static boolean isUrl(String url) { 113 | try { 114 | new URL(url) 115 | } catch (MalformedURLException e) { 116 | return false 117 | } 118 | return true; 119 | } 120 | 121 | void add(URL source, String destination='/') { 122 | this.append("ADD ${source.toString()} ${destination}") 123 | } 124 | 125 | void add(String source, String destination='/') { 126 | if(isUrl(source)) { 127 | this.append("ADD ${source} ${destination}") 128 | } else { 129 | add(resolvePathCallback(source), destination) 130 | } 131 | } 132 | 133 | void add(File source, String destination='/') { 134 | File target 135 | if (source.isDirectory()) { 136 | target = new File(contextDir, source.name) 137 | } 138 | else { 139 | target = contextDir 140 | } 141 | stagingBacklog.add { -> 142 | copyCallback { 143 | from source 144 | into target 145 | } 146 | } 147 | this.append("ADD ${source.name} ${destination}") 148 | } 149 | 150 | void add(Closure copySpec) { 151 | final tarFile = new File(contextDir, "add_${instructions.size()+1}.tar") 152 | stagingBacklog.add { -> 153 | createTarArchive(tarFile, copySpec) 154 | } 155 | instructions.add("ADD ${tarFile.name} ${'/'}") 156 | } 157 | 158 | void copy(String source, String destination='/') { 159 | copy(resolvePathCallback(source), destination) 160 | } 161 | 162 | void copy(File source, String destination='/') { 163 | File target 164 | if (source.isDirectory()) { 165 | target = new File(contextDir, source.name) 166 | } 167 | else { 168 | target = contextDir 169 | } 170 | stagingBacklog.add { -> 171 | copyCallback { 172 | from source 173 | into target 174 | } 175 | } 176 | this.append("COPY ${source.name} ${destination}") 177 | } 178 | 179 | void copy(Closure copySpec) { 180 | final tarFile = new File(contextDir, "copy_${instructions.size()+1}.tar") 181 | stagingBacklog.add { -> 182 | createTarArchive(tarFile, copySpec) 183 | } 184 | instructions.add("COPY ${tarFile.name} ${'/'}") 185 | } 186 | 187 | private void createTarArchive(File tarFile, Closure copySpec) { 188 | final tmpDir = Files.createTempDir() 189 | log.lifecycle("Creating tar archive {} from {}", tarFile, tmpDir) 190 | try { 191 | /* copy all files to temporary directory */ 192 | copyCallback { 193 | with { 194 | into('/') { 195 | with copySpec 196 | } 197 | } 198 | into tmpDir 199 | } 200 | /* create tar archive */ 201 | new AntBuilder().tar( 202 | destfile: tarFile, 203 | basedir: tmpDir 204 | ) 205 | } finally { 206 | if (!tmpDir.deleteDir()) 207 | println "tmpDir $tmpDir not deleted" 208 | } 209 | } 210 | 211 | void label(Map labels) { 212 | if(labels) { 213 | instructions.add("LABEL " + labels.collect { k,v -> "\"$k\"=\"$v\"" }.join(' ')) 214 | } 215 | } 216 | 217 | /** 218 | * Get the contents of the Dockerfile row by row as a list of strings. 219 | * 220 | * @return Dockerfile instructions as a list of Strings. 221 | */ 222 | List getInstructions() { 223 | return (baseInstructions + instructions)*.toString() 224 | } 225 | 226 | /** 227 | * Return true if base image or base dockerfile to extend has been defined. 228 | * 229 | * @return Boolean true if base is set 230 | */ 231 | Boolean hasBase() { 232 | return baseInstructions.size() > 0 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /src/test/groovy/se/transmode/gradle/plugins/docker/image/DockerfileTest.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Transmode AB 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 | package se.transmode.gradle.plugins.docker.image 17 | 18 | import org.junit.Rule 19 | import org.junit.Test 20 | import org.junit.rules.TemporaryFolder 21 | 22 | import static org.hamcrest.MatcherAssert.assertThat 23 | import static org.hamcrest.Matchers.equalTo 24 | import static org.hamcrest.Matchers.is 25 | 26 | class DockerfileTest { 27 | 28 | public static final String BASE_IMAGE = 'ubuntu:14.04' 29 | public static final String MAINTAINER = 'MAINTAINER john doe' 30 | public static final ArrayList INSTRUCTIONS = [ 31 | 'FROM debian:jessie', 32 | 'CMD /bin/bash' 33 | ] 34 | 35 | @Rule 36 | public TemporaryFolder testFolder = new TemporaryFolder() 37 | 38 | private File createTestDockerfile() { 39 | def source = testFolder.newFile('Dockerfile') 40 | source.withWriter { out -> 41 | INSTRUCTIONS.each { out.writeLine(it) } 42 | } 43 | return source 44 | } 45 | 46 | @Test 47 | void fromBase() { 48 | final dockerfile = new Dockerfile(new File("contextDir")) 49 | dockerfile.from(BASE_IMAGE) 50 | assertThat(dockerfile.instructions, 51 | equalTo(["FROM ${BASE_IMAGE}".toString()])) 52 | } 53 | 54 | @Test 55 | void appendInstruction() { 56 | final dockerfile = new Dockerfile(new File("contextDir")) 57 | dockerfile.with { 58 | from BASE_IMAGE 59 | append MAINTAINER 60 | } 61 | assertThat(dockerfile.instructions, 62 | equalTo(["FROM ${BASE_IMAGE}".toString(), MAINTAINER])) 63 | } 64 | 65 | @Test 66 | void fallbackToDefaultMethod() { 67 | final dockerfile = new Dockerfile(new File("contextDir")) 68 | dockerfile.with { 69 | foo('bar', 42) 70 | bar 'All work and no play makes Jack a dull boy' 71 | } 72 | assertThat(dockerfile.instructions, 73 | equalTo(['FOO bar 42', 74 | 'BAR All work and no play makes Jack a dull boy'])) 75 | } 76 | 77 | @Test 78 | void cmdWithString() { 79 | final dockerfile = new Dockerfile(new File("contextDir")) 80 | dockerfile.cmd '/bin/bash' 81 | assertThat dockerfile.instructions, is(equalTo(['CMD /bin/bash'])) 82 | } 83 | 84 | @Test 85 | void cmdWithList() { 86 | final dockerfile = new Dockerfile(new File("contextDir")) 87 | dockerfile.cmd(['/bin/bash', '-i']) 88 | assertThat dockerfile.instructions, is(equalTo(['CMD ["/bin/bash", "-i"]'])) 89 | } 90 | 91 | @Test 92 | void extendDockerfile() { 93 | File source = createTestDockerfile() 94 | final dockerfile = new Dockerfile(new File("contextDir")) 95 | dockerfile.extendDockerfile(source) 96 | assertThat(dockerfile.instructions, is(equalTo(INSTRUCTIONS))) 97 | } 98 | 99 | @Test 100 | void extendDockerfileAndAppend() { 101 | File source = createTestDockerfile() 102 | final dockerfile = new Dockerfile(new File("contextDir")) 103 | dockerfile.extendDockerfile(source) 104 | dockerfile.append(MAINTAINER) 105 | assertThat(dockerfile.instructions, 106 | equalTo(INSTRUCTIONS + [MAINTAINER])) 107 | } 108 | 109 | @Test 110 | void addURL() { 111 | final dockerfile = new Dockerfile(new File("contextDir")) 112 | final URL url = new URL('http', 'localhost', 8080, '/download/myapp.tar') 113 | 114 | dockerfile.add url 115 | assertThat dockerfile.instructions, is(equalTo(["ADD ${url.toString()} /".toString()])) 116 | } 117 | 118 | @Test 119 | void addURLasString() { 120 | final dockerfile = new Dockerfile(new File("contextDir")) 121 | final String url = 'http://foo.bar/file.tar' 122 | 123 | dockerfile.add url, '/target' 124 | assertThat dockerfile.instructions, is(equalTo(["ADD ${url} /target".toString()])) 125 | } 126 | 127 | 128 | @Test 129 | void addFile() { 130 | final dockerfile = new Dockerfile(new File("contextDir")) 131 | final File file = new File('/tmp/adke') 132 | 133 | dockerfile.add file, '/target' 134 | assertThat dockerfile.instructions, is(equalTo(["ADD ${file.name} /target".toString()])) 135 | } 136 | 137 | @Test 138 | void addDirectory() { 139 | final dockerfile = new Dockerfile(new File("contextDir")) 140 | final File file = new File('gradle/wrapper') 141 | 142 | dockerfile.add file, '/gradle' 143 | assertThat dockerfile.instructions, is(equalTo(['ADD wrapper /gradle'])) 144 | } 145 | 146 | @Test 147 | void addFileAsString() { 148 | final dockerfile = new Dockerfile(new File("contextDir")) 149 | final String file = 'gradle/wrapper/gradle-wrapper.properties' 150 | 151 | dockerfile.add file, '/target' 152 | assertThat dockerfile.instructions, is(equalTo(['ADD gradle-wrapper.properties /target'])) 153 | } 154 | 155 | @Test 156 | void addWithCopySpec() { 157 | final dockerfile = new Dockerfile(new File("contextDir")) 158 | 159 | dockerfile.add { 160 | from('src/foo') 161 | into('dest/bar') 162 | exclude '*~' 163 | } 164 | assertThat dockerfile.instructions, is(equalTo(['ADD add_1.tar /'])) 165 | } 166 | 167 | @Test 168 | void copyFile() { 169 | final dockerfile = new Dockerfile(new File("contextDir")) 170 | final File file = new File('tmp/wetoy') 171 | 172 | dockerfile.copy file, '/foo' 173 | assertThat dockerfile.instructions, is(equalTo(['COPY wetoy /foo'])) 174 | } 175 | 176 | @Test 177 | void copyFileAsString() { 178 | final dockerfile = new Dockerfile(new File("contextDir")) 179 | final String path = 'asdrlu/foo' 180 | 181 | dockerfile.copy path, '/bar' 182 | assertThat dockerfile.instructions, is(equalTo(['COPY foo /bar'])) 183 | } 184 | 185 | @Test 186 | void copyWithCopySpec() { 187 | final dockerfile = new Dockerfile(new File("contextDir")) 188 | 189 | dockerfile.copy { 190 | from('src/foo') 191 | into('dest/bar') 192 | exclude '*~' 193 | } 194 | assertThat dockerfile.instructions, is(equalTo(['COPY copy_1.tar /'])) 195 | } 196 | 197 | @Test 198 | void instructionsAreCaseInsesitive() { 199 | final dockerfile = new Dockerfile(new File("contextDir")) 200 | dockerfile.with { 201 | CMD('/bin/bash') 202 | } 203 | dockerfile.ENV('FOO=bar') 204 | assertThat(dockerfile.instructions, 205 | equalTo(['CMD /bin/bash', 206 | 'ENV FOO=bar'])) 207 | } 208 | 209 | @Test 210 | void exposePort() { 211 | final dockerfile = new Dockerfile(new File("contextDir")) 212 | dockerfile.with { 213 | expose 8080 214 | } 215 | assertThat(dockerfile.instructions, equalTo(['EXPOSE 8080'])) 216 | } 217 | 218 | @Test 219 | void exposeMultiplePort() { 220 | final dockerfile = new Dockerfile(new File("contextDir")) 221 | dockerfile.with { 222 | expose 8080, 8081, 9090 223 | } 224 | assertThat(dockerfile.instructions, equalTo(['EXPOSE 8080 8081 9090'])) 225 | } 226 | 227 | @Test 228 | void exposeUdpPort() { 229 | final dockerfile = new Dockerfile(new File("contextDir")) 230 | dockerfile.with { 231 | expose '162/udp' 232 | } 233 | assertThat(dockerfile.instructions, equalTo(['EXPOSE 162/udp'])) 234 | } 235 | } 236 | -------------------------------------------------------------------------------- /src/test/groovy/se/transmode/gradle/plugins/docker/DockerRunTaskTest.groovy: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2014 Transmode AB 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 | package se.transmode.gradle.plugins.docker 17 | 18 | import org.gradle.api.Project 19 | import org.gradle.testfixtures.ProjectBuilder 20 | import org.junit.Test 21 | import org.mockito.ArgumentMatcher; 22 | import org.mockito.stubbing.Answer; 23 | 24 | import se.transmode.gradle.plugins.docker.client.DockerClient; 25 | import static org.junit.Assert.assertTrue 26 | import static org.mockito.Mockito.*; 27 | 28 | class DockerRunTaskTest { 29 | 30 | public static class TestDockerRunTask extends DockerRunTask { 31 | 32 | DockerClient mockClient; 33 | 34 | TestDockerRunTask() { 35 | mockClient = mock(DockerClient.class) 36 | } 37 | 38 | @Override 39 | protected DockerClient getClient() { 40 | return mockClient; 41 | } 42 | 43 | } 44 | 45 | private static ArgumentMatcher> isEmptyCollection = 46 | new ArgumentMatcher>() { 47 | 48 | @Override 49 | public boolean matches(Object collection) { 50 | return (collection != null) && ((Collection) collection).size() == 0; 51 | } 52 | 53 | } 54 | 55 | private static ArgumentMatcher> isSingletonCollection = 56 | new ArgumentMatcher>() { 57 | 58 | @Override 59 | public boolean matches(Object collection) { 60 | return (collection != null) && ((Collection) collection).size() == 1; 61 | } 62 | 63 | } 64 | 65 | private static ArgumentMatcher> isEmptyMap = 66 | new ArgumentMatcher>() { 67 | 68 | @Override 69 | public boolean matches(Object map) { 70 | return (map != null) && ((Map) map).size() == 0; 71 | } 72 | 73 | } 74 | 75 | private static ArgumentMatcher> isSingletonMap = 76 | new ArgumentMatcher>() { 77 | 78 | @Override 79 | public boolean matches(Object map) { 80 | return (map != null) && ((Map) map).size() == 1; 81 | } 82 | 83 | } 84 | 85 | private static final String CONTAINER_NAME = 'mycontainer' 86 | 87 | private Project createProject() { 88 | Project project = ProjectBuilder.builder().build() 89 | // apply java plugin 90 | project.apply plugin: 'java' 91 | // add docker extension 92 | project.extensions.create(DockerPlugin.EXTENSION_NAME, DockerPluginExtension) 93 | // add docker run task configured to return a mock client 94 | project.task('dockerRunTask', type: TestDockerRunTask) 95 | return project 96 | } 97 | 98 | @Test 99 | public void addTaskToProject() { 100 | def task = ProjectBuilder.builder().build().task('dockerTask', type: DockerRunTask) 101 | assertTrue(task instanceof DockerRunTask) 102 | } 103 | 104 | @Test 105 | public void runDefault() { 106 | def project = createProject() 107 | project.dockerRunTask.run() 108 | verify(project.dockerRunTask.mockClient).run(anyString(), eq(null), 109 | eq(false), eq(false), argThat(isEmptyMap), argThat(isEmptyMap), 110 | argThat(isEmptyMap), argThat(isEmptyCollection), argThat(isEmptyCollection)) 111 | } 112 | 113 | @Test 114 | public void runNamedContainer() { 115 | def project = createProject() 116 | project.dockerRunTask.containerName = CONTAINER_NAME 117 | project.dockerRunTask.run() 118 | verify(project.dockerRunTask.mockClient).run(anyString(), eq(CONTAINER_NAME), 119 | eq(false), eq(false), argThat(isEmptyMap), argThat(isEmptyMap), 120 | argThat(isEmptyMap), argThat(isEmptyCollection), argThat(isEmptyCollection)) 121 | } 122 | 123 | @Test 124 | public void runDetached() { 125 | def project = createProject() 126 | project.dockerRunTask.detached = true 127 | project.dockerRunTask.run() 128 | verify(project.dockerRunTask.mockClient).run(anyString(), eq(null), 129 | eq(true), eq(false), argThat(isEmptyMap), argThat(isEmptyMap), 130 | argThat(isEmptyMap), argThat(isEmptyCollection), argThat(isEmptyCollection)) 131 | } 132 | 133 | @Test 134 | public void runAutoRemove() { 135 | def project = createProject() 136 | project.dockerRunTask.autoRemove = true 137 | project.dockerRunTask.run() 138 | verify(project.dockerRunTask.mockClient).run(anyString(), eq(null), 139 | eq(false), eq(true), argThat(isEmptyMap), argThat(isEmptyMap), 140 | argThat(isEmptyMap), argThat(isEmptyCollection), argThat(isEmptyCollection)) 141 | } 142 | 143 | @Test 144 | public void runWithEnv() { 145 | def project = createProject() 146 | project.dockerRunTask.env("foo", "bar") 147 | project.dockerRunTask.run() 148 | verify(project.dockerRunTask.mockClient).run(anyString(), eq(null), 149 | eq(false), eq(false), argThat(isSingletonMap), argThat(isEmptyMap), 150 | argThat(isEmptyMap), argThat(isEmptyCollection), argThat(isEmptyCollection)) 151 | } 152 | 153 | @Test 154 | public void runWithPorts() { 155 | def project = createProject() 156 | project.dockerRunTask.publish("foo", "bar") 157 | project.dockerRunTask.run() 158 | verify(project.dockerRunTask.mockClient).run(anyString(), eq(null), 159 | eq(false), eq(false), argThat(isEmptyMap), argThat(isSingletonMap), 160 | argThat(isEmptyMap), argThat(isEmptyCollection), argThat(isEmptyCollection)) 161 | } 162 | 163 | @Test 164 | public void runWithVolumes() { 165 | def project = createProject() 166 | project.dockerRunTask.volume("foo", "bar") 167 | project.dockerRunTask.run() 168 | verify(project.dockerRunTask.mockClient).run(anyString(), eq(null), 169 | eq(false), eq(false), argThat(isEmptyMap), argThat(isEmptyMap), 170 | argThat(isSingletonMap), argThat(isEmptyCollection), argThat(isEmptyCollection)) 171 | } 172 | 173 | @Test 174 | public void runWithVolumesFrom() { 175 | def project = createProject() 176 | project.dockerRunTask.volumesFrom("foo") 177 | project.dockerRunTask.run() 178 | verify(project.dockerRunTask.mockClient).run(anyString(), eq(null), 179 | eq(false), eq(false), argThat(isEmptyMap), argThat(isEmptyMap), 180 | argThat(isEmptyMap), argThat(isSingletonCollection), argThat(isEmptyCollection)) 181 | } 182 | 183 | @Test 184 | public void runWithLinks() { 185 | def project = createProject() 186 | project.dockerRunTask.link("foo") 187 | project.dockerRunTask.run() 188 | verify(project.dockerRunTask.mockClient).run(anyString(), eq(null), 189 | eq(false), eq(false), argThat(isEmptyMap), argThat(isEmptyMap), 190 | argThat(isEmptyMap), argThat(isEmptyCollection), argThat(isSingletonCollection)) 191 | } 192 | 193 | @Test(expected = IllegalArgumentException.class) 194 | public void runIllegalTaskState() { 195 | // Create project without the client mocked 196 | Project project = ProjectBuilder.builder().build() 197 | project.apply plugin: 'java' 198 | project.extensions.create(DockerPlugin.EXTENSION_NAME, DockerPluginExtension) 199 | project.task('dockerRunTask', type: DockerRunTask) 200 | 201 | // Configure the task to be both detached and auto-remove 202 | project.dockerRunTask.detached = true 203 | project.dockerRunTask.autoRemove = true 204 | project.dockerRunTask.run() 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Gradle Docker plugin 2 | 3 | [![Join the chat at https://gitter.im/Transmode/gradle-docker](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/Transmode/gradle-docker?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 4 | 5 | [![Build Status](https://travis-ci.org/Transmode/gradle-docker.svg?branch=master)](https://travis-ci.org/Transmode/gradle-docker) 6 | [![Coverage](https://codecov.io/gh/Transmode/gradle-docker/branch/master/graph/badge.svg)](https://codecov.io/gh/Transmode/gradle-docker) [ ![Download](https://api.bintray.com/packages/transmode/gradle-plugins/gradle-docker/images/download.png) ](https://bintray.com/transmode/gradle-plugins/gradle-docker/_latestVersion) 7 | 8 | This plugin for [Gradle](http://www.gradle.org/) adds the capability to build and publish [Docker](http://docker.io/) images from the build script. It is available through [jCenter](https://bintray.com/transmode/gradle-plugins/gradle-docker/view) and [MavenCentral](http://search.maven.org/#browse%7C566382288). 9 | 10 | See the [change log](CHANGELOG.md) for information about the latest changes. 11 | 12 | ## Extending the application plugin 13 | The gradle-docker plugin adds a task `distDocker` if the project already has the [application plugin](http://www.gradle.org/docs/current/userguide/application_plugin.html) applied: 14 | 15 | ```gradle 16 | apply plugin: 'application' 17 | apply plugin: 'docker' 18 | ``` 19 | 20 | Executing the `distDocker` task builds a docker image containing all application files (libs, scripts, etc.) created by the `distTar` task from the application plugin. If you already use the application plugin to package your project then the docker plugin will add simple docker image building to your project. 21 | 22 | By default `distDocker` uses a base image with a Java runtime according to the project's `targetCompatibility` property. The docker image entry point is set to the start script created by the application plugin. Checkout the [application example](examples/application/) project. 23 | 24 | **Note**: The creation of the convention task `distDocker` is currently only supported for JVM based application projects. If you are not using a JVM based application, use the task type `Docker` directly to create a task to build Docker images of your application. 25 | 26 | 27 | ## The `Docker`task 28 | The docker plugin introduces the task type `Docker`. A task of this type can be used to build and publish Docker images. See the [Dockerfile documentation](http://docs.docker.com/reference/builder/) for information about how Docker images are built. 29 | 30 | In the following example we build a Docker image in our Gradle build script for the popular reverse proxy nginx. The image will be tagged with the name `foo/nginx`. The example is taken from the official Dockerfile [examples](http://docs.docker.com/reference/builder/#dockerfile-examples): 31 | 32 | ```gradle 33 | apply plugin: 'docker' 34 | 35 | buildscript { 36 | repositories { jcenter() } 37 | dependencies { 38 | classpath 'se.transmode.gradle:gradle-docker:1.2' 39 | } 40 | } 41 | 42 | group = "foo" 43 | 44 | docker { 45 | baseImage "ubuntu" 46 | maintainer 'Guillaume J. Charmes "guillaume@dotcloud.com"' 47 | } 48 | 49 | task nginxDocker(type: Docker) { 50 | applicationName = "nginx" 51 | runCommand 'echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.list' 52 | runCommand "apt-get update" 53 | runCommand "apt-get install -y inotify-tools nginx apache2 openssh-server" 54 | } 55 | ``` 56 | 57 | ## Building your Dockerfile 58 | In the example above the instructions on how to build the nginx Docker image are configured **inline** using methods of the Docker Gradle task. During task execution the plugin first creates a [Dockerfile](https://docs.docker.com/reference/builder/) which it then passes to Docker to build the image. 59 | 60 | The available instructions are: 61 | 62 | | Dockerfile instruction | Gradle task method | 63 | | -----------------------|--------------------| 64 | | `ADD` | `addFile(Closure copySpec)` 65 | | | `addFile(String source, String dest)` 66 | | | `addFile(File source, String dest)` 67 | | `CMD` | `defaultCommand(List cmd)` 68 | | `ENTRYPOINT` | `entryPoint(List entryPoint)` 69 | | `ENV` | `setEnvironment(String key, String val)` 70 | | `EXPOSE` | `exposePort(Integer port)` 71 | | | `exposePort(String port)` 72 | | `RUN` | `runCommand(String cmd)` 73 | | `USER` | `switchUser(String userNameOrUid)` 74 | | `VOLUME` | `volume(String... paths)` 75 | | `WORKDIR` | `workingDir(String dir)` 76 | 77 | Instead of defining the build instructions inline in the task it is also possible to supply an **external Dockerfile**. If the task property `dockerfile` is set to the path of an existing Dockerfile the plugin will use this file instead to build the image. 78 | 79 | You can even combine these two methods: Supplying an external Dockerfile and extending it by defining instructions in the task. The build instructions from the external Dockerfile are read first and the instructions defined in the task appended. If an external Dockerfile is supplied, the `baseImage` property is ignored. 80 | 81 | ## Configuring the plugin 82 | The plugin exposes configuration options on 2 levels: globally through a plugin extension and on a per task basis. The plugin tries to always set sensible defaults for all properties. 83 | 84 | ### Global configuration through plugin extension properties 85 | Configuration properties in the plugin extension `docker` are applied to all Docker tasks. Available properties are: 86 | 87 | - `dockerBinary` - The path to the docker binary. 88 | - `baseImage` - The base docker image used when building images (i.e. the name after `FROM` in the Dockerfile). 89 | - `maintainer` - The name and email address of the image maintainer. 90 | - `registry` - The hostname and port of the Docker image registry unless the Docker Hub Registry is used. 91 | - `useApi` - Use the Docker Remote API instead of a locally installed `docker` binary. See [below](https://github.com/Transmode/gradle-docker/blob/master/README.md#docker-remote-api) 92 | 93 | Example to set the base docker image and maintainer name for all tasks: 94 | 95 | ```gradle 96 | docker { 97 | maintainer = 'John Doe ' 98 | baseImage = 'johndoe/nextgenjdk:9.0' 99 | } 100 | ``` 101 | 102 | ### Task configuration through task properties 103 | All properties that are exposed through the plugin extension can be overridden in each task. 104 | The image tag is constructed according to: 105 | 106 | ```gradle 107 | tag = "${project.group}/${applicationName}:${tagVersion}" 108 | ``` 109 | 110 | Where: 111 | 112 | - `project.group` - This is a standard Gradle project property. If not defined, the `{project.group}/` is omitted. 113 | - `applicationName` - The name of the application being "dockerized". 114 | - `tagVersion` - Optional version name added to the image tag name. Defaults to `project.version` or "latest" if `project.version` is unspecified. 115 | 116 | The following example task will tag the docker image as `org.acme/bar:13.0`: 117 | 118 | ```gradle 119 | ... 120 | group = 'org.acme' 121 | ... 122 | task fooDocker(type: Docker) { 123 | applicationName = 'foobar' 124 | tagVersion = '13.0' 125 | } 126 | ``` 127 | 128 | ### A note about base images ### 129 | If no base image is configured through the extension or task property a suitable image is chosen based on the project's `targetCompatibility`. A project targeting Java 7 will for instance get a default base image with a Java 7 runtime. 130 | 131 | ## Docker Remote API 132 | By default the plug-in will use the `docker` command line tool to execute any docker commands (such as `build` and `push`). However, it can be configured to use the [Docker Remote API](https://docs.docker.com/reference/api/docker_remote_api/) instead via the `useApi` extension property: 133 | 134 | ```gradle 135 | docker { 136 | useApi true 137 | } 138 | ``` 139 | 140 | Use of the remote API requires that the Docker server be configured to listen over HTTP and that it have support for version 1.11 of the API (connecting over Unix Domain sockets is not supported yet). The following configuration options are available: 141 | 142 | * `hostUrl` - set the URL used to contact the Docker server. Defaults to `http://localhost:2375` 143 | * `apiUsername` - set the username used to authenticate the user with the Docker server. Defaults to `nil` which means no authentication is performed. 144 | * `apiPassword` - set the password used to authenticate the user with the Docker server. 145 | * `apiEmail` - set the user's email used to authenticate the user with the Docker server. 146 | 147 | For example: 148 | 149 | ```gradle 150 | docker { 151 | useApi true 152 | hostUrl 'http://myserver:4243' 153 | apiUsername 'user' 154 | apiPassword 'password' 155 | apiEmail 'me@mycompany.com' 156 | } 157 | ``` 158 | 159 | 160 | ## Requirements 161 | * Gradle 2.x 162 | * Docker 0.11+ 163 | 164 | #### Note to Gradle 1.x users 165 | The plugin is built with Gradle 2.x and thus needs version 2.0 or higher to work due to a newer version of Groovy included in Gradle 2.x (2.3 vs. 1.8.6). To use the plugin with Gradle 1.x you have to add Groovy's upward compatibility patch by adding the following line to your build file: 166 | 167 | ```gradle 168 | buildscript { 169 | // ... 170 | dependencies { 171 | classpath 'se.transmode.gradle:gradle-docker:1.2' 172 | classpath 'org.codehaus.groovy:groovy-backports-compat23:2.3.5' 173 | } 174 | } 175 | ``` 176 | 177 | #### Note to native docker client users 178 | If you are not using Docker's remote API (`useApi = false`, i.e. the default behaviour) you need to have Docker installed locally in order to build images. However if the `dryRun` task property is set to `true` all calls to Docker are disabled. In that case only the Dockerfile and its context directory will be created. 179 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright 2013 Transmode AB 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | --------------------------------------------------------------------------------