Skip to content

Commit a3cb696

Browse files
authored
Add support to export container (#2594)
Fixes #2319
1 parent c7f0c0b commit a3cb696

10 files changed

Lines changed: 192 additions & 0 deletions

File tree

docker-java-api/src/main/java/com/github/dockerjava/api/DockerClient.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import com.github.dockerjava.api.command.CommitCmd;
77
import com.github.dockerjava.api.command.ConnectToNetworkCmd;
88
import com.github.dockerjava.api.command.ContainerDiffCmd;
9+
import com.github.dockerjava.api.command.ExportContainerCmd;
910
import com.github.dockerjava.api.command.CopyArchiveFromContainerCmd;
1011
import com.github.dockerjava.api.command.CopyArchiveToContainerCmd;
1112
import com.github.dockerjava.api.command.CopyFileFromContainerCmd;
@@ -233,6 +234,15 @@ public interface DockerClient extends Closeable {
233234

234235
ContainerDiffCmd containerDiffCmd(@Nonnull String containerId);
235236

237+
/**
238+
* Export the contents of a container's filesystem as a tar archive.
239+
*
240+
* @param containerId
241+
* id of the container
242+
* @return created command
243+
*/
244+
ExportContainerCmd exportContainerCmd(@Nonnull String containerId);
245+
236246
StopContainerCmd stopContainerCmd(@Nonnull String containerId);
237247

238248
KillContainerCmd killContainerCmd(@Nonnull String containerId);

docker-java-api/src/main/java/com/github/dockerjava/api/DockerClientDelegate.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import com.github.dockerjava.api.command.CommitCmd;
77
import com.github.dockerjava.api.command.ConnectToNetworkCmd;
88
import com.github.dockerjava.api.command.ContainerDiffCmd;
9+
import com.github.dockerjava.api.command.ExportContainerCmd;
910
import com.github.dockerjava.api.command.CopyArchiveFromContainerCmd;
1011
import com.github.dockerjava.api.command.CopyArchiveToContainerCmd;
1112
import com.github.dockerjava.api.command.CopyFileFromContainerCmd;
@@ -276,6 +277,11 @@ public ContainerDiffCmd containerDiffCmd(@Nonnull String containerId) {
276277
return getDockerClient().containerDiffCmd(containerId);
277278
}
278279

280+
@Override
281+
public ExportContainerCmd exportContainerCmd(@Nonnull String containerId) {
282+
return getDockerClient().exportContainerCmd(containerId);
283+
}
284+
279285
@Override
280286
public StopContainerCmd stopContainerCmd(@Nonnull String containerId) {
281287
return getDockerClient().stopContainerCmd(containerId);

docker-java-api/src/main/java/com/github/dockerjava/api/command/DelegatingDockerCmdExecFactory.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,11 @@ public ContainerDiffCmd.Exec createContainerDiffCmdExec() {
180180
return getDockerCmdExecFactory().createContainerDiffCmdExec();
181181
}
182182

183+
@Override
184+
public ExportContainerCmd.Exec createExportContainerCmdExec() {
185+
return getDockerCmdExecFactory().createExportContainerCmdExec();
186+
}
187+
183188
@Override
184189
public KillContainerCmd.Exec createKillContainerCmdExec() {
185190
return getDockerCmdExecFactory().createKillContainerCmdExec();

docker-java-api/src/main/java/com/github/dockerjava/api/command/DockerCmdExecFactory.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ public interface DockerCmdExecFactory extends Closeable {
7373

7474
ContainerDiffCmd.Exec createContainerDiffCmdExec();
7575

76+
ExportContainerCmd.Exec createExportContainerCmdExec();
77+
7678
KillContainerCmd.Exec createKillContainerCmdExec();
7779

7880
UpdateContainerCmd.Exec createUpdateContainerCmdExec();
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package com.github.dockerjava.api.command;
2+
3+
import java.io.InputStream;
4+
5+
import javax.annotation.CheckForNull;
6+
import javax.annotation.Nonnull;
7+
8+
import com.github.dockerjava.api.exception.NotFoundException;
9+
10+
/**
11+
* Export the contents of a container as a tar archive.
12+
*/
13+
public interface ExportContainerCmd extends SyncDockerCmd<InputStream> {
14+
15+
@CheckForNull
16+
String getContainerId();
17+
18+
ExportContainerCmd withContainerId(@Nonnull String containerId);
19+
20+
/**
21+
* Its the responsibility of the caller to consume and/or close the {@link InputStream} to prevent connection leaks.
22+
*
23+
* @throws NotFoundException
24+
* No such container
25+
*/
26+
@Override
27+
InputStream exec() throws NotFoundException;
28+
29+
interface Exec extends DockerCmdSyncExec<ExportContainerCmd, InputStream> {
30+
}
31+
}

docker-java-core/src/main/java/com/github/dockerjava/core/AbstractDockerCmdExecFactory.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import com.github.dockerjava.api.command.CommitCmd;
99
import com.github.dockerjava.api.command.ConnectToNetworkCmd;
1010
import com.github.dockerjava.api.command.ContainerDiffCmd;
11+
import com.github.dockerjava.api.command.ExportContainerCmd;
1112
import com.github.dockerjava.api.command.CopyArchiveFromContainerCmd;
1213
import com.github.dockerjava.api.command.CopyArchiveToContainerCmd;
1314
import com.github.dockerjava.api.command.CopyFileFromContainerCmd;
@@ -89,6 +90,7 @@
8990
import com.github.dockerjava.core.exec.CommitCmdExec;
9091
import com.github.dockerjava.core.exec.ConnectToNetworkCmdExec;
9192
import com.github.dockerjava.core.exec.ContainerDiffCmdExec;
93+
import com.github.dockerjava.core.exec.ExportContainerCmdExec;
9294
import com.github.dockerjava.core.exec.CopyArchiveFromContainerCmdExec;
9395
import com.github.dockerjava.core.exec.CopyArchiveToContainerCmdExec;
9496
import com.github.dockerjava.core.exec.CopyFileFromContainerCmdExec;
@@ -368,6 +370,11 @@ public ContainerDiffCmd.Exec createContainerDiffCmdExec() {
368370
return new ContainerDiffCmdExec(getBaseResource(), getDockerClientConfig());
369371
}
370372

373+
@Override
374+
public ExportContainerCmd.Exec createExportContainerCmdExec() {
375+
return new ExportContainerCmdExec(getBaseResource(), getDockerClientConfig());
376+
}
377+
371378
@Override
372379
public KillContainerCmd.Exec createKillContainerCmdExec() {
373380
return new KillContainerCmdExec(getBaseResource(), getDockerClientConfig());

docker-java-core/src/main/java/com/github/dockerjava/core/DockerClientImpl.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import com.github.dockerjava.api.command.CommitCmd;
88
import com.github.dockerjava.api.command.ConnectToNetworkCmd;
99
import com.github.dockerjava.api.command.ContainerDiffCmd;
10+
import com.github.dockerjava.api.command.ExportContainerCmd;
1011
import com.github.dockerjava.api.command.CopyArchiveFromContainerCmd;
1112
import com.github.dockerjava.api.command.CopyArchiveToContainerCmd;
1213
import com.github.dockerjava.api.command.CopyFileFromContainerCmd;
@@ -93,6 +94,7 @@
9394
import com.github.dockerjava.core.command.CommitCmdImpl;
9495
import com.github.dockerjava.core.command.ConnectToNetworkCmdImpl;
9596
import com.github.dockerjava.core.command.ContainerDiffCmdImpl;
97+
import com.github.dockerjava.core.command.ExportContainerCmdImpl;
9698
import com.github.dockerjava.core.command.CopyArchiveFromContainerCmdImpl;
9799
import com.github.dockerjava.core.command.CopyArchiveToContainerCmdImpl;
98100
import com.github.dockerjava.core.command.CopyFileFromContainerCmdImpl;
@@ -470,6 +472,11 @@ public ContainerDiffCmd containerDiffCmd(String containerId) {
470472
return new ContainerDiffCmdImpl(getDockerCmdExecFactory().createContainerDiffCmdExec(), containerId);
471473
}
472474

475+
@Override
476+
public ExportContainerCmd exportContainerCmd(String containerId) {
477+
return new ExportContainerCmdImpl(getDockerCmdExecFactory().createExportContainerCmdExec(), containerId);
478+
}
479+
473480
@Override
474481
public StopContainerCmd stopContainerCmd(String containerId) {
475482
return new StopContainerCmdImpl(getDockerCmdExecFactory().createStopContainerCmdExec(), containerId);
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package com.github.dockerjava.core.command;
2+
3+
import java.io.InputStream;
4+
import java.util.Objects;
5+
6+
import com.github.dockerjava.api.command.ExportContainerCmd;
7+
import com.github.dockerjava.api.exception.NotFoundException;
8+
9+
/**
10+
* Export the contents of a container as a tar archive.
11+
*/
12+
public class ExportContainerCmdImpl extends AbstrDockerCmd<ExportContainerCmd, InputStream> implements
13+
ExportContainerCmd {
14+
15+
private String containerId;
16+
17+
public ExportContainerCmdImpl(ExportContainerCmd.Exec exec, String containerId) {
18+
super(exec);
19+
withContainerId(containerId);
20+
}
21+
22+
@Override
23+
public String getContainerId() {
24+
return containerId;
25+
}
26+
27+
@Override
28+
public ExportContainerCmdImpl withContainerId(String containerId) {
29+
this.containerId = Objects.requireNonNull(containerId, "containerId was not specified");
30+
return this;
31+
}
32+
33+
/**
34+
* @throws NotFoundException
35+
* No such container
36+
*/
37+
@Override
38+
public InputStream exec() throws NotFoundException {
39+
return super.exec();
40+
}
41+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package com.github.dockerjava.core.exec;
2+
3+
import java.io.InputStream;
4+
5+
import org.slf4j.Logger;
6+
import org.slf4j.LoggerFactory;
7+
8+
import com.github.dockerjava.api.command.ExportContainerCmd;
9+
import com.github.dockerjava.core.DockerClientConfig;
10+
import com.github.dockerjava.core.MediaType;
11+
import com.github.dockerjava.core.WebTarget;
12+
13+
public class ExportContainerCmdExec extends AbstrSyncDockerCmdExec<ExportContainerCmd, InputStream>
14+
implements ExportContainerCmd.Exec {
15+
16+
private static final Logger LOGGER = LoggerFactory.getLogger(ExportContainerCmdExec.class);
17+
18+
public ExportContainerCmdExec(WebTarget baseResource, DockerClientConfig dockerClientConfig) {
19+
super(baseResource, dockerClientConfig);
20+
}
21+
22+
@Override
23+
protected InputStream execute(ExportContainerCmd command) {
24+
WebTarget webResource = getBaseResource().path("/containers/{id}/export").resolveTemplate("id",
25+
command.getContainerId());
26+
27+
LOGGER.trace("GET: {}", webResource);
28+
29+
return webResource.request().accept(MediaType.APPLICATION_X_TAR).get();
30+
}
31+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package com.github.dockerjava.cmd;
2+
3+
import com.github.dockerjava.api.command.CreateContainerResponse;
4+
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
5+
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
6+
import org.junit.Test;
7+
import org.slf4j.Logger;
8+
import org.slf4j.LoggerFactory;
9+
10+
import java.io.InputStream;
11+
12+
import static com.github.dockerjava.core.DockerRule.DEFAULT_IMAGE;
13+
import static org.hamcrest.MatcherAssert.assertThat;
14+
import static org.hamcrest.Matchers.is;
15+
import static org.hamcrest.Matchers.not;
16+
import static org.hamcrest.Matchers.emptyString;
17+
18+
public class ExportContainerCmdIT extends CmdIT {
19+
20+
private static final Logger LOG = LoggerFactory.getLogger(ExportContainerCmdIT.class);
21+
22+
@Test
23+
public void exportContainerHasCreatedFile() throws Exception {
24+
CreateContainerResponse container = dockerRule.getClient().createContainerCmd(DEFAULT_IMAGE)
25+
.withCmd("touch", "/myExportedFile")
26+
.exec();
27+
28+
LOG.info("Created container: {}", container.toString());
29+
assertThat(container.getId(), not(is(emptyString())));
30+
31+
dockerRule.getClient().startContainerCmd(container.getId()).exec();
32+
33+
int exitCode = dockerRule.getClient().waitContainerCmd(container.getId()).start()
34+
.awaitStatusCode();
35+
assertThat(exitCode, is(0));
36+
37+
try (InputStream response = dockerRule.getClient().exportContainerCmd(container.getId()).exec()) {
38+
boolean foundFile = false;
39+
try (TarArchiveInputStream tarStream = new TarArchiveInputStream(response)) {
40+
TarArchiveEntry entry;
41+
while ((entry = tarStream.getNextTarEntry()) != null) {
42+
if (entry.getName().contains("myExportedFile")) {
43+
foundFile = true;
44+
break;
45+
}
46+
}
47+
}
48+
assertThat("Exported archive should contain the created file", foundFile, is(true));
49+
}
50+
}
51+
52+
}

0 commit comments

Comments
 (0)