From 0035c311b8cd3dc342a092992efdf39d158264b6 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Sat, 6 Jun 2020 09:35:01 +0200 Subject: [PATCH] Avoid connection leaks in `LogUtils` (#2834) --- .../wait/strategy/LogMessageWaitStrategy.java | 37 ++++++++++++++----- .../org/testcontainers/utility/LogUtils.java | 26 ++++++++----- 2 files changed, 44 insertions(+), 19 deletions(-) diff --git a/core/src/main/java/org/testcontainers/containers/wait/strategy/LogMessageWaitStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/strategy/LogMessageWaitStrategy.java index bd164096300..9f7a0078f00 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/strategy/LogMessageWaitStrategy.java +++ b/core/src/main/java/org/testcontainers/containers/wait/strategy/LogMessageWaitStrategy.java @@ -1,15 +1,21 @@ package org.testcontainers.containers.wait.strategy; +import com.github.dockerjava.api.command.LogContainerCmd; +import lombok.SneakyThrows; import org.testcontainers.DockerClientFactory; import org.testcontainers.containers.ContainerLaunchException; +import org.testcontainers.containers.output.FrameConsumerResultCallback; import org.testcontainers.containers.output.OutputFrame; import org.testcontainers.containers.output.WaitingConsumer; -import org.testcontainers.utility.LogUtils; +import java.io.IOException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.function.Predicate; +import static org.testcontainers.containers.output.OutputFrame.OutputType.STDERR; +import static org.testcontainers.containers.output.OutputFrame.OutputType.STDOUT; + public class LogMessageWaitStrategy extends AbstractWaitStrategy { private String regEx; @@ -17,18 +23,31 @@ public class LogMessageWaitStrategy extends AbstractWaitStrategy { private int times = 1; @Override + @SneakyThrows(IOException.class) protected void waitUntilReady() { WaitingConsumer waitingConsumer = new WaitingConsumer(); - LogUtils.followOutput(DockerClientFactory.instance().client(), waitStrategyTarget.getContainerId(), waitingConsumer); - Predicate waitPredicate = outputFrame -> - // (?s) enables line terminator matching (equivalent to Pattern.DOTALL) - outputFrame.getUtf8String().matches("(?s)" + regEx); + LogContainerCmd cmd = DockerClientFactory.instance().client().logContainerCmd(waitStrategyTarget.getContainerId()) + .withFollowStream(true) + .withSince(0) + .withStdOut(true) + .withStdErr(true); + + try (FrameConsumerResultCallback callback = new FrameConsumerResultCallback()) { + callback.addConsumer(STDOUT, waitingConsumer); + callback.addConsumer(STDERR, waitingConsumer); + + cmd.exec(callback); + + Predicate waitPredicate = outputFrame -> + // (?s) enables line terminator matching (equivalent to Pattern.DOTALL) + outputFrame.getUtf8String().matches("(?s)" + regEx); - try { - waitingConsumer.waitUntil(waitPredicate, startupTimeout.getSeconds(), TimeUnit.SECONDS, times); - } catch (TimeoutException e) { - throw new ContainerLaunchException("Timed out waiting for log output matching '" + regEx + "'"); + try { + waitingConsumer.waitUntil(waitPredicate, startupTimeout.getSeconds(), TimeUnit.SECONDS, times); + } catch (TimeoutException e) { + throw new ContainerLaunchException("Timed out waiting for log output matching '" + regEx + "'"); + } } } diff --git a/core/src/main/java/org/testcontainers/utility/LogUtils.java b/core/src/main/java/org/testcontainers/utility/LogUtils.java index b51325cdda7..ce27387490f 100644 --- a/core/src/main/java/org/testcontainers/utility/LogUtils.java +++ b/core/src/main/java/org/testcontainers/utility/LogUtils.java @@ -2,12 +2,15 @@ import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.command.LogContainerCmd; +import lombok.SneakyThrows; import lombok.experimental.UtilityClass; import org.testcontainers.containers.output.FrameConsumerResultCallback; import org.testcontainers.containers.output.OutputFrame; import org.testcontainers.containers.output.ToStringConsumer; import org.testcontainers.containers.output.WaitingConsumer; +import java.io.Closeable; +import java.io.IOException; import java.util.function.Consumer; import static org.testcontainers.containers.output.OutputFrame.OutputType.STDERR; @@ -59,6 +62,7 @@ public void followOutput(DockerClient dockerClient, * @param types types of {@link OutputFrame} to receive * @return all previous output frames (stdout/stderr being separated by newline characters) */ + @SneakyThrows(IOException.class) public String getOutput(DockerClient dockerClient, String containerId, OutputFrame.OutputType... types) { @@ -73,17 +77,19 @@ public String getOutput(DockerClient dockerClient, final ToStringConsumer consumer = new ToStringConsumer(); final WaitingConsumer wait = new WaitingConsumer(); - attachConsumer(dockerClient, containerId, consumer.andThen(wait), false, types); - - wait.waitUntilEnd(); - return consumer.toUtf8String(); + try (Closeable closeable = attachConsumer(dockerClient, containerId, consumer.andThen(wait), false, types)) { + wait.waitUntilEnd(); + return consumer.toUtf8String(); + } } - private static void attachConsumer(DockerClient dockerClient, - String containerId, - Consumer consumer, - boolean followStream, - OutputFrame.OutputType... types) { + private static Closeable attachConsumer( + DockerClient dockerClient, + String containerId, + Consumer consumer, + boolean followStream, + OutputFrame.OutputType... types + ) { final LogContainerCmd cmd = dockerClient.logContainerCmd(containerId) .withFollowStream(followStream) @@ -96,6 +102,6 @@ private static void attachConsumer(DockerClient dockerClient, if (type == STDERR) cmd.withStdErr(true); } - cmd.exec(callback); + return cmd.exec(callback); } }