From 7a15817834662213b59f9c45c2b1618ea0fdb184 Mon Sep 17 00:00:00 2001 From: Marcus Linke Date: Wed, 4 Nov 2015 12:22:59 +0100 Subject: [PATCH 1/2] Fix issue #348 --- .../api/command/WaitContainerCmd.java | 9 ++- .../dockerjava/api/model/WaitResponse.java | 19 +++++ .../core/async/ResultCallbackTemplate.java | 19 ++++- .../core/command/WaitContainerCmdImpl.java | 5 +- .../command/WaitContainerResultCallback.java | 75 +++++++++++++++++++ .../jaxrs/WaitContainerCmdExec.java | 27 ++++--- .../dockerjava/client/DockerClientTest.java | 3 +- .../core/command/BuildImageCmdImplTest.java | 2 +- .../command/ContainerDiffCmdImplTest.java | 2 +- .../core/command/FrameReaderITest.java | 3 +- .../core/command/LogContainerCmdImplTest.java | 12 +-- .../command/RemoveContainerCmdImplTest.java | 2 +- .../command/StartContainerCmdImplTest.java | 2 +- .../command/WaitContainerCmdImplTest.java | 43 +++++++++-- 14 files changed, 190 insertions(+), 33 deletions(-) create mode 100644 src/main/java/com/github/dockerjava/api/model/WaitResponse.java create mode 100644 src/main/java/com/github/dockerjava/core/command/WaitContainerResultCallback.java diff --git a/src/main/java/com/github/dockerjava/api/command/WaitContainerCmd.java b/src/main/java/com/github/dockerjava/api/command/WaitContainerCmd.java index 2accc3af9..f92ca655a 100644 --- a/src/main/java/com/github/dockerjava/api/command/WaitContainerCmd.java +++ b/src/main/java/com/github/dockerjava/api/command/WaitContainerCmd.java @@ -1,13 +1,16 @@ package com.github.dockerjava.api.command; import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.api.async.ResultCallback; +import com.github.dockerjava.api.model.BuildResponseItem; +import com.github.dockerjava.api.model.WaitResponse; /** * Wait a container * * Block until container stops, then returns its exit code */ -public interface WaitContainerCmd extends SyncDockerCmd { +public interface WaitContainerCmd extends AsyncDockerCmd { public String getContainerId(); @@ -18,9 +21,9 @@ public interface WaitContainerCmd extends SyncDockerCmd { * container not found */ @Override - public Integer exec() throws NotFoundException; + public > T exec(T resultCallback); - public static interface Exec extends DockerCmdSyncExec { + public static interface Exec extends DockerCmdAsyncExec { } } \ No newline at end of file diff --git a/src/main/java/com/github/dockerjava/api/model/WaitResponse.java b/src/main/java/com/github/dockerjava/api/model/WaitResponse.java new file mode 100644 index 000000000..578e44f1e --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/model/WaitResponse.java @@ -0,0 +1,19 @@ +package com.github.dockerjava.api.model; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + + +/** + * Represents a wait container command response + */ +@JsonIgnoreProperties(ignoreUnknown = false) +public class WaitResponse { + + @JsonProperty("StatusCode") + private Integer statusCode; + + public Integer getStatusCode() { + return statusCode; + } +} diff --git a/src/main/java/com/github/dockerjava/core/async/ResultCallbackTemplate.java b/src/main/java/com/github/dockerjava/core/async/ResultCallbackTemplate.java index b5387ede0..5ca32c3b7 100644 --- a/src/main/java/com/github/dockerjava/core/async/ResultCallbackTemplate.java +++ b/src/main/java/com/github/dockerjava/core/async/ResultCallbackTemplate.java @@ -12,6 +12,7 @@ import org.slf4j.LoggerFactory; import com.github.dockerjava.api.async.ResultCallback; +import com.google.common.base.Throwables; /** * Abstract template implementation of {@link ResultCallback} @@ -30,6 +31,8 @@ public abstract class ResultCallbackTemplate implements WaitContainerCmd { +public class WaitContainerCmdImpl extends AbstrAsyncDockerCmd implements WaitContainerCmd { private String containerId; diff --git a/src/main/java/com/github/dockerjava/core/command/WaitContainerResultCallback.java b/src/main/java/com/github/dockerjava/core/command/WaitContainerResultCallback.java new file mode 100644 index 000000000..48f2749dc --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/command/WaitContainerResultCallback.java @@ -0,0 +1,75 @@ +/* + * Created on 21.07.2015 + */ +package com.github.dockerjava.core.command; + +import java.util.concurrent.TimeUnit; + +import javax.annotation.CheckForNull; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.github.dockerjava.api.DockerClientException; +import com.github.dockerjava.api.model.WaitResponse; +import com.github.dockerjava.core.async.ResultCallbackTemplate; +import com.google.common.base.Throwables; + +/** + * + * @author marcus + * + */ +public class WaitContainerResultCallback extends ResultCallbackTemplate { + + private final static Logger LOGGER = LoggerFactory.getLogger(WaitContainerResultCallback.class); + + @CheckForNull + private WaitResponse waitResponse = null; + + @Override + public void onNext(WaitResponse waitResponse) { + this.waitResponse = waitResponse; + LOGGER.debug(waitResponse.toString()); + } + + /** + * Awaits the status code from the container. + * + * @throws DockerClientException + * if the wait operation fails. + */ + public Integer awaitStatusCode() { + try { + awaitCompletion(); + } catch (InterruptedException e) { + throw new DockerClientException("", e); + } + + return getStatusCode(); + } + + /** + * Awaits the status code from the container. + * + * @throws DockerClientException + * if the wait operation fails. + */ + public Integer awaitStatusCode(long timeout, TimeUnit timeUnit) { + try { + awaitCompletion(timeout, timeUnit); + } catch (InterruptedException e) { + throw new DockerClientException("Awaiting status code interrupted: ", e); + } + + return getStatusCode(); + } + + private Integer getStatusCode() { + if (waitResponse == null) { + throw new DockerClientException("Error while wait container"); + } else { + return waitResponse.getStatusCode(); + } + } +} diff --git a/src/main/java/com/github/dockerjava/jaxrs/WaitContainerCmdExec.java b/src/main/java/com/github/dockerjava/jaxrs/WaitContainerCmdExec.java index 16dab933d..7a91a8b4f 100644 --- a/src/main/java/com/github/dockerjava/jaxrs/WaitContainerCmdExec.java +++ b/src/main/java/com/github/dockerjava/jaxrs/WaitContainerCmdExec.java @@ -1,15 +1,22 @@ package com.github.dockerjava.jaxrs; -import com.fasterxml.jackson.databind.node.ObjectNode; -import com.github.dockerjava.api.command.WaitContainerCmd; -import com.github.dockerjava.core.DockerClientConfig; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import static javax.ws.rs.client.Entity.entity; import javax.ws.rs.client.WebTarget; import javax.ws.rs.core.MediaType; -public class WaitContainerCmdExec extends AbstrSyncDockerCmdExec implements +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.github.dockerjava.api.async.ResultCallback; +import com.github.dockerjava.api.command.WaitContainerCmd; +import com.github.dockerjava.api.model.WaitResponse; +import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.core.async.JsonStreamProcessor; +import com.github.dockerjava.jaxrs.async.AbstractCallbackNotifier; +import com.github.dockerjava.jaxrs.async.POSTCallbackNotifier; + +public class WaitContainerCmdExec extends AbstrAsyncDockerCmdExec implements WaitContainerCmd.Exec { private static final Logger LOGGER = LoggerFactory.getLogger(WaitContainerCmdExec.class); @@ -19,14 +26,16 @@ public WaitContainerCmdExec(WebTarget baseResource, DockerClientConfig dockerCli } @Override - protected Integer execute(WaitContainerCmd command) { + protected AbstractCallbackNotifier callbackNotifier(WaitContainerCmd command, + ResultCallback resultCallback) { + WebTarget webResource = getBaseResource().path("/containers/{id}/wait").resolveTemplate("id", command.getContainerId()); LOGGER.trace("POST: {}", webResource); - ObjectNode ObjectNode = webResource.request().accept(MediaType.APPLICATION_JSON).post(null, ObjectNode.class); - return ObjectNode.get("StatusCode").asInt(); + return new POSTCallbackNotifier(new JsonStreamProcessor( + WaitResponse.class), resultCallback, webResource.request().accept(MediaType.APPLICATION_JSON), entity(null, MediaType.APPLICATION_JSON)); } } diff --git a/src/test/java/com/github/dockerjava/client/DockerClientTest.java b/src/test/java/com/github/dockerjava/client/DockerClientTest.java index 0141e01ac..73cf49571 100644 --- a/src/test/java/com/github/dockerjava/client/DockerClientTest.java +++ b/src/test/java/com/github/dockerjava/client/DockerClientTest.java @@ -16,6 +16,7 @@ import com.github.dockerjava.api.DockerException; import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.core.command.WaitContainerResultCallback; /** * Unit test for DockerClient. @@ -61,7 +62,7 @@ public void testRunShlex() throws DockerException { CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd(commands).exec(); dockerClient.startContainerCmd(container.getId()); - int exitcode = dockerClient.waitContainerCmd(container.getId()).exec(); + int exitcode = dockerClient.waitContainerCmd(container.getId()).exec(new WaitContainerResultCallback()).awaitStatusCode(); assertThat(exitcode, equalTo(0)); } } diff --git a/src/test/java/com/github/dockerjava/core/command/BuildImageCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/BuildImageCmdImplTest.java index 22030cc9f..6f4f0e3f4 100644 --- a/src/test/java/com/github/dockerjava/core/command/BuildImageCmdImplTest.java +++ b/src/test/java/com/github/dockerjava/core/command/BuildImageCmdImplTest.java @@ -158,7 +158,7 @@ private String execBuild(BuildImageCmd buildImageCmd) throws Exception { assertThat(container.getId(), not(isEmptyString())); dockerClient.startContainerCmd(container.getId()).exec(); - dockerClient.waitContainerCmd(container.getId()).exec(); + dockerClient.waitContainerCmd(container.getId()).exec(new WaitContainerResultCallback()).awaitStatusCode(); return containerLog(container.getId()); } diff --git a/src/test/java/com/github/dockerjava/core/command/ContainerDiffCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/ContainerDiffCmdImplTest.java index 3db2b7282..f7602ff5a 100644 --- a/src/test/java/com/github/dockerjava/core/command/ContainerDiffCmdImplTest.java +++ b/src/test/java/com/github/dockerjava/core/command/ContainerDiffCmdImplTest.java @@ -53,7 +53,7 @@ public void testContainerDiff() throws DockerException { assertThat(container.getId(), not(isEmptyString())); dockerClient.startContainerCmd(container.getId()).exec(); - int exitCode = dockerClient.waitContainerCmd(container.getId()).exec(); + int exitCode = dockerClient.waitContainerCmd(container.getId()).exec(new WaitContainerResultCallback()).awaitStatusCode(); assertThat(exitCode, equalTo(0)); List filesystemDiff = dockerClient.containerDiffCmd(container.getId()).exec(); diff --git a/src/test/java/com/github/dockerjava/core/command/FrameReaderITest.java b/src/test/java/com/github/dockerjava/core/command/FrameReaderITest.java index 5b1df2241..4ca4f7b5b 100644 --- a/src/test/java/com/github/dockerjava/core/command/FrameReaderITest.java +++ b/src/test/java/com/github/dockerjava/core/command/FrameReaderITest.java @@ -41,7 +41,8 @@ public void deleteDockerContainerImage() throws Exception { public void canCloseFrameReaderAndReadExpectedLines() throws Exception { // wait for the container to be successfully executed - int exitCode = dockerClient.waitContainerCmd(dockerfileFixture.getContainerId()).exec(); + int exitCode = dockerClient.waitContainerCmd(dockerfileFixture.getContainerId()) + .exec(new WaitContainerResultCallback()).awaitStatusCode(); assertEquals(0, exitCode); Iterator response = getLoggingFrames().iterator(); diff --git a/src/test/java/com/github/dockerjava/core/command/LogContainerCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/LogContainerCmdImplTest.java index c3f6ab01c..638cbdc4c 100644 --- a/src/test/java/com/github/dockerjava/core/command/LogContainerCmdImplTest.java +++ b/src/test/java/com/github/dockerjava/core/command/LogContainerCmdImplTest.java @@ -17,7 +17,6 @@ import com.github.dockerjava.api.NotFoundException; import com.github.dockerjava.api.command.CreateContainerResponse; -import com.github.dockerjava.api.model.Frame; import com.github.dockerjava.client.AbstractDockerClientTest; @Test(groups = "integration") @@ -56,7 +55,8 @@ public void asyncLogContainer() throws Exception { dockerClient.startContainerCmd(container.getId()).exec(); - int exitCode = dockerClient.waitContainerCmd(container.getId()).exec(); + int exitCode = dockerClient.waitContainerCmd(container.getId()).exec(new WaitContainerResultCallback()) + .awaitStatusCode(); assertThat(exitCode, equalTo(0)); @@ -80,7 +80,7 @@ public void onError(Throwable throwable) { assertEquals(throwable.getClass().getName(), NotFoundException.class.getName()); try { - // close the callback to prevent the call to onFinish + // close the callback to prevent the call to onComplete close(); } catch (IOException e) { throw new RuntimeException(); @@ -111,7 +111,8 @@ public void asyncMultipleLogContainer() throws Exception { dockerClient.startContainerCmd(container.getId()).exec(); - int exitCode = dockerClient.waitContainerCmd(container.getId()).exec(); + int exitCode = dockerClient.waitContainerCmd(container.getId()).exec(new WaitContainerResultCallback()) + .awaitStatusCode(); assertThat(exitCode, equalTo(0)); @@ -150,7 +151,8 @@ public void asyncLogContainerWithSince() throws Exception { dockerClient.startContainerCmd(container.getId()).exec(); - int exitCode = dockerClient.waitContainerCmd(container.getId()).exec(); + int exitCode = dockerClient.waitContainerCmd(container.getId()).exec(new WaitContainerResultCallback()) + .awaitStatusCode(); assertThat(exitCode, equalTo(0)); diff --git a/src/test/java/com/github/dockerjava/core/command/RemoveContainerCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/RemoveContainerCmdImplTest.java index 3241f0739..1b39e5ac0 100644 --- a/src/test/java/com/github/dockerjava/core/command/RemoveContainerCmdImplTest.java +++ b/src/test/java/com/github/dockerjava/core/command/RemoveContainerCmdImplTest.java @@ -56,7 +56,7 @@ public void removeContainer() throws DockerException { CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("true").exec(); dockerClient.startContainerCmd(container.getId()).exec(); - dockerClient.waitContainerCmd(container.getId()).exec(); + dockerClient.waitContainerCmd(container.getId()).exec(new WaitContainerResultCallback()).awaitStatusCode(); LOG.info("Removing container: {}", container.getId()); dockerClient.removeContainerCmd(container.getId()).exec(); diff --git a/src/test/java/com/github/dockerjava/core/command/StartContainerCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/StartContainerCmdImplTest.java index ae323c4c0..560d8cbca 100644 --- a/src/test/java/com/github/dockerjava/core/command/StartContainerCmdImplTest.java +++ b/src/test/java/com/github/dockerjava/core/command/StartContainerCmdImplTest.java @@ -87,7 +87,7 @@ public void startContainerWithVolumes() throws DockerException { dockerClient.startContainerCmd(container.getId()).exec(); - dockerClient.waitContainerCmd(container.getId()).exec(); + dockerClient.waitContainerCmd(container.getId()).exec(new WaitContainerResultCallback()).awaitStatusCode(); inspectContainerResponse = dockerClient.inspectContainerCmd(container.getId()).exec(); diff --git a/src/test/java/com/github/dockerjava/core/command/WaitContainerCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/WaitContainerCmdImplTest.java index 3b5172b9e..d3b38c4e1 100644 --- a/src/test/java/com/github/dockerjava/core/command/WaitContainerCmdImplTest.java +++ b/src/test/java/com/github/dockerjava/core/command/WaitContainerCmdImplTest.java @@ -19,6 +19,7 @@ import com.github.dockerjava.api.NotFoundException; import com.github.dockerjava.api.command.CreateContainerResponse; import com.github.dockerjava.api.command.InspectContainerResponse; +import com.github.dockerjava.api.model.WaitResponse; import com.github.dockerjava.client.AbstractDockerClientTest; @Test(groups = "integration") @@ -54,7 +55,8 @@ public void testWaitContainer() throws DockerException { dockerClient.startContainerCmd(container.getId()).exec(); - int exitCode = dockerClient.waitContainerCmd(container.getId()).exec(); + int exitCode = dockerClient.waitContainerCmd(container.getId()).exec(new WaitContainerResultCallback()) + .awaitStatusCode(); LOG.info("Container exit code: {}", exitCode); assertThat(exitCode, equalTo(0)); @@ -66,12 +68,39 @@ public void testWaitContainer() throws DockerException { assertThat(inspectContainerResponse.getState().getExitCode(), is(equalTo(exitCode))); } - @Test + @Test(expectedExceptions = NotFoundException.class) public void testWaitNonExistingContainer() throws DockerException { - try { - dockerClient.waitContainerCmd("non-existing").exec(); - fail("expected NotFoundException"); - } catch (NotFoundException e) { - } + + WaitContainerResultCallback callback = new WaitContainerResultCallback() { + public void onNext(WaitResponse waitResponse) { + fail("expected NotFoundException"); + }; + }; + + dockerClient.waitContainerCmd("non-existing").exec(callback).awaitStatusCode(); + } + + @Test + public void testWaitContainerAbort() throws Exception { + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("sleep", "9999").exec(); + + LOG.info("Created container: {}", container.toString()); + assertThat(container.getId(), not(isEmptyString())); + + dockerClient.startContainerCmd(container.getId()).exec(); + + WaitContainerResultCallback callback = dockerClient.waitContainerCmd(container.getId()).exec(new WaitContainerResultCallback()); + + Thread.sleep(5000); + + callback.close(); + + dockerClient.killContainerCmd(container.getId()).exec(); + + InspectContainerResponse inspectContainerResponse = dockerClient.inspectContainerCmd(container.getId()).exec(); + LOG.info("Container Inspect: {}", inspectContainerResponse.toString()); + + assertThat(inspectContainerResponse.getState().isRunning(), is(equalTo(false))); } } From d25eea146681314d7178567a5777167335c9dd31 Mon Sep 17 00:00:00 2001 From: Marcus Linke Date: Wed, 4 Nov 2015 14:27:51 +0100 Subject: [PATCH 2/2] Added @CheckForNull annotation --- .../github/dockerjava/core/async/ResultCallbackTemplate.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/com/github/dockerjava/core/async/ResultCallbackTemplate.java b/src/main/java/com/github/dockerjava/core/async/ResultCallbackTemplate.java index 5ca32c3b7..44af625af 100644 --- a/src/main/java/com/github/dockerjava/core/async/ResultCallbackTemplate.java +++ b/src/main/java/com/github/dockerjava/core/async/ResultCallbackTemplate.java @@ -8,6 +8,8 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import javax.annotation.CheckForNull; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -96,6 +98,7 @@ public RC_T awaitCompletion(long timeout, TimeUnit timeUnit) throws InterruptedE return (RC_T) this; } + @CheckForNull protected RuntimeException getFirstError() { if (firstError != null) { // this call throws a RuntimeException