From 7e5b8dc75109ce65ad1572204fcbffeaeba22a08 Mon Sep 17 00:00:00 2001 From: Pete Cornish Date: Fri, 21 Oct 2016 20:07:09 +0100 Subject: [PATCH] Adds the ability to tail child container logs. Adds the option to enable/disable pulling of images before starting Compose services. --- .../containers/DockerComposeContainer.java | 54 +++++++++++++++++-- .../containers/GenericContainer.java | 13 +---- .../org/testcontainers/utility/LogUtils.java | 37 +++++++++++++ 3 files changed, 87 insertions(+), 17 deletions(-) create mode 100644 core/src/main/java/org/testcontainers/utility/LogUtils.java diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index c34f94ffd2b..ecd23770db6 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -17,6 +17,7 @@ import org.slf4j.LoggerFactory; import org.slf4j.profiler.Profiler; import org.testcontainers.DockerClientFactory; +import org.testcontainers.containers.output.OutputFrame; import org.testcontainers.containers.output.Slf4jLogConsumer; import org.testcontainers.containers.startupcheck.IndefiniteWaitOneShotStartupCheckStrategy; import org.testcontainers.utility.*; @@ -49,6 +50,8 @@ public class DockerComposeContainer> e private Map scalingPreferences = new HashMap<>(); private DockerClient dockerClient; private boolean localCompose; + private boolean pull = true; + private boolean tailChildContainers; private static final Object MUTEX = new Object(); @@ -99,9 +102,14 @@ public void starting(Description description) { profiler.start("Docker Compose container startup"); synchronized (MUTEX) { - pullImages(); + if (pull) { + pullImages(); + } applyScaling(); // scale before up, so that all scaled instances are available first for linking createServices(); + if (tailChildContainers) { + tailChildContainerLogs(); + } registerContainersForShutdown(); startAmbassadorContainers(profiler); } @@ -119,6 +127,16 @@ private void createServices() { .start(); } + private void tailChildContainerLogs() { + listChildContainers().forEach(container -> + LogUtils.followOutput(dockerClient, + container.getId(), + new Slf4jLogConsumer(logger()).withPrefix(container.getNames()[0]), + OutputFrame.OutputType.STDOUT, + OutputFrame.OutputType.STDERR) + ); + } + private DockerCompose getDockerCompose(String cmd) { final DockerCompose dockerCompose; if (localCompose) { @@ -147,10 +165,7 @@ private void applyScaling() { private void registerContainersForShutdown() { // Ensure that all service containers that were launched by compose will be killed at shutdown try { - List containers = dockerClient.listContainersCmd() - .withLabelFilter("name=/" + identifier) - .withShowAll(true) - .exec(); + final List containers = listChildContainers(); // register with ResourceReaper to ensure final shutdown with JVM containers.forEach(container -> @@ -175,6 +190,15 @@ private void registerContainersForShutdown() { } } + private List listChildContainers() { + return dockerClient.listContainersCmd() + .withShowAll(true) + .exec().stream() + .filter(container -> Arrays.stream(container.getNames()).anyMatch(name -> + name.startsWith("/" + identifier))) + .collect(Collectors.toList()); + } + private void startAmbassadorContainers(Profiler profiler) { for (final Map.Entry address : ambassadorContainers.entrySet()) { @@ -315,6 +339,26 @@ public SELF withLocalCompose(boolean localCompose) { return self(); } + /** + * Whether to pull images first. + * + * @return this instance, for chaining + */ + public SELF withPull(boolean pull) { + this.pull = pull; + return self(); + } + + /** + * Whether to tail child container logs. + * + * @return this instance, for chaining + */ + public SELF withTailChildContainers(boolean tailChildContainers) { + this.tailChildContainers = tailChildContainers; + return self(); + } + private SELF self() { return (SELF) this; } diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 24c3415b6f2..a4cb50baa14 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -4,7 +4,6 @@ import com.github.dockerjava.api.command.CreateContainerCmd; import com.github.dockerjava.api.command.ExecCreateCmdResponse; import com.github.dockerjava.api.command.InspectContainerResponse; -import com.github.dockerjava.api.command.LogContainerCmd; import com.github.dockerjava.api.exception.DockerException; import com.github.dockerjava.api.model.*; import com.google.common.base.Preconditions; @@ -757,17 +756,7 @@ public void followOutput(Consumer consumer) { */ @Override public void followOutput(Consumer consumer, OutputFrame.OutputType... types) { - LogContainerCmd cmd = dockerClient.logContainerCmd(containerId) - .withFollowStream(true); - - FrameConsumerResultCallback callback = new FrameConsumerResultCallback(); - for (OutputFrame.OutputType type : types) { - callback.addConsumer(type, consumer); - if (type == STDOUT) cmd.withStdOut(true); - if (type == STDERR) cmd.withStdErr(true); - } - - cmd.exec(callback); + LogUtils.followOutput(dockerClient, containerId, consumer, types); } /** diff --git a/core/src/main/java/org/testcontainers/utility/LogUtils.java b/core/src/main/java/org/testcontainers/utility/LogUtils.java new file mode 100644 index 00000000000..30db5a0a972 --- /dev/null +++ b/core/src/main/java/org/testcontainers/utility/LogUtils.java @@ -0,0 +1,37 @@ +package org.testcontainers.utility; + +import com.github.dockerjava.api.DockerClient; +import com.github.dockerjava.api.command.LogContainerCmd; +import lombok.experimental.UtilityClass; +import org.testcontainers.containers.output.FrameConsumerResultCallback; +import org.testcontainers.containers.output.OutputFrame; + +import java.util.function.Consumer; + +import static org.testcontainers.containers.output.OutputFrame.OutputType.STDERR; +import static org.testcontainers.containers.output.OutputFrame.OutputType.STDOUT; + +/** + * Provides utility methods for logging. + */ +@UtilityClass +public class LogUtils { + /** + * {@inheritDoc} + */ + public void followOutput(DockerClient dockerClient, String containerId, + Consumer consumer, OutputFrame.OutputType... types) { + + final LogContainerCmd cmd = dockerClient.logContainerCmd(containerId) + .withFollowStream(true); + + final FrameConsumerResultCallback callback = new FrameConsumerResultCallback(); + for (OutputFrame.OutputType type : types) { + callback.addConsumer(type, consumer); + if (type == STDOUT) cmd.withStdOut(true); + if (type == STDERR) cmd.withStdErr(true); + } + + cmd.exec(callback); + } +}