Skip to content

Commit 524b957

Browse files
lukidzirnorth
authored andcommitted
Add methods which copy files to/from containers at run time
Resolves #378
1 parent 0e898fc commit 524b957

File tree

4 files changed

+105
-0
lines changed

4 files changed

+105
-0
lines changed

core/src/main/java/org/testcontainers/containers/Container.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import org.testcontainers.containers.traits.LinkableContainer;
1111
import org.testcontainers.containers.wait.Wait;
1212
import org.testcontainers.containers.wait.WaitStrategy;
13+
import org.testcontainers.utility.MountableFile;
1314

1415
import java.io.IOException;
1516
import java.nio.charset.Charset;
@@ -408,6 +409,27 @@ ExecResult execInContainer(String... command)
408409
ExecResult execInContainer(Charset outputCharset, String... command)
409410
throws UnsupportedOperationException, IOException, InterruptedException;
410411

412+
/**
413+
*
414+
* Copies a file which resides inside the classpath to the container.
415+
*
416+
* @param mountableLocalFile file which is copied into the container
417+
* @param containerPath destination path inside the container
418+
* @throws IOException if there's an issue communicating with Docker
419+
* @throws InterruptedException if the thread waiting for the response is interrupted
420+
*/
421+
void copyFileToContainer(MountableFile mountableLocalFile, String containerPath) throws IOException, InterruptedException;
422+
423+
/**
424+
* Copies a file which resides inside the container to user defined directory
425+
*
426+
* @param containerPath path to file which is copied from container
427+
* @param destinationPath destination path to which file is copied with file name
428+
* @throws IOException if there's an issue communicating with Docker or receiving entry from TarArchiveInputStream
429+
* @throws InterruptedException if the thread waiting for the response is interrupted
430+
*/
431+
void copyFileFromContainer(String containerPath, String destinationPath) throws IOException, InterruptedException;
432+
411433
List<Integer> getExposedPorts();
412434

413435
List<String> getPortBindings();

core/src/main/java/org/testcontainers/containers/GenericContainer.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
import com.google.common.base.Preconditions;
1010
import com.google.common.base.Strings;
1111
import lombok.*;
12+
import org.apache.commons.compress.utils.IOUtils;
13+
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
1214
import org.jetbrains.annotations.Nullable;
1315
import org.junit.runner.Description;
1416
import org.rnorth.ducttape.ratelimits.RateLimiter;
@@ -31,6 +33,7 @@
3133
import org.testcontainers.utility.*;
3234

3335
import java.io.File;
36+
import java.io.FileOutputStream;
3437
import java.io.IOException;
3538
import java.nio.charset.Charset;
3639
import java.nio.file.Path;
@@ -858,6 +861,32 @@ public ExecResult execInContainer(String... command)
858861
return execInContainer(UTF8, command);
859862
}
860863

864+
/**
865+
* {@inheritDoc}
866+
*/
867+
@Override
868+
public void copyFileToContainer(MountableFile mountableLocalFile, String containerPath) throws IOException, InterruptedException {
869+
870+
this.dockerClient
871+
.copyArchiveToContainerCmd(this.containerId)
872+
.withHostResource(mountableLocalFile.getResolvedPath())
873+
.withRemotePath(containerPath)
874+
.exec();
875+
}
876+
877+
/**
878+
* {@inheritDoc}
879+
*/
880+
@Override
881+
public void copyFileFromContainer(String containerPath, String destinationPath) throws IOException, InterruptedException {
882+
try (final TarArchiveInputStream tarInputStream = new TarArchiveInputStream(this.dockerClient
883+
.copyArchiveFromContainerCmd(this.containerId, containerPath)
884+
.exec())) {
885+
tarInputStream.getNextTarEntry();
886+
IOUtils.copy(tarInputStream, new FileOutputStream(destinationPath));
887+
}
888+
}
889+
861890
/**
862891
* {@inheritDoc}
863892
*/

core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,21 @@
11
package org.testcontainers.junit;
22

