From 58ae898e7d659c7dcbb2f55e6a86e71f3d3266fd Mon Sep 17 00:00:00 2001 From: tomco Date: Mon, 17 Jul 2023 22:03:55 +0200 Subject: [PATCH] Create a Complex Wait Strategy and include it in the PostgreSQLContainer --- .../experimental/ComplexWaitStrategy.java | 41 +++++++++++++++++++ .../MultiLogMessageWaitStrategy.java | 1 - .../containers/PostgreSQLContainer.java | 16 ++++++-- .../postgresql/SimplePostgreSQLTest.java | 20 ++++++++- 4 files changed, 72 insertions(+), 6 deletions(-) create mode 100644 core/src/main/java/org/testcontainers/containers/wait/experimental/ComplexWaitStrategy.java diff --git a/core/src/main/java/org/testcontainers/containers/wait/experimental/ComplexWaitStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/experimental/ComplexWaitStrategy.java new file mode 100644 index 00000000000..dadbe0e88e0 --- /dev/null +++ b/core/src/main/java/org/testcontainers/containers/wait/experimental/ComplexWaitStrategy.java @@ -0,0 +1,41 @@ +package org.testcontainers.containers.wait.experimental; + +import org.rnorth.ducttape.unreliables.Unreliables; +import org.testcontainers.containers.output.WaitingConsumer; +import org.testcontainers.containers.wait.strategy.AbstractWaitStrategy; +import org.testcontainers.containers.wait.strategy.WaitStrategy; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + + +public class ComplexWaitStrategy extends AbstractWaitStrategy { + + private final List waitStrategyList = new ArrayList<>(); + + @Override + protected void waitUntilReady() { + ExecutorService service = Executors.newFixedThreadPool(waitStrategyList.size()); + List> futures = waitStrategyList.stream() + .map(waitStrategy -> service.submit(() -> waitStrategy.waitUntilReady(waitStrategyTarget))) + .collect(Collectors.toList()); + + Unreliables.retryUntilTrue( + (int) startupTimeout.getSeconds(), + TimeUnit.SECONDS, + () -> futures.stream().anyMatch(Future::isDone) + ); + } + + public ComplexWaitStrategy with(WaitStrategy waitStrategy) { + this.waitStrategyList.add(waitStrategy); + return this; + } +} diff --git a/core/src/main/java/org/testcontainers/containers/wait/experimental/MultiLogMessageWaitStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/experimental/MultiLogMessageWaitStrategy.java index 76fecf4286f..b8f31c0cd9d 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/experimental/MultiLogMessageWaitStrategy.java +++ b/core/src/main/java/org/testcontainers/containers/wait/experimental/MultiLogMessageWaitStrategy.java @@ -51,7 +51,6 @@ protected String timeoutErrorMessage() { } public MultiLogMessageWaitStrategy withRegex(String... regEx) { - // TODO, add validation that we have at least one regex. :-) this.regEx = new LinkedBlockingDeque<>(Arrays.asList(regEx)); return this; } diff --git a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java index d8f974ad2d5..75ec6050d00 100644 --- a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java +++ b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java @@ -1,6 +1,8 @@ package org.testcontainers.containers; import org.jetbrains.annotations.NotNull; +import org.testcontainers.containers.wait.experimental.ComplexWaitStrategy; +import org.testcontainers.containers.wait.experimental.MultiLogMessageWaitStrategy; import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy; import org.testcontainers.utility.DockerImageName; @@ -49,10 +51,16 @@ public PostgreSQLContainer(final DockerImageName dockerImageName) { dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); this.waitStrategy = - new LogMessageWaitStrategy() - .withRegEx(".*database system is ready to accept connections.*\\s") - .withTimes(2) - .withStartupTimeout(Duration.of(60, ChronoUnit.SECONDS)); + new ComplexWaitStrategy() + .with(new LogMessageWaitStrategy() + .withRegEx(".*database system is ready to accept connections.*\\s") + .withTimes(2) + .withStartupTimeout(Duration.of(60, ChronoUnit.SECONDS))) + .with(new MultiLogMessageWaitStrategy() + .withRegex(".*PostgreSQL Database directory appears to contain a database; Skipping initialization.*", + ".*database system is ready to accept connections.*\\s") + .withStartupTimeout(Duration.of(60, ChronoUnit.SECONDS))) +; this.setCommand("postgres", "-c", FSYNC_OFF_OPTION); addExposedPort(POSTGRESQL_PORT); diff --git a/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/SimplePostgreSQLTest.java b/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/SimplePostgreSQLTest.java index 84beb779fa4..c8b81ae8f9d 100644 --- a/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/SimplePostgreSQLTest.java +++ b/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/SimplePostgreSQLTest.java @@ -4,6 +4,7 @@ import org.testcontainers.PostgreSQLTestImages; import org.testcontainers.containers.PostgreSQLContainer; import org.testcontainers.db.AbstractContainerDatabaseTest; +import org.testcontainers.utility.DockerImageName; import java.sql.ResultSet; import java.sql.SQLException; @@ -11,6 +12,7 @@ import java.util.logging.LogManager; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertEquals; public class SimplePostgreSQLTest extends AbstractContainerDatabaseTest { static { @@ -20,7 +22,23 @@ public class SimplePostgreSQLTest extends AbstractContainerDatabaseTest { @Test public void testSimple() throws SQLException { - try (PostgreSQLContainer postgres = new PostgreSQLContainer<>(PostgreSQLTestImages.POSTGRES_TEST_IMAGE)) { + try (PostgreSQLContainer postgres = new PostgreSQLContainer<>(DockerImageName.parse("postgres:14"))) { + postgres.start(); + + ResultSet resultSet = performQuery(postgres, "SELECT 1"); + int resultSetInt = resultSet.getInt(1); + assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + } + } + + @Test + public void testSimpleWithData() throws SQLException { + DockerImageName IMAGE = DockerImageName.parse("tomcools/postgres:main") + .asCompatibleSubstituteFor("postgres"); + try (PostgreSQLContainer postgres = new PostgreSQLContainer<>(IMAGE) + .withDatabaseName("testcontainer") + .withUsername("sa") + .withPassword("sa")) { postgres.start(); ResultSet resultSet = performQuery(postgres, "SELECT 1");