diff --git a/docker-java-api/src/main/java/com/github/dockerjava/api/DockerClient.java b/docker-java-api/src/main/java/com/github/dockerjava/api/DockerClient.java index 441decad3..bf6acdee0 100644 --- a/docker-java-api/src/main/java/com/github/dockerjava/api/DockerClient.java +++ b/docker-java-api/src/main/java/com/github/dockerjava/api/DockerClient.java @@ -6,6 +6,7 @@ import com.github.dockerjava.api.command.CommitCmd; import com.github.dockerjava.api.command.ConnectToNetworkCmd; import com.github.dockerjava.api.command.ContainerDiffCmd; +import com.github.dockerjava.api.command.ExportContainerCmd; import com.github.dockerjava.api.command.CopyArchiveFromContainerCmd; import com.github.dockerjava.api.command.CopyArchiveToContainerCmd; import com.github.dockerjava.api.command.CopyFileFromContainerCmd; @@ -233,6 +234,15 @@ public interface DockerClient extends Closeable { ContainerDiffCmd containerDiffCmd(@Nonnull String containerId); + /** + * Export the contents of a container's filesystem as a tar archive. + * + * @param containerId + * id of the container + * @return created command + */ + ExportContainerCmd exportContainerCmd(@Nonnull String containerId); + StopContainerCmd stopContainerCmd(@Nonnull String containerId); KillContainerCmd killContainerCmd(@Nonnull String containerId); diff --git a/docker-java-api/src/main/java/com/github/dockerjava/api/DockerClientDelegate.java b/docker-java-api/src/main/java/com/github/dockerjava/api/DockerClientDelegate.java index fe1f72670..da600bd4d 100644 --- a/docker-java-api/src/main/java/com/github/dockerjava/api/DockerClientDelegate.java +++ b/docker-java-api/src/main/java/com/github/dockerjava/api/DockerClientDelegate.java @@ -6,6 +6,7 @@ import com.github.dockerjava.api.command.CommitCmd; import com.github.dockerjava.api.command.ConnectToNetworkCmd; import com.github.dockerjava.api.command.ContainerDiffCmd; +import com.github.dockerjava.api.command.ExportContainerCmd; import com.github.dockerjava.api.command.CopyArchiveFromContainerCmd; import com.github.dockerjava.api.command.CopyArchiveToContainerCmd; import com.github.dockerjava.api.command.CopyFileFromContainerCmd; @@ -276,6 +277,11 @@ public ContainerDiffCmd containerDiffCmd(@Nonnull String containerId) { return getDockerClient().containerDiffCmd(containerId); } + @Override + public ExportContainerCmd exportContainerCmd(@Nonnull String containerId) { + return getDockerClient().exportContainerCmd(containerId); + } + @Override public StopContainerCmd stopContainerCmd(@Nonnull String containerId) { return getDockerClient().stopContainerCmd(containerId); diff --git a/docker-java-api/src/main/java/com/github/dockerjava/api/command/DelegatingDockerCmdExecFactory.java b/docker-java-api/src/main/java/com/github/dockerjava/api/command/DelegatingDockerCmdExecFactory.java index 8f102a10e..d7cdd97a9 100644 --- a/docker-java-api/src/main/java/com/github/dockerjava/api/command/DelegatingDockerCmdExecFactory.java +++ b/docker-java-api/src/main/java/com/github/dockerjava/api/command/DelegatingDockerCmdExecFactory.java @@ -180,6 +180,11 @@ public ContainerDiffCmd.Exec createContainerDiffCmdExec() { return getDockerCmdExecFactory().createContainerDiffCmdExec(); } + @Override + public ExportContainerCmd.Exec createExportContainerCmdExec() { + return getDockerCmdExecFactory().createExportContainerCmdExec(); + } + @Override public KillContainerCmd.Exec createKillContainerCmdExec() { return getDockerCmdExecFactory().createKillContainerCmdExec(); diff --git a/docker-java-api/src/main/java/com/github/dockerjava/api/command/DockerCmdExecFactory.java b/docker-java-api/src/main/java/com/github/dockerjava/api/command/DockerCmdExecFactory.java index 29c001737..bdf39269d 100644 --- a/docker-java-api/src/main/java/com/github/dockerjava/api/command/DockerCmdExecFactory.java +++ b/docker-java-api/src/main/java/com/github/dockerjava/api/command/DockerCmdExecFactory.java @@ -73,6 +73,8 @@ public interface DockerCmdExecFactory extends Closeable { ContainerDiffCmd.Exec createContainerDiffCmdExec(); + ExportContainerCmd.Exec createExportContainerCmdExec(); + KillContainerCmd.Exec createKillContainerCmdExec(); UpdateContainerCmd.Exec createUpdateContainerCmdExec(); diff --git a/docker-java-api/src/main/java/com/github/dockerjava/api/command/ExportContainerCmd.java b/docker-java-api/src/main/java/com/github/dockerjava/api/command/ExportContainerCmd.java new file mode 100644 index 000000000..bef73d261 --- /dev/null +++ b/docker-java-api/src/main/java/com/github/dockerjava/api/command/ExportContainerCmd.java @@ -0,0 +1,31 @@ +package com.github.dockerjava.api.command; + +import java.io.InputStream; + +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; + +import com.github.dockerjava.api.exception.NotFoundException; + +/** + * Export the contents of a container as a tar archive. + */ +public interface ExportContainerCmd extends SyncDockerCmd { + + @CheckForNull + String getContainerId(); + + ExportContainerCmd withContainerId(@Nonnull String containerId); + + /** + * Its the responsibility of the caller to consume and/or close the {@link InputStream} to prevent connection leaks. + * + * @throws NotFoundException + * No such container + */ + @Override + InputStream exec() throws NotFoundException; + + interface Exec extends DockerCmdSyncExec { + } +} diff --git a/docker-java-core/src/main/java/com/github/dockerjava/core/AbstractDockerCmdExecFactory.java b/docker-java-core/src/main/java/com/github/dockerjava/core/AbstractDockerCmdExecFactory.java index 5b908dff4..9e1d71020 100644 --- a/docker-java-core/src/main/java/com/github/dockerjava/core/AbstractDockerCmdExecFactory.java +++ b/docker-java-core/src/main/java/com/github/dockerjava/core/AbstractDockerCmdExecFactory.java @@ -8,6 +8,7 @@ import com.github.dockerjava.api.command.CommitCmd; import com.github.dockerjava.api.command.ConnectToNetworkCmd; import com.github.dockerjava.api.command.ContainerDiffCmd; +import com.github.dockerjava.api.command.ExportContainerCmd; import com.github.dockerjava.api.command.CopyArchiveFromContainerCmd; import com.github.dockerjava.api.command.CopyArchiveToContainerCmd; import com.github.dockerjava.api.command.CopyFileFromContainerCmd; @@ -89,6 +90,7 @@ import com.github.dockerjava.core.exec.CommitCmdExec; import com.github.dockerjava.core.exec.ConnectToNetworkCmdExec; import com.github.dockerjava.core.exec.ContainerDiffCmdExec; +import com.github.dockerjava.core.exec.ExportContainerCmdExec; import com.github.dockerjava.core.exec.CopyArchiveFromContainerCmdExec; import com.github.dockerjava.core.exec.CopyArchiveToContainerCmdExec; import com.github.dockerjava.core.exec.CopyFileFromContainerCmdExec; @@ -368,6 +370,11 @@ public ContainerDiffCmd.Exec createContainerDiffCmdExec() { return new ContainerDiffCmdExec(getBaseResource(), getDockerClientConfig()); } + @Override + public ExportContainerCmd.Exec createExportContainerCmdExec() { + return new ExportContainerCmdExec(getBaseResource(), getDockerClientConfig()); + } + @Override public KillContainerCmd.Exec createKillContainerCmdExec() { return new KillContainerCmdExec(getBaseResource(), getDockerClientConfig()); diff --git a/docker-java-core/src/main/java/com/github/dockerjava/core/DockerClientImpl.java b/docker-java-core/src/main/java/com/github/dockerjava/core/DockerClientImpl.java index b17c1b481..a1ddc2897 100644 --- a/docker-java-core/src/main/java/com/github/dockerjava/core/DockerClientImpl.java +++ b/docker-java-core/src/main/java/com/github/dockerjava/core/DockerClientImpl.java @@ -7,6 +7,7 @@ import com.github.dockerjava.api.command.CommitCmd; import com.github.dockerjava.api.command.ConnectToNetworkCmd; import com.github.dockerjava.api.command.ContainerDiffCmd; +import com.github.dockerjava.api.command.ExportContainerCmd; import com.github.dockerjava.api.command.CopyArchiveFromContainerCmd; import com.github.dockerjava.api.command.CopyArchiveToContainerCmd; import com.github.dockerjava.api.command.CopyFileFromContainerCmd; @@ -93,6 +94,7 @@ import com.github.dockerjava.core.command.CommitCmdImpl; import com.github.dockerjava.core.command.ConnectToNetworkCmdImpl; import com.github.dockerjava.core.command.ContainerDiffCmdImpl; +import com.github.dockerjava.core.command.ExportContainerCmdImpl; import com.github.dockerjava.core.command.CopyArchiveFromContainerCmdImpl; import com.github.dockerjava.core.command.CopyArchiveToContainerCmdImpl; import com.github.dockerjava.core.command.CopyFileFromContainerCmdImpl; @@ -470,6 +472,11 @@ public ContainerDiffCmd containerDiffCmd(String containerId) { return new ContainerDiffCmdImpl(getDockerCmdExecFactory().createContainerDiffCmdExec(), containerId); } + @Override + public ExportContainerCmd exportContainerCmd(String containerId) { + return new ExportContainerCmdImpl(getDockerCmdExecFactory().createExportContainerCmdExec(), containerId); + } + @Override public StopContainerCmd stopContainerCmd(String containerId) { return new StopContainerCmdImpl(getDockerCmdExecFactory().createStopContainerCmdExec(), containerId); diff --git a/docker-java-core/src/main/java/com/github/dockerjava/core/command/ExportContainerCmdImpl.java b/docker-java-core/src/main/java/com/github/dockerjava/core/command/ExportContainerCmdImpl.java new file mode 100644 index 000000000..92070a2e2 --- /dev/null +++ b/docker-java-core/src/main/java/com/github/dockerjava/core/command/ExportContainerCmdImpl.java @@ -0,0 +1,41 @@ +package com.github.dockerjava.core.command; + +import java.io.InputStream; +import java.util.Objects; + +import com.github.dockerjava.api.command.ExportContainerCmd; +import com.github.dockerjava.api.exception.NotFoundException; + +/** + * Export the contents of a container as a tar archive. + */ +public class ExportContainerCmdImpl extends AbstrDockerCmd implements + ExportContainerCmd { + + private String containerId; + + public ExportContainerCmdImpl(ExportContainerCmd.Exec exec, String containerId) { + super(exec); + withContainerId(containerId); + } + + @Override + public String getContainerId() { + return containerId; + } + + @Override + public ExportContainerCmdImpl withContainerId(String containerId) { + this.containerId = Objects.requireNonNull(containerId, "containerId was not specified"); + return this; + } + + /** + * @throws NotFoundException + * No such container + */ + @Override + public InputStream exec() throws NotFoundException { + return super.exec(); + } +} diff --git a/docker-java-core/src/main/java/com/github/dockerjava/core/exec/ExportContainerCmdExec.java b/docker-java-core/src/main/java/com/github/dockerjava/core/exec/ExportContainerCmdExec.java new file mode 100644 index 000000000..9dab86f29 --- /dev/null +++ b/docker-java-core/src/main/java/com/github/dockerjava/core/exec/ExportContainerCmdExec.java @@ -0,0 +1,31 @@ +package com.github.dockerjava.core.exec; + +import java.io.InputStream; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.github.dockerjava.api.command.ExportContainerCmd; +import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.core.MediaType; +import com.github.dockerjava.core.WebTarget; + +public class ExportContainerCmdExec extends AbstrSyncDockerCmdExec + implements ExportContainerCmd.Exec { + + private static final Logger LOGGER = LoggerFactory.getLogger(ExportContainerCmdExec.class); + + public ExportContainerCmdExec(WebTarget baseResource, DockerClientConfig dockerClientConfig) { + super(baseResource, dockerClientConfig); + } + + @Override + protected InputStream execute(ExportContainerCmd command) { + WebTarget webResource = getBaseResource().path("/containers/{id}/export").resolveTemplate("id", + command.getContainerId()); + + LOGGER.trace("GET: {}", webResource); + + return webResource.request().accept(MediaType.APPLICATION_X_TAR).get(); + } +} diff --git a/docker-java/src/test/java/com/github/dockerjava/cmd/ExportContainerCmdIT.java b/docker-java/src/test/java/com/github/dockerjava/cmd/ExportContainerCmdIT.java new file mode 100644 index 000000000..8e4712d84 --- /dev/null +++ b/docker-java/src/test/java/com/github/dockerjava/cmd/ExportContainerCmdIT.java @@ -0,0 +1,52 @@ +package com.github.dockerjava.cmd; + +import com.github.dockerjava.api.command.CreateContainerResponse; +import org.apache.commons.compress.archivers.tar.TarArchiveEntry; +import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.InputStream; + +import static com.github.dockerjava.core.DockerRule.DEFAULT_IMAGE; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.emptyString; + +public class ExportContainerCmdIT extends CmdIT { + + private static final Logger LOG = LoggerFactory.getLogger(ExportContainerCmdIT.class); + + @Test + public void exportContainerHasCreatedFile() throws Exception { + CreateContainerResponse container = dockerRule.getClient().createContainerCmd(DEFAULT_IMAGE) + .withCmd("touch", "/myExportedFile") + .exec(); + + LOG.info("Created container: {}", container.toString()); + assertThat(container.getId(), not(is(emptyString()))); + + dockerRule.getClient().startContainerCmd(container.getId()).exec(); + + int exitCode = dockerRule.getClient().waitContainerCmd(container.getId()).start() + .awaitStatusCode(); + assertThat(exitCode, is(0)); + + try (InputStream response = dockerRule.getClient().exportContainerCmd(container.getId()).exec()) { + boolean foundFile = false; + try (TarArchiveInputStream tarStream = new TarArchiveInputStream(response)) { + TarArchiveEntry entry; + while ((entry = tarStream.getNextTarEntry()) != null) { + if (entry.getName().contains("myExportedFile")) { + foundFile = true; + break; + } + } + } + assertThat("Exported archive should contain the created file", foundFile, is(true)); + } + } + +}