Skip to content

Commit 5b9c639

Browse files
DockerComposeContainer: add 'removeVolumes' parameter (#7009)
Closes #7008 Co-authored-by: Eddú Meléndez <[email protected]>
1 parent 31430f6 commit 5b9c639

File tree

2 files changed

+110
-1
lines changed

2 files changed

+110
-1
lines changed

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

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,8 @@ public class DockerComposeContainer<SELF extends DockerComposeContainer<SELF>>
121121

122122
private RemoveImages removeImages;
123123

124+
private boolean removeVolumes = true;
125+
124126
@Deprecated
125127
public DockerComposeContainer(File composeFile, String identifier) {
126128
this(identifier, composeFile);
@@ -368,7 +370,11 @@ public void stop() {
368370
ambassadorContainer.stop();
369371

370372
// Kill the services using docker-compose
371-
String cmd = "down -v";
373+
String cmd = "down";
374+
375+
if (removeVolumes) {
376+
cmd += " -v";
377+
}
372378
if (removeImages != null) {
373379
cmd += " --rmi " + removeImages.dockerRemoveImagesType();
374380
}
@@ -600,6 +606,17 @@ public SELF withRemoveImages(RemoveImages removeImages) {
600606
return self();
601607
}
602608

609+
/**
610+
* Remove volumes after containers shut down.
611+
*
612+
* @param removeVolumes whether volumes are to be removed.
613+
* @return this instance, for chaining.
614+
*/
615+
public SELF withRemoveVolumes(boolean removeVolumes) {
616+
this.removeVolumes = removeVolumes;
617+
return self();
618+
}
619+
603620
/**
604621
* Set the maximum startup timeout all the waits set are bounded to.
605622
*
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package org.testcontainers.junit;
2+
3+
import org.junit.Test;
4+
import org.junit.runner.RunWith;
5+
import org.junit.runners.Parameterized;
6+
import org.testcontainers.DockerClientFactory;
7+
import org.testcontainers.containers.DockerComposeContainer;
8+
9+
import java.io.File;
10+
import java.util.LinkedHashSet;
11+
import java.util.Set;
12+
import java.util.concurrent.atomic.AtomicReference;
13+
import java.util.stream.Stream;
14+
15+
import static org.assertj.core.api.Assertions.assertThat;
16+
import static org.awaitility.Awaitility.await;
17+
18+
@RunWith(Parameterized.class)
19+
public class DockerComposeContainerVolumeRemovalTest {
20+
21+
public DockerComposeContainerVolumeRemovalTest(
22+
final boolean removeVolumes,
23+
final boolean shouldVolumesBePresentAfterRunning
24+
) {
25+
this.removeVolumes = removeVolumes;
26+
this.shouldVolumesBePresentAfterRunning = shouldVolumesBePresentAfterRunning;
27+
}
28+
29+
public final boolean removeVolumes;
30+
31+
public final boolean shouldVolumesBePresentAfterRunning;
32+
33+
@Parameterized.Parameters(name = "removeVolumes = {0}")
34+
public static Object[][] params() {
35+
return new Object[][] { { true, false }, { false, true } };
36+
}
37+
38+
@Test
39+
public void performTest() {
40+
final File composeFile = new File("src/test/resources/compose-test.yml");
41+
42+
final AtomicReference<String> volumeName = new AtomicReference<>("");
43+
try (
44+
DockerComposeContainer environment = new DockerComposeContainer<>(composeFile)
45+
.withExposedService("redis", 6379)
46+
.withRemoveVolumes(this.removeVolumes)
47+
.withRemoveImages(DockerComposeContainer.RemoveImages.ALL)
48+
) {
49+
environment.start();
50+
51+
volumeName.set(volumeNameForRunningContainer("_redis_1"));
52+
final boolean isVolumePresentWhileRunning = isVolumePresent(volumeName.get());
53+
assertThat(isVolumePresentWhileRunning).as("the container volume is present while running").isEqualTo(true);
54+
}
55+
56+
await()
57+
.untilAsserted(() -> {
58+
final boolean isVolumePresentAfterRunning = isVolumePresent(volumeName.get());
59+
assertThat(isVolumePresentAfterRunning)
60+
.as("the container volume is present after running")
61+
.isEqualTo(this.shouldVolumesBePresentAfterRunning);
62+
});
63+
}
64+
65+
private String volumeNameForRunningContainer(final String containerNameSuffix) {
66+
return DockerClientFactory
67+
.instance()
68+
.client()
69+
.listContainersCmd()
70+
.exec()
71+
.stream()
72+
.filter(it -> Stream.of(it.getNames()).anyMatch(name -> name.endsWith(containerNameSuffix)))
73+
.findFirst()
74+
.map(container -> container.getMounts().get(0).getName())
75+
.orElseThrow(IllegalStateException::new);
76+
}
77+
78+
private boolean isVolumePresent(final String volumeName) {
79+
Set<String> nameFilter = new LinkedHashSet<>(1);
80+
nameFilter.add(volumeName);
81+
return DockerClientFactory
82+
.instance()
83+
.client()
84+
.listVolumesCmd()
85+
.withFilter("name", nameFilter)
86+
.exec()
87+
.getVolumes()
88+
.stream()
89+
.findFirst()
90+
.isPresent();
91+
}
92+
}

0 commit comments

Comments
 (0)