diff --git a/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java b/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java index ce50313d429..a7c4ecd978a 100644 --- a/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java +++ b/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java @@ -1,6 +1,5 @@ package org.testcontainers.containers.localstack; -import com.github.dockerjava.api.command.InspectContainerResponse; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.experimental.FieldDefaults; @@ -20,6 +19,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; /** @@ -203,13 +203,19 @@ protected void configure() { exposePorts(); } + /** + * Copied before the container starts: the entrypoint waits only for the script to exist, so copying it + * into an already running container can have it executed while still incomplete, exiting with code 126. + */ @Override - protected void containerIsStarting(InspectContainerResponse containerInfo) { + protected void containerIsCreated(String containerId) { + Map labels = getDockerClient().inspectContainerCmd(containerId).exec().getConfig().getLabels(); String command = "#!/bin/bash\n"; - command += "export LAMBDA_DOCKER_FLAGS=" + configureServiceContainerLabels("LAMBDA_DOCKER_FLAGS") + "\n"; - command += "export ECS_DOCKER_FLAGS=" + configureServiceContainerLabels("ECS_DOCKER_FLAGS") + "\n"; - command += "export EC2_DOCKER_FLAGS=" + configureServiceContainerLabels("EC2_DOCKER_FLAGS") + "\n"; - command += "export BATCH_DOCKER_FLAGS=" + configureServiceContainerLabels("BATCH_DOCKER_FLAGS") + "\n"; + command += + "export LAMBDA_DOCKER_FLAGS=" + configureServiceContainerLabels("LAMBDA_DOCKER_FLAGS", labels) + "\n"; + command += "export ECS_DOCKER_FLAGS=" + configureServiceContainerLabels("ECS_DOCKER_FLAGS", labels) + "\n"; + command += "export EC2_DOCKER_FLAGS=" + configureServiceContainerLabels("EC2_DOCKER_FLAGS", labels) + "\n"; + command += "export BATCH_DOCKER_FLAGS=" + configureServiceContainerLabels("BATCH_DOCKER_FLAGS", labels) + "\n"; command += "/usr/local/bin/docker-entrypoint.sh\n"; copyFileToContainer(Transferable.of(command, 0777), STARTER_SCRIPT); } @@ -220,8 +226,8 @@ protected void containerIsStarting(InspectContainerResponse containerInfo) { * chance. * @return the lambda container labels as a string */ - private String configureServiceContainerLabels(String existingEnvFlagKey) { - String internalMarkerFlags = internalMarkerLabels(); + private String configureServiceContainerLabels(String existingEnvFlagKey, Map labels) { + String internalMarkerFlags = internalMarkerLabels(labels); String existingFlags = getEnvMap().get(existingEnvFlagKey); if (existingFlags != null) { internalMarkerFlags = existingFlags + " " + internalMarkerFlags; @@ -233,10 +239,8 @@ private String configureServiceContainerLabels(String existingEnvFlagKey) { * Provides a docker argument string including all default labels set on testcontainers containers (excluding reuse labels) * @return Argument string in the format `-l key1=value1 -l key2=value2` */ - private String internalMarkerLabels() { - return getContainerInfo() - .getConfig() - .getLabels() + private String internalMarkerLabels(Map labels) { + return labels .entrySet() .stream() .filter(entry -> entry.getKey().startsWith(DockerClientFactory.TESTCONTAINERS_LABEL)) diff --git a/modules/localstack/src/main/java/org/testcontainers/localstack/LocalStackContainer.java b/modules/localstack/src/main/java/org/testcontainers/localstack/LocalStackContainer.java index 31eee4ca5ca..791de36e9fe 100644 --- a/modules/localstack/src/main/java/org/testcontainers/localstack/LocalStackContainer.java +++ b/modules/localstack/src/main/java/org/testcontainers/localstack/LocalStackContainer.java @@ -1,6 +1,5 @@ package org.testcontainers.localstack; -import com.github.dockerjava.api.command.InspectContainerResponse; import lombok.extern.slf4j.Slf4j; import org.testcontainers.DockerClientFactory; import org.testcontainers.containers.GenericContainer; @@ -15,6 +14,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; /** @@ -76,13 +76,19 @@ protected void configure() { } } + /** + * Copied before the container starts: the entrypoint waits only for the script to exist, so copying it + * into an already running container can have it executed while still incomplete, exiting with code 126. + */ @Override - protected void containerIsStarting(InspectContainerResponse containerInfo) { + protected void containerIsCreated(String containerId) { + Map labels = getDockerClient().inspectContainerCmd(containerId).exec().getConfig().getLabels(); String command = "#!/bin/bash\n"; - command += "export LAMBDA_DOCKER_FLAGS=" + configureServiceContainerLabels("LAMBDA_DOCKER_FLAGS") + "\n"; - command += "export ECS_DOCKER_FLAGS=" + configureServiceContainerLabels("ECS_DOCKER_FLAGS") + "\n"; - command += "export EC2_DOCKER_FLAGS=" + configureServiceContainerLabels("EC2_DOCKER_FLAGS") + "\n"; - command += "export BATCH_DOCKER_FLAGS=" + configureServiceContainerLabels("BATCH_DOCKER_FLAGS") + "\n"; + command += + "export LAMBDA_DOCKER_FLAGS=" + configureServiceContainerLabels("LAMBDA_DOCKER_FLAGS", labels) + "\n"; + command += "export ECS_DOCKER_FLAGS=" + configureServiceContainerLabels("ECS_DOCKER_FLAGS", labels) + "\n"; + command += "export EC2_DOCKER_FLAGS=" + configureServiceContainerLabels("EC2_DOCKER_FLAGS", labels) + "\n"; + command += "export BATCH_DOCKER_FLAGS=" + configureServiceContainerLabels("BATCH_DOCKER_FLAGS", labels) + "\n"; command += "/usr/local/bin/docker-entrypoint.sh\n"; copyFileToContainer(Transferable.of(command, 0777), STARTER_SCRIPT); } @@ -93,8 +99,8 @@ protected void containerIsStarting(InspectContainerResponse containerInfo) { * chance. * @return the lambda container labels as a string */ - private String configureServiceContainerLabels(String existingEnvFlagKey) { - String internalMarkerFlags = internalMarkerLabels(); + private String configureServiceContainerLabels(String existingEnvFlagKey, Map labels) { + String internalMarkerFlags = internalMarkerLabels(labels); String existingFlags = getEnvMap().get(existingEnvFlagKey); if (existingFlags != null) { internalMarkerFlags = existingFlags + " " + internalMarkerFlags; @@ -106,10 +112,8 @@ private String configureServiceContainerLabels(String existingEnvFlagKey) { * Provides a docker argument string including all default labels set on testcontainers containers (excluding reuse labels) * @return Argument string in the format `-l key1=value1 -l key2=value2` */ - private String internalMarkerLabels() { - return getContainerInfo() - .getConfig() - .getLabels() + private String internalMarkerLabels(Map labels) { + return labels .entrySet() .stream() .filter(entry -> entry.getKey().startsWith(DockerClientFactory.TESTCONTAINERS_LABEL)) diff --git a/modules/localstack/src/test/java/org/testcontainers/localstack/StarterScriptTest.java b/modules/localstack/src/test/java/org/testcontainers/localstack/StarterScriptTest.java new file mode 100644 index 00000000000..59d5cec917e --- /dev/null +++ b/modules/localstack/src/test/java/org/testcontainers/localstack/StarterScriptTest.java @@ -0,0 +1,39 @@ +package org.testcontainers.localstack; + +import org.apache.commons.io.IOUtils; +import org.junit.jupiter.api.Test; +import org.testcontainers.containers.localstack.LocalstackTestImages; + +import java.nio.charset.StandardCharsets; +import java.util.concurrent.atomic.AtomicReference; + +import static org.assertj.core.api.Assertions.assertThat; + +class StarterScriptTest { + + @Test + void starterScriptIsCompleteBeforeTheContainerStarts() { + AtomicReference script = new AtomicReference<>(); + + try ( + LocalStackContainer localstack = new LocalStackContainer(LocalstackTestImages.LOCALSTACK_IMAGE) { + @Override + protected void containerIsCreated(String containerId) { + super.containerIsCreated(containerId); + script.set( + copyFileFromContainer( + "/testcontainers_start.sh", + stream -> IOUtils.toString(stream, StandardCharsets.UTF_8) + ) + ); + } + } + ) { + localstack.start(); + } + + // read back while the container was created but not yet started, so the entrypoint could not + // have executed a partially copied script + assertThat(script.get()).endsWith("/usr/local/bin/docker-entrypoint.sh\n"); + } +}