Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test-integration-unreleased.yml
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ jobs:
- name: Run Integration Test
run: |
cd opensearch-java
./gradlew clean integrationTest -Dhttps=false
./gradlew clean integrationTest -Dhttps=false -Dtests.opensearch.testcontainers.enabled=false
- name: Upload Reports
if: failure()
Expand Down
25 changes: 1 addition & 24 deletions .github/workflows/test-integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,27 +40,8 @@ jobs:
java-version: ${{ matrix.entry.java }}
distribution: 'temurin'
cache: 'gradle'
- name: Run Docker
run: |
echo "PASSWORD=admin" >> $GITHUB_ENV
docker info
docker compose --project-directory .ci/opensearch build --build-arg OPENSEARCH_VERSION=${{ matrix.entry.opensearch_version }}
docker compose --project-directory .ci/opensearch up -d
sleep 60

- name: Sets password (new versions)
run: |
VERSION_COMPONENTS=(${OPENSEARCH_VERSION//./ })
MAJOR_VERSION=${VERSION_COMPONENTS[0]}
MINOR_VERSION=${VERSION_COMPONENTS[1]}
if (( $MAJOR_VERSION > 2 || ( $MAJOR_VERSION == 2 && $MINOR_VERSION >= 12 ) )); then
echo "PASSWORD=0_aD^min_0" >> $GITHUB_ENV
fi
env:
OPENSEARCH_VERSION: ${{ matrix.entry.opensearch_version }}

- name: Run Integration Test
run: ./gradlew clean integrationTest -Dpassword=${{ env.PASSWORD }}
run: ./gradlew clean integrationTest -Dtests.opensearch.version=${{ matrix.entry.opensearch_version }}

- name: Upload Reports
if: failure()
Expand All @@ -69,7 +50,3 @@ jobs:
name: test-reports-os${{ matrix.entry.opensearch_version }}-java${{ matrix.entry.java }}
path: java-client/build/reports/
retention-days: 7

- name: Stop Docker
run: |
docker compose --project-directory .ci/opensearch down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
- Bump `org.apache.httpcomponents.client5:httpclient5` from 5.6 to 5.6.1 ([#1967](https://github.com/opensearch-project/opensearch-java/pull/1967))

### Added
- Run Java client integration tests with a Testcontainers-managed OpenSearch instance by default ([#2033](https://github.com/opensearch-project/opensearch-java/pull/2033))
- Detect AWS SDK `Apache5HttpClient` in `AwsSdk2Transport` body-method guardrail ([#1903](https://github.com/opensearch-project/opensearch-java/pull/1970))
- Support Jackson 3.x release line ([#1810](https://github.com/opensearch-project/opensearch-java/pull/1810))
- Added `equals()` and `hashCode()` implementations to `FieldValue` ([#1998](https://github.com/opensearch-project/opensearch-java/pull/1998))
Expand Down
21 changes: 16 additions & 5 deletions DEVELOPER_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,17 +52,28 @@ To run unit tests for the java-client:

#### Integration Tests

To run integration tests for the java-client, start an OpenSearch cluster using docker and pass the OpenSearch version:
To run integration tests for the java-client:

```
docker-compose --project-directory .ci/opensearch build --build-arg OPENSEARCH_VERSION=1.3.0
docker-compose --project-directory .ci/opensearch up -d
./gradlew clean integrationTest
```

Run integration tests after starting OpenSearch cluster:
By default, the integration test task starts a single OpenSearch test container for the test JVM. To test against a specific OpenSearch image version, pass the OpenSearch version:

```
./gradlew clean integrationTest
./gradlew clean integrationTest -Dtests.opensearch.version=3.2.0
```

To pass the full official OpenSearch image name, use:

```
./gradlew clean integrationTest -Dtests.opensearch.image=opensearchproject/opensearch:3.2.0
```

To run against an already running cluster, disable the test container and pass the cluster endpoint if it is not `localhost:9200`:

```
./gradlew clean integrationTest -Dtests.opensearch.testcontainers.enabled=false -Dtests.rest.cluster=localhost:9200
```

#### AWS Transport Integration Tests
Expand Down
23 changes: 20 additions & 3 deletions java-client/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,9 @@ tasks.build {
dependsOn("spotlessJavaCheck")
}

val opensearchVersion = "3.5.0-SNAPSHOT"
val opensearchDockerVersion = opensearchVersion.removeSuffix("-SNAPSHOT")

tasks.test {
systemProperty("tests.security.manager", "false")

Expand All @@ -168,6 +171,16 @@ val integrationTest = task<Test>("integrationTest") {
systemProperty("https", System.getProperty("https", "true"))
systemProperty("user", System.getProperty("user", "admin"))
systemProperty("password", System.getProperty("password", "admin"))
systemProperty(
"tests.opensearch.testcontainers.enabled",
System.getProperty("tests.opensearch.testcontainers.enabled", "true")
)
systemProperty(
"tests.opensearch.version",
System.getProperty("tests.opensearch.version", opensearchDockerVersion)
)
System.getProperty("tests.rest.cluster")?.let { systemProperty("tests.rest.cluster", it) }
System.getProperty("tests.opensearch.image")?.let { systemProperty("tests.opensearch.image", it) }
systemProperty("tests.awsSdk2support.domainHost",
System.getProperty("tests.awsSdk2support.domainHost", null))
systemProperty("tests.awsSdk2support.serviceName",
Expand All @@ -176,8 +189,6 @@ val integrationTest = task<Test>("integrationTest") {
System.getProperty("tests.awsSdk2support.domainRegion", "us-east-1"))
}

val opensearchVersion = "3.5.0-SNAPSHOT"

dependencies {
val jacksonVersion = "2.21.2"
val jacksonDatabindVersion = "2.21.2"
Expand Down Expand Up @@ -387,6 +398,12 @@ if (runtimeJavaVersion >= JavaVersion.VERSION_21) {
testImplementation("org.opensearch.test", "framework", opensearchVersion) {
exclude(group = "org.hamcrest")
}
// opensearch-testcontainers exposes OpenSearchContainer as a GenericContainer subclass, but publishes
Comment thread
lsh1215 marked this conversation as resolved.
Outdated
// Testcontainers as a runtime dependency. javac needs the superclass API for inherited methods.
"java21CompileOnly"("org.testcontainers:testcontainers:2.0.3") {
isTransitive = false
}
"java21Implementation"("org.opensearch:opensearch-testcontainers:4.1.0")
}

tasks.named<JavaCompile>("compileJava21Java") {
Expand All @@ -408,4 +425,4 @@ if (runtimeJavaVersion >= JavaVersion.VERSION_21) {
testClassesDirs += java21.output.classesDirs
classpath = sourceSets["java21"].runtimeClasspath
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

package org.opensearch.client.opensearch.integTest;

import com.carrotsearch.randomizedtesting.annotations.ThreadLeakFilters;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
Expand All @@ -30,6 +31,7 @@
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.opensearch.Version;
import org.opensearch.client.RestClient;
import org.opensearch.client.RestClientBuilder;
Expand All @@ -45,6 +47,7 @@
import org.opensearch.common.settings.Settings;
import org.opensearch.test.rest.OpenSearchRestTestCase;

@ThreadLeakFilters(filters = TestcontainersThreadFilter.class)
public abstract class OpenSearchJavaClientTestCase extends OpenSearchRestTestCase implements OpenSearchTransportSupport {
private static final List<String> systemIndices = List.of(
".opensearch-observability",
Expand All @@ -59,6 +62,12 @@ public abstract class OpenSearchJavaClientTestCase extends OpenSearchRestTestCas
private static TreeSet<Version> nodeVersions;
private static List<HttpHost> clusterHosts;

// Superclass setup reads tests.rest.cluster before subclass @Before methods run.
@BeforeClass
public static void startOpenSearchTestContainer() {
OpenSearchTestContainer.startIfNeeded();
}

@Before
public void initJavaClient() throws IOException {
if (javaClient == null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/

package org.opensearch.client.opensearch.integTest;

import org.opensearch.testcontainers.OpenSearchContainer;

final class OpenSearchTestContainer {
Comment thread
lsh1215 marked this conversation as resolved.
Outdated
static final String ENABLED_PROPERTY = "tests.opensearch.testcontainers.enabled";
static final String VERSION_PROPERTY = "tests.opensearch.version";
static final String IMAGE_PROPERTY = "tests.opensearch.image";
static final String CLUSTER_PROPERTY = "tests.rest.cluster";
static final String HTTPS_PROPERTY = "https";
static final String USER_PROPERTY = "user";
static final String PASSWORD_PROPERTY = "password";

private static final String DEFAULT_IMAGE = "opensearchproject/opensearch";
private static final String DEFAULT_ADMIN_PASSWORD = "admin";
private static final int HTTP_PORT = 9200;

private static OpenSearchContainer<?> container;

private OpenSearchTestContainer() {}

static synchronized void startIfNeeded() {
if (hasText(System.getProperty(CLUSTER_PROPERTY)) || !testcontainersEnabled()) {
return;
}

if (container == null) {
String version = System.getProperty(VERSION_PROPERTY);
String imageName = resolveImageName(version, System.getProperty(IMAGE_PROPERTY));
OpenSearchContainer<?> openSearch = createContainer(imageName);
openSearch.start();
container = openSearch;
}

System.setProperty(CLUSTER_PROPERTY, container.getHost() + ":" + container.getMappedPort(HTTP_PORT));
System.setProperty(HTTPS_PROPERTY, Boolean.toString(container.isSecurityEnabled()));
System.setProperty(USER_PROPERTY, container.getUsername());
System.setProperty(PASSWORD_PROPERTY, container.getPassword());
}

private static OpenSearchContainer<?> createContainer(String imageName) {
OpenSearchContainer<?> openSearch = new OpenSearchContainer<>(imageName).withSecurityEnabled()
.withEnv("bootstrap.memory_lock", "true")
.withEnv("cluster.routing.allocation.disk.threshold_enabled", "false");

String configuredPassword = System.getProperty(PASSWORD_PROPERTY);
if (hasText(configuredPassword) && !DEFAULT_ADMIN_PASSWORD.equals(configuredPassword)) {
openSearch.withEnv("OPENSEARCH_INITIAL_ADMIN_PASSWORD", configuredPassword);
}

return openSearch;
}

static String resolveImageName(String version, String image) {
if (hasText(image)) {
return image;
}
if (!hasText(version)) {
throw new IllegalStateException("Missing " + VERSION_PROPERTY + " for OpenSearch Testcontainers image");
}
return DEFAULT_IMAGE + ":" + version;
}

private static boolean testcontainersEnabled() {
return Boolean.parseBoolean(System.getProperty(ENABLED_PROPERTY, "true"));
}

private static boolean hasText(String value) {
return value != null && !value.isBlank();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/

package org.opensearch.client.opensearch.integTest;

import static org.junit.Assert.assertEquals;

import org.junit.Test;

public class OpenSearchTestContainerTests {
Comment thread
lsh1215 marked this conversation as resolved.
Outdated

@Test
public void defaultImageUsesOfficialOpenSearchImage() {
assertEquals("opensearchproject/opensearch:3.2.0", OpenSearchTestContainer.resolveImageName("3.2.0", null));
}

@Test
public void configuredImageTakesPrecedenceOverVersion() {
assertEquals(
"opensearchproject/opensearch:2.19.2",
OpenSearchTestContainer.resolveImageName("3.2.0", "opensearchproject/opensearch:2.19.2")
);
}

@Test
public void missingVersionWithoutCustomImageFailsFast() {
try {
OpenSearchTestContainer.resolveImageName(null, null);
} catch (IllegalStateException e) {
assertEquals("Missing tests.opensearch.version for OpenSearch Testcontainers image", e.getMessage());
return;
}

throw new AssertionError("Expected missing OpenSearch version to fail");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/

package org.opensearch.client.opensearch.integTest;

import com.carrotsearch.randomizedtesting.ThreadFilter;

public final class TestcontainersThreadFilter implements ThreadFilter {
Comment thread
lsh1215 marked this conversation as resolved.
@Override
public boolean reject(Thread thread) {
String name = thread.getName();
// Testcontainers owns these helper threads and Ryuk cleans them up after the JVM exits.
return "testcontainers-ryuk".equals(name) || name.startsWith("testcontainers-pull-watchdog-") || name.startsWith("ducttape-");
}
}
Loading