Skip to content

Commit

Permalink
Add reused parameter to the hooks (#2052)
Browse files Browse the repository at this point in the history
* Add `containerIsReused` hook

* Add `reused` parameter to `containerIs{Starting,Started}`

* post-merge fixes
  • Loading branch information
bsideup authored Nov 11, 2019
1 parent c027751 commit 7e5adad
Show file tree
Hide file tree
Showing 3 changed files with 130 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,7 @@ private void tryStart(Instant startedAt) {
// Tell subclasses that we're starting
containerInfo = dockerClient.inspectContainerCmd(containerId).exec();
containerName = containerInfo.getName();
containerIsStarting(containerInfo);
containerIsStarting(containerInfo, reused);

// Wait until the container has reached the desired running state
if (!this.startupCheckStrategy.waitUntilStartupSuccessful(dockerClient, containerId)) {
Expand Down Expand Up @@ -444,7 +444,7 @@ private void tryStart(Instant startedAt) {
}

logger().info("Container {} started in {}", dockerImageName, Duration.between(startedAt, Instant.now()));
containerIsStarted(containerInfo);
containerIsStarted(containerInfo, reused);
} catch (Exception e) {
if (e instanceof UndeclaredThrowableException && e.getCause() instanceof Exception) {
e = (Exception) e.getCause();
Expand Down Expand Up @@ -582,10 +582,22 @@ protected void containerIsCreated(String containerId) {
protected void containerIsStarting(InspectContainerResponse containerInfo) {
}

@SuppressWarnings({"EmptyMethod", "UnusedParameters"})
@UnstableAPI
protected void containerIsStarting(InspectContainerResponse containerInfo, boolean reused) {
containerIsStarting(containerInfo);
}

@SuppressWarnings({"EmptyMethod", "UnusedParameters"})
protected void containerIsStarted(InspectContainerResponse containerInfo) {
}

@SuppressWarnings({"EmptyMethod", "UnusedParameters"})
@UnstableAPI
protected void containerIsStarted(InspectContainerResponse containerInfo, boolean reused) {
containerIsStarted(containerInfo);
}

/**
* A hook that is executed before the container is stopped with {@link #stop()}.
* Warning! This hook won't be executed if the container is terminated during
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@
import org.testcontainers.DockerClientFactory;
import org.testcontainers.containers.startupcheck.StartupCheckStrategy;
import org.testcontainers.containers.wait.strategy.AbstractWaitStrategy;
import org.testcontainers.containers.wait.strategy.WaitStrategy;
import org.testcontainers.utility.TestcontainersConfiguration;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
Expand Down Expand Up @@ -98,34 +98,93 @@ protected void containerIsCreated(String containerId) {

@RunWith(BlockJUnit4ClassRunner.class)
@FieldDefaults(makeFinal = true)
public static class HashTest {
public static class HooksTest extends AbstractReusabilityTest {

List<String> script = new ArrayList<>();

GenericContainer<?> container = makeReusable(new GenericContainer(IMAGE_FUTURE) {

StartupCheckStrategy startupCheckStrategy = new StartupCheckStrategy() {
@Override
public StartupStatus checkStartupState(DockerClient dockerClient, String containerId) {
return StartupStatus.SUCCESSFUL;
protected boolean canBeReused() {
// Because we override "containerIsCreated"
return true;
}
};

WaitStrategy waitStrategy = new AbstractWaitStrategy() {
@Override
protected void waitUntilReady() {
protected void containerIsCreated(String containerId) {
script.add("containerIsCreated");
}

@Override
protected void containerIsStarting(InspectContainerResponse containerInfo, boolean reused) {
script.add("containerIsStarting(reused=" + reused + ")");
}
};

DockerClient client = Mockito.mock(DockerClient.class);
@Override
protected void containerIsStarted(InspectContainerResponse containerInfo, boolean reused) {
script.add("containerIsStarted(reused=" + reused + ")");
}
});

GenericContainer container = new GenericContainer(IMAGE_FUTURE)
.withNetworkMode("none") // to disable the port forwarding
.withStartupCheckStrategy(startupCheckStrategy)
.waitingFor(waitStrategy)
.withReuse(true);
@Test
public void shouldSetLabelsIfEnvironmentDoesNotSupportReuse() {
// TODO mock TestcontainersConfiguration
Assume.assumeFalse("does not support reuse", TestcontainersConfiguration.getInstance().environmentSupportsReuse());
AtomicReference<CreateContainerCmd> commandRef = new AtomicReference<>();
String containerId = randomContainerId();
when(client.createContainerCmd(any())).then(createContainerAnswer(containerId));
when(client.listContainersCmd()).then(listContainersAnswer());
when(client.startContainerCmd(containerId)).then(startContainerAnswer());
when(client.inspectContainerCmd(containerId)).then(inspectContainerAnswer());

{
container.dockerClient = client;
container.start();
assertThat(script).containsExactly(
"containerIsCreated",
"containerIsStarting(reused=false)",
"containerIsStarted(reused=false)"
);
}

@Test
public void shouldCallHookIfReused() {
// TODO mock TestcontainersConfiguration
Assume.assumeTrue("supports reuse", TestcontainersConfiguration.getInstance().environmentSupportsReuse());
String containerId = randomContainerId();
when(client.createContainerCmd(any())).then(createContainerAnswer(containerId));
String existingContainerId = randomContainerId();
when(client.listContainersCmd()).then(listContainersAnswer(existingContainerId));
when(client.inspectContainerCmd(existingContainerId)).then(inspectContainerAnswer());

container.start();
assertThat(script).containsExactly(
"containerIsStarting(reused=true)",
"containerIsStarted(reused=true)"
);
}

@Test
public void shouldNotCallHookIfNotReused() {
String containerId = randomContainerId();
when(client.createContainerCmd(any())).then(createContainerAnswer(containerId));
when(client.listContainersCmd()).then(listContainersAnswer());
when(client.startContainerCmd(containerId)).then(startContainerAnswer());
when(client.inspectContainerCmd(containerId)).then(inspectContainerAnswer());

container.start();
assertThat(script).containsExactly(
"containerIsCreated",
"containerIsStarting(reused=false)",
"containerIsStarted(reused=false)"
);
}
}

@RunWith(BlockJUnit4ClassRunner.class)
@FieldDefaults(makeFinal = true)
public static class HashTest extends AbstractReusabilityTest {

protected GenericContainer<?> container = makeReusable(new GenericContainer<>(IMAGE_FUTURE));

@Test
public void shouldStartIfListReturnsEmpty() {
String containerId = randomContainerId();
Expand All @@ -143,13 +202,15 @@ public void shouldStartIfListReturnsEmpty() {
public void shouldReuseIfListReturnsID() {
// TODO mock TestcontainersConfiguration
Assume.assumeTrue("supports reuse", TestcontainersConfiguration.getInstance().environmentSupportsReuse());
when(client.createContainerCmd(any())).then(createContainerAnswer(randomContainerId()));
String containerId = randomContainerId();
when(client.createContainerCmd(any())).then(createContainerAnswer(containerId));
String existingContainerId = randomContainerId();
when(client.listContainersCmd()).then(listContainersAnswer(existingContainerId));
when(client.inspectContainerCmd(existingContainerId)).then(inspectContainerAnswer());

container.start();

Mockito.verify(client, Mockito.never()).startContainerCmd(containerId);
Mockito.verify(client, Mockito.never()).startContainerCmd(existingContainerId);
}

Expand All @@ -172,12 +233,37 @@ public void shouldSetLabelsIfEnvironmentDoesNotSupportReuse() {
.containsKeys(DockerClientFactory.TESTCONTAINERS_SESSION_ID_LABEL);
});
}
}

@FieldDefaults(makeFinal = true)
static abstract class AbstractReusabilityTest {

protected DockerClient client = Mockito.mock(DockerClient.class);

protected <T extends GenericContainer<?>> T makeReusable(T container) {
container.dockerClient = client;
container.withNetworkMode("none"); // to disable the port forwarding
container.withStartupCheckStrategy(new StartupCheckStrategy() {
@Override
public StartupStatus checkStartupState(DockerClient dockerClient, String containerId) {
return StartupStatus.SUCCESSFUL;
}
});
container.waitingFor(new AbstractWaitStrategy() {
@Override
protected void waitUntilReady() {

}
});
container.withReuse(true);
return container;
}

private String randomContainerId() {
protected String randomContainerId() {
return UUID.randomUUID().toString();
}

private Answer<ListContainersCmd> listContainersAnswer(String... ids) {
protected Answer<ListContainersCmd> listContainersAnswer(String... ids) {
return invocation -> {
ListContainersCmd.Exec exec = command -> {
return new ObjectMapper().convertValue(
Expand All @@ -191,11 +277,11 @@ private Answer<ListContainersCmd> listContainersAnswer(String... ids) {
};
}

private Answer<CreateContainerCmd> createContainerAnswer(String containerId) {
protected Answer<CreateContainerCmd> createContainerAnswer(String containerId) {
return createContainerAnswer(containerId, command -> {});
}

private Answer<CreateContainerCmd> createContainerAnswer(String containerId, Consumer<CreateContainerCmd> cmdConsumer) {
protected Answer<CreateContainerCmd> createContainerAnswer(String containerId, Consumer<CreateContainerCmd> cmdConsumer) {
return invocation -> {
CreateContainerCmd.Exec exec = command -> {
cmdConsumer.accept(command);
Expand All @@ -207,7 +293,7 @@ private Answer<CreateContainerCmd> createContainerAnswer(String containerId, Con
};
}

private Answer<StartContainerCmd> startContainerAnswer() {
protected Answer<StartContainerCmd> startContainerAnswer() {
return invocation -> {
StartContainerCmd.Exec exec = command -> {
return null;
Expand All @@ -216,7 +302,7 @@ private Answer<StartContainerCmd> startContainerAnswer() {
};
}

private Answer<InspectContainerCmd> inspectContainerAnswer() {
protected Answer<InspectContainerCmd> inspectContainerAnswer() {
return invocation -> {
InspectContainerCmd.Exec exec = command -> {
return new InspectContainerResponse();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,12 @@ protected void doStart() {

@Override
@SneakyThrows
protected void containerIsStarting(InspectContainerResponse containerInfo) {
super.containerIsStarting(containerInfo);
protected void containerIsStarting(InspectContainerResponse containerInfo, boolean reused) {
super.containerIsStarting(containerInfo, reused);

if (reused) {
return;
}

port = getMappedPort(KAFKA_PORT);

Expand Down

0 comments on commit 7e5adad

Please sign in to comment.