Skip to content

Commit

Permalink
Standard labels
Browse files Browse the repository at this point in the history
* provide standard labels for Docker image #1

* perform docker login only if username is provided

* show exceptions from tests

* try to fix test failing on TravisCI

* disable problematic assertions

* different test of docker inspect output

* intentialy break the test

* fix the test

* more stable tests

* test the GitExecutor
  • Loading branch information
augi authored Feb 10, 2018
1 parent f90211e commit 6c785e7
Show file tree
Hide file tree
Showing 8 changed files with 170 additions and 9 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
[![Build Status](https://travis-ci.org/augi/gradle-docker-java.svg)](https://travis-ci.org/augi/gradle-docker-java) [ ![Download](https://api.bintray.com/packages/augi/maven/gradle-docker-java/images/download.svg) ](https://bintray.com/augi/maven/gradle-docker-java/_latestVersion)

Gradle plugin that wraps your JVM application to a new Docker image.
The image has [standard labels](http://label-schema.org/rc1/) derived from the build environment (environment variables, Git).

The plugin takes product of `distTar` task (added by [the application plugin](https://docs.gradle.org/current/userguide/application_plugin.html)) and wraps it to Docker image.

It takes product of `distTar` task (added by [the application plugin](https://docs.gradle.org/current/userguide/application_plugin.html)) and wraps it to Docker image.

Usage
=====
Expand Down
7 changes: 7 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ dependencies {

}

test {
testLogging {
events 'failed'
exceptionFormat 'full'
}
}

task sourcesJar(type: Jar) {
from sourceSets.main.allSource
classifier = 'sources'
Expand Down
57 changes: 57 additions & 0 deletions src/main/groovy/cz/augi/gradle/dockerjava/DistDockerTask.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import org.gradle.api.tasks.application.CreateStartScripts

import java.nio.file.Files
import java.nio.file.StandardCopyOption
import java.time.Clock

class DistDockerTask extends DefaultTask {
DistDockerTask() {
Expand All @@ -20,6 +21,8 @@ class DistDockerTask extends DefaultTask {

@Internal
DockerExecutor dockerExecutor
@Internal
GitExecutor gitExecutor
@Nested
DistDockerSettings settings

Expand All @@ -33,6 +36,7 @@ class DistDockerTask extends DefaultTask {
dockerFile << 'EXPOSE ' + settings.ports.join(' ') + '\n'
}
settings.volumes.each { dockerFile << "VOLUME $it\n" }
dockerFile << 'LABEL ' + getLabels().collect { "\"${it.key}\"=\"${it.value}\"" }.join(' ') + '\n'
settings.dockerfileLines.each { dockerFile << it + '\n' }
dockerFile << "ADD $tarFileName C:\n"
dockerFile << "WORKDIR C:\\\\$tarRootDirectory\\\\bin\n"
Expand All @@ -43,6 +47,7 @@ class DistDockerTask extends DefaultTask {
dockerFile << 'EXPOSE ' + settings.ports.join(' ') + '\n'
}
settings.volumes.each { dockerFile << "VOLUME $it\n" }
dockerFile << 'LABEL ' + getLabels().collect { "\"${it.key}\"=\"${it.value}\"" }.join(' ') + '\n'
settings.dockerfileLines.each { dockerFile << it + '\n' }
dockerFile << "ADD $tarFileName /var\n"
dockerFile << "WORKDIR /var/$tarRootDirectory/bin\n"
Expand Down Expand Up @@ -79,6 +84,58 @@ class DistDockerTask extends DefaultTask {
}
}

private Map<String, String> getLabels() {
def labels = ['org.label-schema.schema-version':'1.0']
labels.put('org.label-schema.build-date', Clock.systemUTC().instant().toString())
labels.put('org.label-schema.version', project.version.toString())
labels.put('org.label-schema.name', project.name)
if (project.description) {
labels.put('org.label-schema.description', project.description)
}
def url = getUrl()
if (url) labels.put('org.label-schema.url', url)
def vcsUrl = getVcsUrl()
if (vcsUrl) labels.put('org.label-schema.vcs-url', vcsUrl)
def vcsRef = getVcsRef()
if (vcsRef) labels.put('org.label-schema.vcs-ref', vcsRef)
labels.put('org.label-schema.docker.cmd', "docker run -d ${settings.ports.collect { "-p $it:$it" }.join(' ')} ${settings.volumes.collect { "-v $it:$it" }.join(' ')} ${settings.image}")
labels
}

// following environment variables that can be present in various environments
// * https://confluence.jetbrains.com/display/TCD10/Predefined+Build+Parameters
// * https://wiki.jenkins.io/display/JENKINS/Building+a+software+project#Buildingasoftwareproject-belowJenkinsSetEnvironmentVariables
// * https://docs.travis-ci.com/user/environment-variables/#Default-Environment-Variables
// * https://circleci.com/docs/1.0/environment-variables/
// * http://circleci.com/docs/2.0/env-vars/#build-details

private String getVcsUrl() {
def r = System.getenv('GIT_URL') ?: System.getenv('CIRCLE_REPOSITORY_URL')
if (r) return r
def slug = System.getenv('TRAVIS_PULL_REQUEST_SLUG') ?: System.getenv('TRAVIS_REPO_SLUG')
if (slug) return "https://github.com/$slug"
gitExecutor.getUrl()
}

private String getVcsRef() {
def r = System.getenv('GIT_COMMIT') ?: System.getenv('TRAVIS_COMMIT') ?: System.getenv('CIRCLE_SHA1')
if (r) return r
def fromTC = System.getenv().findAll { it.key.startsWith('BUILD_VCS_NUMBER') }.collect { it.value }.find()
if (fromTC) return fromTC
gitExecutor.getRef()
}

private String getVcsBranch() {
def r = System.getenv('GIT_BRANCH') ?: System.getenv('TRAVIS_PULL_REQUEST_BRANCH') ?: System.getenv('TRAVIS_BRANCH') ?: System.getenv('CIRCLE_BRANCH')
if (r) return r
gitExecutor.getBranch()
}

private String getUrl() {
def url = getVcsUrl()
if (url && url.startsWith('http')) url else null
}

@TaskAction
def create() {
assert settings.image : 'Image must be specified'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@ class DockerJavaPlugin implements Plugin<Project> {
project.plugins.apply 'application'

def dockerExecutor = new DockerExecutor(project)
def gitExecutor = new GitExecutor(project)
def extension = project.extensions.create('dockerJava', DockerJavaExtension, project, dockerExecutor)
def distDocker = project.tasks.create('distDocker', DistDockerTask)
def dockerPush = project.tasks.create('dockerPush', DockerPushTask)
distDocker.settings = extension
distDocker.dockerExecutor = dockerExecutor
distDocker.gitExecutor = gitExecutor
dockerPush.settings = extension
dockerPush.dockerExecutor = dockerExecutor

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,11 @@ class DockerPushTask extends DefaultTask {
assert settings.image : 'Image must be specified'
def configFile = new File(project.buildDir, 'localDockerConfig')
try {
project.exec {
it.commandLine 'docker', '--config', configFile.absolutePath, 'login', '-u', settings.username, '--password-stdin', settings.registry
it.standardInput = new ByteArrayInputStream((settings.password ?: '').getBytes(StandardCharsets.UTF_8))
if (settings.username) {
project.exec {
it.commandLine 'docker', '--config', configFile.absolutePath, 'login', '-u', settings.username, '--password-stdin', settings.registry
it.standardInput = new ByteArrayInputStream((settings.password ?: '').getBytes(StandardCharsets.UTF_8))
}
}
project.exec {
it.commandLine 'docker', '--config', configFile.absolutePath, 'push', settings.image
Expand Down
56 changes: 56 additions & 0 deletions src/main/groovy/cz/augi/gradle/dockerjava/GitExecutor.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package cz.augi.gradle.dockerjava

import org.gradle.api.Project
import org.gradle.api.logging.Logger
import org.gradle.platform.base.Platform
import org.gradle.process.ExecSpec

class GitExecutor {
private final Project project
private final Logger logger

GitExecutor(Project project) {
this.project = project
this.logger = project.logger
}

String execute(String... args) {
new ByteArrayOutputStream().withStream { os ->
project.exec { ExecSpec e ->
def finalArgs = ['git']
finalArgs.addAll(args)
e.commandLine finalArgs
e.standardOutput os
}
os.toString().trim()
}
}

String getUrl() {
try {
def remoteName = execute('remote').readLines().find()
remoteName ? execute('remote', 'get-url', remoteName) : null
} catch (RuntimeException e) {
logger.debug("Cannot get Git remote url: ${e.message}", e)
null
}
}

String getRef() {
try {
execute('rev-parse', 'HEAD')
} catch (RuntimeException e) {
logger.debug("Cannot get Git revision: ${e.message}", e)
null
}
}

String getBranch() {
try {
execute('rev-parse', '--abbrev-ref', 'HEAD')
} catch (RuntimeException e) {
logger.debug("Cannot get Git revision: ${e.message}", e)
null
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package cz.augi.gradle.dockerjava

import groovy.json.JsonSlurper
import org.gradle.testfixtures.ProjectBuilder
import org.gradle.testkit.runner.GradleRunner
import spock.lang.Specification
Expand All @@ -22,6 +23,7 @@ class DockerJavaPluginTest extends Specification {
}

def "builds a new image that correctly wraps simple Java application"() {
def dockerExecutor = new DockerExecutor(ProjectBuilder.builder().build())
def projectDir = File.createTempDir('test', 'gradleDockerJava')
new File(projectDir, 'build.gradle') << '''
plugins {
Expand All @@ -45,18 +47,24 @@ class DockerJavaPluginTest extends Specification {
}
'''
when:
def result = GradleRunner.create()
def gradleExecutionResult = GradleRunner.create()
.withProjectDir(projectDir)
.withArguments('distDocker', '-Pversion=1.2.3')
.withArguments('distDocker', '-Pversion=1.2.3', '-S')
.withPluginClasspath()
.build()
def dockerOutput = new DockerExecutor(ProjectBuilder.builder().build()).execute('run', '--rm', 'test/my-app:1.2.3')
def dockerRunOutput = dockerExecutor.execute('run', '--rm', 'test/my-app:1.2.3')
def dockerInspectOutput = dockerExecutor.execute('inspect', 'test/my-app:1.2.3')
def labels = new JsonSlurper().parseText(dockerInspectOutput)[0].Config.Labels
then:
!result.output.contains('FAILED')
dockerOutput.contains('Hello from Docker')
!gradleExecutionResult.output.contains('FAILED')
dockerRunOutput.contains('Hello from Docker')
labels.'org.label-schema.schema-version' == '1.0'
labels.'org.label-schema.version' == '1.2.3'
def workingDirectory = Paths.get(projectDir.absolutePath, 'build', 'dockerJava')
Files.exists(workingDirectory.resolve('Dockerfile'))
cleanup:
dockerExecutor.execute('rmi', 'test/my-app:1.2.3')
dockerExecutor.project.projectDir.deleteDir()
projectDir.deleteDir()
}

Expand Down
27 changes: 27 additions & 0 deletions src/test/groovy/cz/augi/gradle/dockerjava/GitExecutorTest.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package cz.augi.gradle.dockerjava

import org.gradle.testfixtures.ProjectBuilder
import spock.lang.Specification

class GitExecutorTest extends Specification {
def "reads url, ref and branch"() {
def project = ProjectBuilder.builder().build()
def target = new GitExecutor(project)
target.execute('init')
project.file('file.txt') << 'content'
target.execute('checkout', '-b', 'master')
target.execute('add', '.')
target.execute('commit', '-m', 'first commit')
target.execute('remote', 'add', 'origin', 'https://github.com/test/test')
when:
def url = target.url
def ref = target.ref
def branch = target.branch
then:
url == 'https://github.com/test/test'
ref.size() == 40
branch == 'master'
cleanup:
project.projectDir.deleteDir()
}
}

0 comments on commit 6c785e7

Please sign in to comment.