Skip to content

Commit c648b3b

Browse files
committed
Ensure graceful shutdown in GenericContainer.stop() (#10365)
1 parent f2a6fe9 commit c648b3b

File tree

2 files changed

+46
-1
lines changed

2 files changed

+46
-1
lines changed

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -647,7 +647,11 @@ public void stop() {
647647
} catch (Exception e) {
648648
imageName = "<unknown>";
649649
}
650-
650+
try {
651+
dockerClient.stopContainerCmd(containerId).exec();
652+
} catch (Exception e) {
653+
logger().warn("Failed to stop container gracefully: {}", e.getMessage());
654+
}
651655
containerIsStopping(containerInfo);
652656
ResourceReaper.instance().stopAndRemoveContainer(containerId, imageName);
653657
containerIsStopped(containerInfo);

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

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import org.rnorth.ducttape.unreliables.Unreliables;
2222
import org.testcontainers.DockerClientFactory;
2323
import org.testcontainers.TestImages;
24+
import org.testcontainers.containers.output.ToStringConsumer;
2425
import org.testcontainers.containers.startupcheck.StartupCheckStrategy;
2526
import org.testcontainers.containers.wait.strategy.AbstractWaitStrategy;
2627
import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy;
@@ -336,6 +337,46 @@ private static Optional<String> reportLeakedContainers() {
336337
);
337338
}
338339

340+
@Test
341+
public void testPostHookExecutionOnStop() {
342+
String scriptContent =
343+
"#!/bin/bash\n" +
344+
"function on_shutdown() {\n" +
345+
" echo 'HOOK_TRIGGERED'\n" +
346+
"}\n" +
347+
"trap on_shutdown SIGTERM SIGINT EXIT\n" +
348+
"echo 'CONTAINER_STARTED'\n" +
349+
"while true; do sleep 1; done\n";
350+
351+
ToStringConsumer logConsumer = new ToStringConsumer();
352+
353+
try (
354+
GenericContainer<?> container = new GenericContainer<>(
355+
new ImageFromDockerfile()
356+
.withFileFromString("entrypoint.sh", scriptContent)
357+
.withDockerfileFromBuilder(builder -> {
358+
builder
359+
.from("alpine:3.18")
360+
.run("apk add --no-cache tini bash")
361+
.copy("entrypoint.sh", "/entrypoint.sh")
362+
.run("chmod +x /entrypoint.sh")
363+
.entryPoint("/sbin/tini", "--", "/entrypoint.sh")
364+
.build();
365+
})
366+
)
367+
.withLogConsumer(logConsumer)
368+
.waitingFor(Wait.forLogMessage(".*CONTAINER_STARTED.*", 1))
369+
) {
370+
container.start();
371+
372+
container.stop();
373+
374+
assertThat(logConsumer.toUtf8String())
375+
.as("The shutdown hook defined in 'trap' should be executed upon container stop")
376+
.contains("HOOK_TRIGGERED");
377+
}
378+
}
379+
339380
static class NoopStartupCheckStrategy extends StartupCheckStrategy {
340381

341382
@Override

0 commit comments

Comments
 (0)