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 27b3679b626..426acde48ba 100644 --- a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java +++ b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java @@ -4,9 +4,14 @@ import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy; import org.testcontainers.utility.DockerImageName; +import lombok.NonNull; + import java.time.Duration; import java.time.temporal.ChronoUnit; +import java.util.Map; +import java.util.HashMap; import java.util.Set; +import java.util.stream.Collectors; /** * Testcontainers implementation for PostgreSQL. @@ -39,7 +44,7 @@ public class PostgreSQLContainer> extends private String password = "test"; - private static final String FSYNC_OFF_OPTION = "fsync=off"; + private final Map CONFIG_OPTIONS = new HashMap<>(Map.of("fsync", "off")); /** * @deprecated use {@link #PostgreSQLContainer(DockerImageName)} or {@link #PostgreSQLContainer(String)} instead @@ -62,7 +67,6 @@ public PostgreSQLContainer(final DockerImageName dockerImageName) { .withRegEx(".*database system is ready to accept connections.*\\s") .withTimes(2) .withStartupTimeout(Duration.of(60, ChronoUnit.SECONDS)); - this.setCommand("postgres", "-c", FSYNC_OFF_OPTION); addExposedPort(POSTGRESQL_PORT); } @@ -85,6 +89,35 @@ protected void configure() { addEnv("POSTGRES_DB", databaseName); addEnv("POSTGRES_USER", username); addEnv("POSTGRES_PASSWORD", password); + + // If user never configured a command, and CONFIG_OPTIONS exist, then generate command + if (this.getContainerDef().getCommand().length == 0 && CONFIG_OPTIONS.size() > 0) { + this.setCommand("postgres -c " + CONFIG_OPTIONS.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining(" -c "))); + } + } + + /** + * Collects a set of configurations that will be set as a startup command of the PostgreSQL container. + * Note: configuration options will be ignored if a command is set using either withCommand() or setCommand() + * + *
+     * For example:
+     *   postgresql.withConfigOption("max_prepared_transactions", "5").withConfigOption("log_destination", "'syslog'");
+     * 
+     * Will result in the startup command:
+     *   postgres -c max_prepared_transactions=5 -c log_destination='syslog'
+     * 
+ * + * @param key The configuration name + * @param value the configuration value + * @return self + */ + public SELF withConfigOption(@NonNull String key, @NonNull String value) { + if(!key.matches("[a-zA-Z0-9_]")) { + throw new IllegalArgumentException("PostgreSQL configuration option with key: " + key + " is an invalid configuration string."); + } + CONFIG_OPTIONS.put(key, value); + return self(); } @Override 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 93e49d2d24c..c1e9a6d8ffc 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 @@ -60,6 +60,25 @@ public void testUnsetCommand() throws SQLException { } } + @Test + public void testWithConfigOption() throws SQLException { + try ( + PostgreSQLContainer postgres = new PostgreSQLContainer<>(PostgreSQLTestImages.POSTGRES_TEST_IMAGE) + .withConfigOption("max_connections", "42") + ) { + postgres.start(); + + ResultSet resultSet = performQuery(postgres, "SELECT current_setting('max_connections')"); + String result = resultSet.getString(1); + assertThat(result).as("max_connections should be overriden").isEqualTo("42"); + + // Ensure default config + resultSet = performQuery(postgres, "SELECT current_setting('fsync')"); + result = resultSet.getString(1); + assertThat(result).as("fsync should not overriden").isEqualTo("off"); + } + } + @Test public void testMissingInitScript() { try (