diff --git a/src/main/java/com/github/dockerjava/api/command/BuildImageCmd.java b/src/main/java/com/github/dockerjava/api/command/BuildImageCmd.java index ec013d424..925946db4 100644 --- a/src/main/java/com/github/dockerjava/api/command/BuildImageCmd.java +++ b/src/main/java/com/github/dockerjava/api/command/BuildImageCmd.java @@ -1,12 +1,12 @@ package com.github.dockerjava.api.command; +import com.github.dockerjava.api.model.AuthConfigurations; +import com.github.dockerjava.api.model.EventStreamItem; + import java.io.File; import java.io.IOException; import java.io.InputStream; -import com.github.dockerjava.api.model.AuthConfigurations; -import com.github.dockerjava.api.model.EventStreamItem; - /** * * Build an image from Dockerfile. @@ -61,6 +61,9 @@ public interface BuildImageCmd extends DockerCmd{ public static interface Exec extends DockerCmdExec { } + /** + * @see {@link com.github.dockerjava.core.command.EventStreamReader} + */ public static abstract class Response extends InputStream { public abstract Iterable getItems() throws IOException; } diff --git a/src/main/java/com/github/dockerjava/api/command/PullImageCmd.java b/src/main/java/com/github/dockerjava/api/command/PullImageCmd.java index c39617dde..4b5fc4c32 100644 --- a/src/main/java/com/github/dockerjava/api/command/PullImageCmd.java +++ b/src/main/java/com/github/dockerjava/api/command/PullImageCmd.java @@ -1,6 +1,7 @@ package com.github.dockerjava.api.command; import com.github.dockerjava.api.model.AuthConfig; +import com.github.dockerjava.core.command.EventStreamReader; import java.io.InputStream; @@ -33,6 +34,8 @@ public static interface Exec extends DockerCmdExec { /** * Its the responsibility of the caller to consume and/or close the {@link InputStream} to prevent * connection leaks. + * + * @see {@link EventStreamReader} */ @Override public InputStream exec(); diff --git a/src/main/java/com/github/dockerjava/api/command/PushImageCmd.java b/src/main/java/com/github/dockerjava/api/command/PushImageCmd.java index 80e16c6d9..9ed38beb7 100644 --- a/src/main/java/com/github/dockerjava/api/command/PushImageCmd.java +++ b/src/main/java/com/github/dockerjava/api/command/PushImageCmd.java @@ -1,11 +1,12 @@ package com.github.dockerjava.api.command; -import java.io.IOException; -import java.io.InputStream; - import com.github.dockerjava.api.NotFoundException; import com.github.dockerjava.api.model.AuthConfig; import com.github.dockerjava.api.model.PushEventStreamItem; +import com.github.dockerjava.core.command.EventStreamReader; + +import java.io.IOException; +import java.io.InputStream; /** * Push the latest image to the repository. @@ -40,6 +41,9 @@ public interface PushImageCmd extends DockerCmd{ public static interface Exec extends DockerCmdExec { } + /** + * @see {@link EventStreamReader} + */ public static abstract class Response extends InputStream { public abstract Iterable getItems() throws IOException; } diff --git a/src/main/java/com/github/dockerjava/api/model/PullEventStreamItem.java b/src/main/java/com/github/dockerjava/api/model/PullEventStreamItem.java new file mode 100644 index 000000000..4a3a0b507 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/model/PullEventStreamItem.java @@ -0,0 +1,59 @@ +package com.github.dockerjava.api.model; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.base.Objects; + +import java.io.Serializable; + +/** + * Represents an item returned from pull + */ +@JsonIgnoreProperties(ignoreUnknown=true) +public class PullEventStreamItem implements Serializable { + + private static final long serialVersionUID = -5187169652557467828L; + + @JsonProperty("status") + private String status; + + @JsonProperty("progress") + private String progress; + + @JsonProperty("progressDetail") + private ProgressDetail progressDetail; + + + public String getStatus() { + return status; + } + + public String getProgress() { + return progress; + } + + public ProgressDetail getProgressDetail() { + return progressDetail; + } + + @JsonIgnoreProperties(ignoreUnknown=true) + public static class ProgressDetail implements Serializable { + @JsonProperty("current") + int current; + + + @Override + public String toString() { + return "current " + current; + } + } + + @Override + public String toString() { + return Objects.toStringHelper(this) + .add("status", status) + .add("progress", progress) + .add("progressDetail", progressDetail) + .toString(); + } +} diff --git a/src/main/java/com/github/dockerjava/core/command/EventStreamReader.java b/src/main/java/com/github/dockerjava/core/command/EventStreamReader.java new file mode 100644 index 000000000..b8975224f --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/command/EventStreamReader.java @@ -0,0 +1,35 @@ +package com.github.dockerjava.core.command; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.io.IOException; +import java.io.InputStream; + +public class EventStreamReader implements AutoCloseable { + + private final ObjectMapper objectMapper = new ObjectMapper(); + private final Class type; + private final InputStream inputStream; + + public EventStreamReader(InputStream inputStream, Class type) { + this.inputStream = inputStream; + this.type = type; + } + + public I readItem() throws IOException { + try { + return objectMapper.readValue(inputStream, type); + } catch (IOException e) { + // dirty, but works + if (e.getMessage().equals("Stream closed")) { + return null; + } + throw e; + } + } + + @Override + public void close() throws IOException { + inputStream.close(); + } +} diff --git a/src/test/java/com/github/dockerjava/core/command/DockerfileFixture.java b/src/test/java/com/github/dockerjava/core/command/DockerfileFixture.java index 2d7ca7c8d..840f63da4 100644 --- a/src/test/java/com/github/dockerjava/core/command/DockerfileFixture.java +++ b/src/test/java/com/github/dockerjava/core/command/DockerfileFixture.java @@ -8,6 +8,7 @@ import org.slf4j.LoggerFactory; import java.io.File; +import java.io.IOException; /** * Start and stop a single container for testing. @@ -25,7 +26,7 @@ public DockerfileFixture(DockerClient dockerClient, String directory) { this.directory = directory; } - public void open() throws Exception { + public void open() throws IOException { LOGGER.info("building {}", directory); dockerClient @@ -79,7 +80,7 @@ public void close() throws Exception { .removeImageCmd(repository) .withForce() .exec(); - } catch (InternalServerErrorException e) { + } catch (NotFoundException | InternalServerErrorException e) { LOGGER.info("ignoring {}", e.getMessage()); } repository = null; diff --git a/src/test/java/com/github/dockerjava/core/command/EventStreamReaderITest.java b/src/test/java/com/github/dockerjava/core/command/EventStreamReaderITest.java new file mode 100644 index 000000000..dfc330d9e --- /dev/null +++ b/src/test/java/com/github/dockerjava/core/command/EventStreamReaderITest.java @@ -0,0 +1,72 @@ +package com.github.dockerjava.core.command; + +import com.github.dockerjava.api.DockerClient; +import com.github.dockerjava.api.model.EventStreamItem; +import com.github.dockerjava.api.model.PullEventStreamItem; +import com.github.dockerjava.core.DockerClientBuilder; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import java.io.File; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.beans.HasPropertyWithValue.hasProperty; +import static org.hamcrest.core.AllOf.allOf; +import static org.hamcrest.core.IsEqual.equalTo; +import static org.hamcrest.core.IsNull.nullValue; +import static org.testng.AssertJUnit.assertNull; + + +@Test(groups = "integration") +public class EventStreamReaderITest { + + private DockerClient dockerClient; + + @BeforeTest + public void setUp() throws Exception { + dockerClient = DockerClientBuilder.getInstance().build(); + } + + @AfterTest + public void tearDown() throws Exception { + dockerClient.close(); + } + + @Test + public void pullCanBeStreamed() throws Exception { + + try (EventStreamReader reader = new EventStreamReader<>( + dockerClient.pullImageCmd("busybox:latest").exec(), + PullEventStreamItem.class) + ) {; + assertThat(reader.readItem(), + allOf( + hasProperty("status", equalTo("Pulling repository busybox")), + hasProperty("progress", nullValue()), + hasProperty("progressDetail", nullValue()) + ) + ); + assertNull(reader.readItem()); + } + } + + @Test + public void buildCanBeStreamed() throws Exception { + + try (EventStreamReader reader = new EventStreamReader<>( + dockerClient.buildImageCmd(new File("src/test/resources/eventStreamReaderDockerfile")).exec(), + EventStreamItem.class) + ) { + assertThat(reader.readItem(), + allOf( + hasProperty("stream", equalTo("Step 0 : FROM busybox:latest\n")), + hasProperty("error", nullValue()), + hasProperty("errorDetail", nullValue()) + ) + ); + assertNull(reader.readItem()); + + } + } +} \ No newline at end of file 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 4654d83dd..0cf08e953 100644 --- a/src/test/java/com/github/dockerjava/core/command/FrameReaderITest.java +++ b/src/test/java/com/github/dockerjava/core/command/FrameReaderITest.java @@ -1,44 +1,37 @@ package com.github.dockerjava.core.command; +import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.model.Frame; import com.github.dockerjava.api.model.StreamType; -import com.github.dockerjava.client.AbstractDockerClientTest; -import org.testng.annotations.AfterMethod; +import com.github.dockerjava.core.DockerClientBuilder; import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeMethod; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; import java.io.IOException; import java.io.InputStream; +import static org.testng.Assert.assertEquals; +import static org.testng.AssertJUnit.assertNull; + @Test(groups = "integration") -public class FrameReaderITest extends AbstractDockerClientTest { +public class FrameReaderITest { + private DockerClient dockerClient; private DockerfileFixture dockerfileFixture; @BeforeTest - @Override - public void beforeTest() { - super.beforeTest(); + public void beforeTest() throws Exception { + dockerClient = DockerClientBuilder.getInstance().build(); dockerfileFixture = new DockerfileFixture(dockerClient, "frameReaderDockerfile"); - } - - @BeforeMethod - public void createAndStartDockerContainer() throws Exception { dockerfileFixture.open(); } - @AfterMethod + @AfterTest public void deleteDockerContainerImage() throws Exception { dockerfileFixture.close(); - } - - @AfterTest - @Override - public void afterTest() { - super.afterTest(); + dockerClient.close(); } @Test diff --git a/src/test/resources/eventStreamReaderDockerfile/Dockerfile b/src/test/resources/eventStreamReaderDockerfile/Dockerfile new file mode 100644 index 000000000..cdd3bba79 --- /dev/null +++ b/src/test/resources/eventStreamReaderDockerfile/Dockerfile @@ -0,0 +1,5 @@ +FROM busybox:latest + +RUN true + +CMD ["true"] \ No newline at end of file