Inspect exec container
Remove obsolate logging handler
ADD
src/functTest/groovy/com/bmuschko/gradle/docker/DockerInspectExecContainerFunctionalTest.groovy
+package com.bmuschko.gradle.docker
+
+import org.gradle.testkit.runner.BuildResult
+import spock.lang.Requires
+
+/**
+ * System tests for {@link com.bmuschko.gradle.docker.tasks.container.DockerInspectExecContainer} class.
+ */
+@Requires({ TestPrecondition.DOCKER_SERVER_INFO_URL_REACHABLE })
+class DockerInspectExecContainerFunctionalTest extends AbstractFunctionalTest {
+
+ def "Inspect executed command within running container"() {
+ buildFile << """
+ import com.bmuschko.gradle.docker.tasks.image.DockerPullImage
+ import com.bmuschko.gradle.docker.tasks.container.DockerCreateContainer
+ import com.bmuschko.gradle.docker.tasks.container.DockerStartContainer
+ import com.bmuschko.gradle.docker.tasks.container.DockerExecContainer
+ import com.bmuschko.gradle.docker.tasks.container.DockerInspectExecContainer
+ import com.bmuschko.gradle.docker.tasks.container.DockerRemoveContainer
+
+ task pullImage(type: DockerPullImage) {
+ repository = 'alpine'
+ tag = '3.4'
+ }
+
+ task createContainer(type: DockerCreateContainer) {
+ dependsOn pullImage
+ targetImageId { pullImage.repository + ":" + pullImage.tag }
+ cmd = ['sleep','10']
+ }
+
+ task startContainer(type: DockerStartContainer) {
+ dependsOn createContainer
+ targetContainerId { createContainer.getContainerId() }
+ }
+
+ task execContainer(type: DockerExecContainer) {
+ dependsOn startContainer
+ targetContainerId { startContainer.getContainerId() }
+ cmd = ['touch', '/tmp/test.txt']
+ }
+
+ task inspectExec(type: DockerInspectExecContainer) {
+ dependsOn execContainer
+ targetExecId { execContainer.execId }
+ }
+
+ task removeContainer(type: DockerRemoveContainer) {
+ dependsOn inspectExec
+ removeVolumes = true
+ force = true
+ targetContainerId { startContainer.getContainerId() }
+ }
+
+ task workflow {
+ dependsOn removeContainer
+ }
+ """
+
+ expect:
+ BuildResult result = build('workflow')
+ result.output.contains("Exec ID: ")
+ }
+
+ def "Inspect exit code of executed command within running container"() {
+ buildFile << """
+ import com.bmuschko.gradle.docker.tasks.image.DockerPullImage
+ import com.bmuschko.gradle.docker.tasks.container.DockerCreateContainer
+ import com.bmuschko.gradle.docker.tasks.container.DockerStartContainer
+ import com.bmuschko.gradle.docker.tasks.container.DockerExecContainer
+ import com.bmuschko.gradle.docker.tasks.container.DockerInspectExecContainer
+ import com.bmuschko.gradle.docker.tasks.container.DockerRemoveContainer
+
+ task pullImage(type: DockerPullImage) {
+ repository = 'alpine'
+ tag = '3.4'
+ }
+
+ task createContainer(type: DockerCreateContainer) {
+ dependsOn pullImage
+ targetImageId { pullImage.repository + ":" + pullImage.tag }
+ cmd = ['sleep','10']
+ }
+
+ task startContainer(type: DockerStartContainer) {
+ dependsOn createContainer
+ targetContainerId { createContainer.getContainerId() }
+ }
+
+ task execContainer(type: DockerExecContainer) {
+ dependsOn startContainer
+ targetContainerId { startContainer.getContainerId() }
+ cmd = ['test', '-e', '/not_existing_file']
+ }
+
+ task inspectExec(type: DockerInspectExecContainer) {
+ dependsOn execContainer
+ targetExecId { execContainer.execId }
+ onNext { r ->
+ if(r.exitCode) {
+ throw new GradleException("Docker container exec failed with exit code: " + r.exitCode)
+ }
+ }
+ }
+
+ task removeContainer(type: DockerRemoveContainer) {
+ dependsOn inspectExec
+ removeVolumes = true
+ force = true
+ targetContainerId { startContainer.getContainerId() }
+ }
+
+ task workflow {
+ dependsOn removeContainer
+ }
+ """
+
+ expect:
+ BuildResult result = buildAndFail('workflow')
+ result.output.contains("Docker container exec failed with exit code: ")
+ }
+}

MODIFY
src/main/groovy/com/bmuschko/gradle/docker/tasks/container/DockerExecContainer.groovy
src/main/groovy/com/bmuschko/gradle/docker/tasks/container/DockerExecContainer.groovy
@@ -31,6 +31,11 @@ class DockerExecContainer extends DockerExistingContainer {
@Optional
Boolean attachStderr = true
+ String execId
+
+ DockerExecContainer() {
+ ext.getExecId = { execId }
+ }
@Override
void runRemoteCommand(dockerClient) {
@@ -38,7 +43,9 @@ class DockerExecContainer extends DockerExistingContainer {
def execCallback = onNext ? threadContextClassLoader.createExecCallback(onNext) : threadContextClassLoader.createExecCallback(System.out, System.err)
def execCmd = dockerClient.execCreateCmd(getContainerId())
setContainerCommandConfig(execCmd)
- dockerClient.execStartCmd(execCmd.exec().getId()).withDetach(false).exec(execCallback).awaitCompletion();
+ execId = execCmd.exec().getId()
+ logger.quiet "Exec ID '${execId}'."
+ dockerClient.execStartCmd(execId).withDetach(false).exec(execCallback).awaitCompletion()
}
private void setContainerCommandConfig(containerCommand) {

ADD
src/main/groovy/com/bmuschko/gradle/docker/tasks/container/DockerInspectExecContainer.groovy
+package com.bmuschko.gradle.docker.tasks.container
+
+import com.bmuschko.gradle.docker.tasks.AbstractDockerRemoteApiTask
+import org.gradle.api.tasks.Input
+
+/**
+ * Inspects task executed inside container
+ * with {@link DockerExecContainer} command.
+ */
+class DockerInspectExecContainer extends AbstractDockerRemoteApiTask {
+
+ /**
+ * Exec ID used to perform operation. The exec for the provided
+ * ID has to be created and started first.
+ */
+ String execId
+
+ void targetExecId(Closure execId) {
+ conventionMapping.execId = execId
+ }
+
+ @Input
+ String getExecId() {
+ execId
+ }
+
+ @Override
+ void runRemoteCommand(Object dockerClient) {
+ logger.quiet "Inspecting exec with ID '${getExecId()}'."
+ def result = dockerClient.inspectExecCmd(getExecId()).exec()
+ if (onNext) {
+ onNext.call(result)
+ } else {
+ logger.quiet("Exec ID: {}", result.id)
+ logger.quiet("Container ID: {}", result.containerID)
+ logger.quiet("Is running: {}", result.running)
+ logger.quiet("Exit code: {}", result.exitCode)
+ }
+ }
+}

Inspect exec container (#362)

Remove obsolate logging handler