3+
import com.github.dockerjava.api.exception.NotFoundException;
34
import com.google.common.collect.ImmutableMap;
5+
import com.google.common.io.Files;
46
import com.google.common.util.concurrent.Uninterruptibles;
57
import com.mongodb.MongoClient;
68
import com.mongodb.client.MongoCollection;
79
import com.mongodb.client.MongoDatabase;
810
import com.rabbitmq.client.*;
11+
import org.apache.commons.io.FileUtils;
912
import org.bson.Document;
1013
import org.junit.*;
1114
import org.rnorth.ducttape.RetryCountExceededException;
1215
import org.rnorth.ducttape.unreliables.Unreliables;
1316
import org.testcontainers.containers.GenericContainer;
1417
import org.testcontainers.utility.Base58;
18+
import org.testcontainers.utility.MountableFile;
1519
import org.testcontainers.utility.TestEnvironment;
1620

1721
import java.io.*;
@@ -330,6 +334,54 @@ public void createContainerCmdHookTest() {
330334
}
331335
}
332336

337+
@Test
338+
public void copyToContainerTest() throws Exception {
339+
final File tempResultFolder = Files.createTempDir();
340+
341+
try (final GenericContainer alpineCopyToContainer = new GenericContainer("alpine:3.2")
342+
.withCommand("top")){
343+
344+
alpineCopyToContainer.start();
345+
final MountableFile mountableFile = MountableFile.forClasspathResource("test_copy_to_container.txt");
346+
alpineCopyToContainer.copyFileToContainer(mountableFile, "/home/");
347+
alpineCopyToContainer.copyFileFromContainer("/home/test_copy_to_container.txt",
348+
tempResultFolder.getAbsolutePath() + "/test_copy_to_container.txt");
349+
350+
File expectedFile = new File(mountableFile.getResolvedPath());
351+
File actualFile = new File(tempResultFolder.getAbsolutePath() + "/test_copy_to_container.txt");
352+
assertTrue("Files aren't same ", FileUtils.contentEquals(expectedFile,actualFile));
353+
}
354+
}
355+
356+
@Test(expected = NotFoundException.class)
357+
public void copyFromContainerShouldFailBecauseNoFileTest() throws NotFoundException, IOException, InterruptedException {
358+
359+
try (final GenericContainer alpineCopyToContainer = new GenericContainer("alpine:3.2")
360+
.withCommand("top")) {
361+
alpineCopyToContainer.start();
362+
alpineCopyToContainer.copyFileFromContainer("/home/test.txt", "src/test/resources/copy-from/test.txt");
363+
}
364+
}
365+
366+
@Test
367+
public void shouldCopyFileFromContainerTest() throws IOException, InterruptedException {
368+
final File tempResultFolder = Files.createTempDir();
369+
370+
try (final GenericContainer alpineCopyToContainer = new GenericContainer("alpine:3.2")
371+
.withCommand("top")) {
372+
373+
alpineCopyToContainer.start();
374+
final MountableFile mountableFile = MountableFile.forClasspathResource("test_copy_to_container.txt");
375+
alpineCopyToContainer.copyFileToContainer(mountableFile, "/home/");
376+
alpineCopyToContainer.copyFileFromContainer("/home/test_copy_to_container.txt",
377+
tempResultFolder.getAbsolutePath() + "/test_copy_from_container.txt");
378+
379+
File expectedFile = new File(mountableFile.getResolvedPath());
380+
File actualFile = new File(tempResultFolder.getAbsolutePath() + "/test_copy_from_container.txt");
381+
assertTrue("Files aren't same ", FileUtils.contentEquals(expectedFile,actualFile));
382+
}
383+
}
384+
333385
private BufferedReader getReaderForContainerPort80(GenericContainer container) {
334386

335387
return Unreliables.retryUntilSuccess(10, TimeUnit.SECONDS, () -> {
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Some Test
2+
Message

0 commit comments

Comments
 (0)