diff --git a/.github/workflows/main_and_pr_workflow.yml b/.github/workflows/main_and_pr_workflow.yml index b0dfe23..6c21bd0 100644 --- a/.github/workflows/main_and_pr_workflow.yml +++ b/.github/workflows/main_and_pr_workflow.yml @@ -9,7 +9,7 @@ jobs: strategy: fail-fast: false matrix: - java-version: [ 11, 17, 21 ] + java-version: [ 11, 17, 21, 23 ] runs-on: [ubuntu-latest] name: Build on ${{ matrix.runs-on }} with jdk ${{ matrix.java-version }} runs-on: ${{ matrix.runs-on }} diff --git a/src/main/java/org/opensearch/testcontainers/OpensearchContainer.java b/src/main/java/org/opensearch/testcontainers/OpensearchContainer.java index d8d6d52..aa38b00 100644 --- a/src/main/java/org/opensearch/testcontainers/OpensearchContainer.java +++ b/src/main/java/org/opensearch/testcontainers/OpensearchContainer.java @@ -10,6 +10,7 @@ import java.net.InetSocketAddress; import java.time.Duration; +import java.util.regex.Pattern; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; import org.testcontainers.containers.wait.strategy.WaitStrategy; @@ -21,10 +22,16 @@ * (http/https) and 9300 (tcp, deprecated). */ public class OpensearchContainer> extends GenericContainer { + // The initial password is required starting from OpenSearch 2.12.0 + private static final Pattern OPENSEARCH_INITIAL_PASSWORD_VERSION = Pattern.compile( + "^(([3-9][.]\\d+[.]\\d+|[2][.][1][2-9]+[.]\\d+|[2][.][2-9]\\d+[.]\\d+)(-SNAPSHOT)?|latest)$"); + // Default username to connect to Opensearch instance private static final String DEFAULT_USER = "admin"; // Default password to connect to Opensearch instance private static final String DEFAULT_PASSWORD = "admin"; + // Default initial password to connect to Opensearch instance + private static final String DEFAULT_INITIAL_PASSWORD = "_ad0m#Ns_"; // Default HTTP port. private static final int DEFAULT_HTTP_PORT = 9200; @@ -39,6 +46,8 @@ public class OpensearchContainer> extends // HTTPs, // along with Basic Auth being used. private boolean disableSecurity = true; + private boolean requireInitialPassword = false; + private String password = DEFAULT_PASSWORD; /** * Create an Opensearch Container by passing the full docker image name. @@ -65,6 +74,14 @@ public OpensearchContainer(String dockerImageName) { public OpensearchContainer(final DockerImageName dockerImageName) { super(dockerImageName); dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); + + final String version = dockerImageName.getVersionPart(); + if (version == null || version.isBlank()) { + requireInitialPassword = false; /* we don't know the version */ + } else { + requireInitialPassword = + OPENSEARCH_INITIAL_PASSWORD_VERSION.matcher(version).matches(); + } } /** @@ -85,6 +102,13 @@ protected void configure() { withEnv("discovery.type", "single-node"); if (disableSecurity) { withEnv("DISABLE_SECURITY_PLUGIN", Boolean.toString(disableSecurity)); + } else if (requireInitialPassword) { + // Check if the OPENSEARCH_INITIAL_ADMIN_PASSWORD is already provided + password = getEnvMap().get("OPENSEARCH_INITIAL_ADMIN_PASSWORD"); + if (password == null || password.isBlank()) { + withEnv("OPENSEARCH_INITIAL_ADMIN_PASSWORD", DEFAULT_INITIAL_PASSWORD); + password = DEFAULT_INITIAL_PASSWORD; + } } addExposedPorts(DEFAULT_HTTP_PORT, DEFAULT_TCP_PORT); @@ -96,7 +120,7 @@ protected void configure() { .usingTls() .allowInsecure() .forPort(DEFAULT_HTTP_PORT) - .withBasicCredentials(DEFAULT_USER, DEFAULT_PASSWORD) + .withBasicCredentials(DEFAULT_USER, password) .forStatusCodeMatching(response -> response == HTTP_OK || response == HTTP_UNAUTHORIZED) .withReadTimeout(Duration.ofSeconds(10)) .withStartupTimeout(Duration.ofMinutes(5)); @@ -153,6 +177,6 @@ public String getUsername() { * @return password to connect to Opensearch container */ public String getPassword() { - return DEFAULT_PASSWORD; + return password; } } diff --git a/src/test/java/org/opensearch/testcontainers/OpensearchContainerTest.java b/src/test/java/org/opensearch/testcontainers/OpensearchContainerTest.java index 89c3d07..1a027e3 100644 --- a/src/test/java/org/opensearch/testcontainers/OpensearchContainerTest.java +++ b/src/test/java/org/opensearch/testcontainers/OpensearchContainerTest.java @@ -13,6 +13,7 @@ import java.security.KeyManagementException; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; +import java.util.Map; import java.util.Optional; import java.util.stream.Stream; import javax.net.ssl.SSLContext; @@ -38,8 +39,10 @@ class OpensearchContainerTest { @DisplayName("Create default OpensearchContainer with security enabled") @ParameterizedTest(name = "Running Opensearch version={0} (security enabled)") @MethodSource("containers") - public void defaultWithSecurity(final String version, final DockerImageName image) throws Exception { - try (OpensearchContainer container = new OpensearchContainer<>(image).withSecurityEnabled()) { + public void defaultWithSecurity(final String version, final Map env, final DockerImageName image) + throws Exception { + try (OpensearchContainer container = + new OpensearchContainer<>(image).withEnv(env).withSecurityEnabled()) { container.start(); try (RestClient client = getClient(container)) { @@ -59,8 +62,9 @@ public void defaultWithSecurity(final String version, final DockerImageName imag @DisplayName("Create OpensearchContainer with security disabled") @ParameterizedTest(name = "Running Opensearch version={0} (security disabled)") @MethodSource("containers") - public void defaultNoSecurity(final String version, final DockerImageName image) throws Exception { - try (OpensearchContainer container = new OpensearchContainer<>(image)) { + public void defaultNoSecurity(final String version, final Map env, final DockerImageName image) + throws Exception { + try (OpensearchContainer container = new OpensearchContainer<>(image).withEnv(env)) { container.start(); try (RestClient client = getClient(container)) { @@ -81,16 +85,28 @@ private static Stream containers() { return Stream.of( Arguments.of( "1.3.4", + Map.of(), /* empty env */ DockerImageName.parse("opensearchproject/opensearch").withTag("1.3.4")), Arguments.of( "2.0.1", + Map.of(), /* empty env */ DockerImageName.parse("opensearchproject/opensearch").withTag("2.0.1")), Arguments.of( "2.1.0", + Map.of(), /* empty env */ DockerImageName.parse("opensearchproject/opensearch").withTag("2.1.0")), Arguments.of( "2.11.0", - DockerImageName.parse("opensearchproject/opensearch").withTag("2.11.0"))); + Map.of(), /* empty env */ + DockerImageName.parse("opensearchproject/opensearch").withTag("2.11.0")), + Arguments.of( + "2.15.0", + Map.of("OPENSEARCH_INITIAL_ADMIN_PASSWORD", "_oop0m#NsR_"), + DockerImageName.parse("opensearchproject/opensearch").withTag("2.15.0")), + Arguments.of( + "2.17.0", + Map.of(), /* empty env */ + DockerImageName.parse("opensearchproject/opensearch").withTag("2.17.0"))); } private RestClient getClient(OpensearchContainer container)