Skip to content

Commit 3afa556

Browse files
ssheikinfindepiJan Waś
committed
Prevent ghost containers after retries
ReportLeakedContainers adapted from https://github.com/trinodb/trino/blob/master/testing/trino-testing-containers/src/main/java/io/trino/testing/containers/junit/ReportLeakedContainers.java Co-authored-by: Piotr Findeisen <[email protected]> Co-authored-by: Jan Waś <[email protected]>
1 parent 77a423c commit 3afa556

File tree

3 files changed

+64
-0
lines changed

3 files changed

+64
-0
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -550,6 +550,7 @@ private void tryStart() {
550550
} else {
551551
logger().error("There are no stdout/stderr logs available for the failed container");
552552
}
553+
stop();
553554
}
554555

555556
throw new ContainerLaunchException("Could not create/start container", e);

core/src/test/java/org/testcontainers/containers/GenericContainerTest.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.testcontainers.utility.DockerImageName;
2929
import org.testcontainers.utility.MountableFile;
3030

31+
import java.time.Duration;
3132
import java.util.Arrays;
3233
import java.util.List;
3334
import java.util.Map;
@@ -39,9 +40,27 @@
3940
import static org.assertj.core.api.Assertions.assertThatThrownBy;
4041
import static org.assertj.core.api.Assertions.catchThrowable;
4142
import static org.assertj.core.api.Assumptions.assumeThat;
43+
import static org.testcontainers.containers.ReportLeakedContainers.reportLeakedContainers;
44+
import static org.testcontainers.containers.wait.strategy.Wait.forLogMessage;
4245

4346
public class GenericContainerTest {
4447

48+
@Test
49+
public void testStartupTimeout()
50+
{
51+
try (
52+
GenericContainer<?> container = new GenericContainer<>(TestImages.TINY_IMAGE)
53+
.withStartupAttempts(3)
54+
.waitingFor(forLogMessage("this text does not exist in logs", 1)
55+
.withStartupTimeout(Duration.ofMillis(1)))
56+
.withCommand("tail", "-f", "/dev/null");
57+
) {
58+
assertThatThrownBy(container::start)
59+
.hasStackTraceContaining("Retry limit hit with exception");
60+
}
61+
assertThat(reportLeakedContainers()).isEmpty();
62+
}
63+
4564
@Test
4665
public void shouldReportOOMAfterWait() {
4766
Info info = DockerClientFactory.instance().client().infoCmd().exec();
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package org.testcontainers.containers;
2+
3+
import com.github.dockerjava.api.DockerClient;
4+
import com.github.dockerjava.api.model.Container;
5+
import org.testcontainers.DockerClientFactory;
6+
7+
import java.util.List;
8+
import java.util.Optional;
9+
10+
import static com.google.common.base.MoreObjects.toStringHelper;
11+
import static com.google.common.collect.ImmutableList.toImmutableList;
12+
import static java.lang.String.format;
13+
import static java.util.Arrays.asList;
14+
import static java.util.Collections.singletonMap;
15+
import static java.util.stream.Collectors.joining;
16+
17+
public class ReportLeakedContainers
18+
{
19+
public static Optional<String> reportLeakedContainers()
20+
{
21+
@SuppressWarnings("resource") // Throws when close is attempted, as this is a global instance.
22+
DockerClient dockerClient = DockerClientFactory.lazyClient();
23+
24+
List<Container> containers = dockerClient.listContainersCmd()
25+
.withLabelFilter(singletonMap(DockerClientFactory.TESTCONTAINERS_SESSION_ID_LABEL, DockerClientFactory.SESSION_ID))
26+
// ignore status "exited" - for example, failed containers after using `withStartupAttempts()`
27+
.withStatusFilter(asList("created", "restarting", "running", "paused"))
28+
.exec()
29+
.stream()
30+
.collect(toImmutableList());
31+
32+
if (containers.isEmpty()) {
33+
return Optional.empty();
34+
}
35+
36+
return Optional.of(format("Leaked containers: %s", containers.stream()
37+
.map(container -> toStringHelper("container")
38+
.add("id", container.getId())
39+
.add("image", container.getImage())
40+
.add("imageId", container.getImageId())
41+
.toString())
42+
.collect(joining(", ", "[", "]"))));
43+
}
44+
}

0 commit comments

Comments
 (0)