From 4653e82e1acdbf63a7397e1e8a3c0d45f7937e45 Mon Sep 17 00:00:00 2001 From: Yury Fridlyand Date: Tue, 2 Aug 2022 19:31:10 -0700 Subject: [PATCH 1/2] Removed backward compatibility (BWC) tests, because `OpenDistro` was archived and not available anymore for testing. Signed-off-by: Yury Fridlyand --- .../workflows/sql-test-and-build-workflow.yml | 3 - integ-test/build.gradle | 162 ------------- .../sql/bwc/SQLBackwardsCompatibilityIT.java | 214 ------------------ .../sql/legacy/SQLIntegTestCase.java | 6 +- scripts/bwctest.sh | 58 ----- 5 files changed, 2 insertions(+), 441 deletions(-) delete mode 100644 integ-test/src/test/java/org/opensearch/sql/bwc/SQLBackwardsCompatibilityIT.java delete mode 100755 scripts/bwctest.sh diff --git a/.github/workflows/sql-test-and-build-workflow.yml b/.github/workflows/sql-test-and-build-workflow.yml index fcc63433a8f..70d1c3a3e59 100644 --- a/.github/workflows/sql-test-and-build-workflow.yml +++ b/.github/workflows/sql-test-and-build-workflow.yml @@ -22,9 +22,6 @@ jobs: - name: Build with Gradle run: ./gradlew build assemble - - name: Run backward compatibility tests - run: ./scripts/bwctest.sh - - name: Create Artifact Path run: | mkdir -p opensearch-sql-builds diff --git a/integ-test/build.gradle b/integ-test/build.gradle index e640c41025a..1b2998ccd29 100644 --- a/integ-test/build.gradle +++ b/integ-test/build.gradle @@ -123,12 +123,6 @@ integTest { jvmArgs '-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:5005' } - if (System.getProperty("tests.rest.bwcsuite") == null) { - filter { - excludeTestsMatching "org.opensearch.sql.bwc.*IT" - } - } - exclude 'org/opensearch/sql/doctest/**/*IT.class' exclude 'org/opensearch/sql/correctness/**' @@ -187,156 +181,6 @@ task compileJdbc(type: Exec) { } } -String bwcVersion = "1.13.2.0"; -String baseName = "sqlBwcCluster" -String bwcFilePath = "src/test/resources/bwc/" -String bwcOpenDistroPlugin = "opendistro-sql-" + bwcVersion + ".zip" -String bwcRemoteFile = 'https://d3g5vo6xdbdb9a.cloudfront.net/downloads/elasticsearch-plugins/opendistro-sql/' + bwcOpenDistroPlugin - -2.times { i -> - testClusters { - "${baseName}$i" { - testDistribution = "ARCHIVE" - versions = ["7.10.2", opensearch_version] - numberOfNodes = 3 - plugin(provider(new Callable() { - @Override - RegularFile call() throws Exception { - return new RegularFile() { - @Override - File getAsFile() { - File dir = new File('./integ-test/' + bwcFilePath + bwcVersion) - if (!dir.exists()) { - dir.mkdirs() - } - File f = new File(dir, bwcOpenDistroPlugin) - if (!f.exists()) { - new URL(bwcRemoteFile).withInputStream{ ins -> f.withOutputStream{ it << ins }} - } - return fileTree(bwcFilePath + bwcVersion).getSingleFile() - } - } - } - })) - setting 'path.repo', "${buildDir}/cluster/shared/repo/${baseName}" - setting 'http.content_type.required', 'true' - } - } -} - -List> plugins = [ - provider(new Callable() { - @Override - RegularFile call() throws Exception { - return new RegularFile() { - @Override - File getAsFile() { - return fileTree(bwcFilePath + project.version).getSingleFile() - } - } - } - }) -] - -// Creates 2 test clusters with 3 nodes of the old version. -2.times { i -> - task "${baseName}#oldVersionClusterTask$i"(type: StandaloneRestIntegTestTask) { - useCluster testClusters."${baseName}$i" - filter { - includeTestsMatching "org.opensearch.sql.bwc.*IT" - } - systemProperty 'tests.rest.bwcsuite', 'old_cluster' - systemProperty 'tests.rest.bwcsuite_round', 'old' - systemProperty 'tests.plugin_bwc_version', bwcVersion - nonInputProperties.systemProperty('tests.rest.cluster', "${-> testClusters."${baseName}$i".allHttpSocketURI.join(",")}") - nonInputProperties.systemProperty('tests.clustername', "${-> testClusters."${baseName}$i".getName()}") - } -} - -// Upgrade one node of the old cluster to new OpenSearch version with upgraded plugin version. -// This results in a mixed cluster with 2 nodes on the old version and 1 upgraded node. -// This is also used as a one third upgraded cluster for a rolling upgrade. -task "${baseName}#mixedClusterTask"(type: StandaloneRestIntegTestTask) { - useCluster testClusters."${baseName}0" - dependsOn "${baseName}#oldVersionClusterTask0" - doFirst { - testClusters."${baseName}0".upgradeNodeAndPluginToNextVersion(plugins) - } - filter { - includeTestsMatching "org.opensearch.sql.bwc.*IT" - } - systemProperty 'tests.rest.bwcsuite', 'mixed_cluster' - systemProperty 'tests.rest.bwcsuite_round', 'first' - systemProperty 'tests.plugin_bwc_version', bwcVersion - nonInputProperties.systemProperty('tests.rest.cluster', "${-> testClusters."${baseName}0".allHttpSocketURI.join(",")}") - nonInputProperties.systemProperty('tests.clustername', "${-> testClusters."${baseName}0".getName()}") -} - -// Upgrade the second node to new OpenSearch version with upgraded plugin version after the first node is upgraded. -// This results in a mixed cluster with 1 node on the old version and 2 upgraded nodes. -// This is used for rolling upgrade. -task "${baseName}#twoThirdsUpgradedClusterTask"(type: StandaloneRestIntegTestTask) { - dependsOn "${baseName}#mixedClusterTask" - useCluster testClusters."${baseName}0" - doFirst { - testClusters."${baseName}0".upgradeNodeAndPluginToNextVersion(plugins) - } - filter { - includeTestsMatching "org.opensearch.sql.bwc.*IT" - } - systemProperty 'tests.rest.bwcsuite', 'mixed_cluster' - systemProperty 'tests.rest.bwcsuite_round', 'second' - systemProperty 'tests.plugin_bwc_version', bwcVersion - nonInputProperties.systemProperty('tests.rest.cluster', "${-> testClusters."${baseName}0".allHttpSocketURI.join(",")}") - nonInputProperties.systemProperty('tests.clustername', "${-> testClusters."${baseName}0".getName()}") -} - -// Upgrade the third node to new OpenSearch version with upgraded plugin version after the second node is upgraded. -// This results in a fully upgraded cluster. -// This is used for rolling upgrade. -task "${baseName}#rollingUpgradeClusterTask"(type: StandaloneRestIntegTestTask) { - dependsOn "${baseName}#twoThirdsUpgradedClusterTask" - useCluster testClusters."${baseName}0" - doFirst { - testClusters."${baseName}0".upgradeNodeAndPluginToNextVersion(plugins) - } - filter { - includeTestsMatching "org.opensearch.sql.bwc.*IT" - } - mustRunAfter "${baseName}#mixedClusterTask" - systemProperty 'tests.rest.bwcsuite', 'mixed_cluster' - systemProperty 'tests.rest.bwcsuite_round', 'third' - systemProperty 'tests.plugin_bwc_version', bwcVersion - nonInputProperties.systemProperty('tests.rest.cluster', "${-> testClusters."${baseName}0".allHttpSocketURI.join(",")}") - nonInputProperties.systemProperty('tests.clustername', "${-> testClusters."${baseName}0".getName()}") -} - -// Upgrade all the nodes of the old cluster to new OpenSearch version with upgraded plugin version -// at the same time resulting in a fully upgraded cluster. -task "${baseName}#fullRestartClusterTask"(type: StandaloneRestIntegTestTask) { - dependsOn "${baseName}#oldVersionClusterTask1" - useCluster testClusters."${baseName}1" - doFirst { - testClusters."${baseName}1".upgradeAllNodesAndPluginsToNextVersion(plugins) - } - filter { - includeTestsMatching "org.opensearch.sql.bwc.*IT" - } - systemProperty 'tests.rest.bwcsuite', 'upgraded_cluster' - systemProperty 'tests.plugin_bwc_version', bwcVersion - nonInputProperties.systemProperty('tests.rest.cluster', "${-> testClusters."${baseName}1".allHttpSocketURI.join(",")}") - nonInputProperties.systemProperty('tests.clustername', "${-> testClusters."${baseName}1".getName()}") -} - -// A bwc test suite which runs all the bwc tasks combined -task bwcTestSuite(type: StandaloneRestIntegTestTask) { - exclude '**/*Test*' - exclude '**/*IT*' - dependsOn tasks.named("${baseName}#mixedClusterTask") - dependsOn tasks.named("${baseName}#rollingUpgradeClusterTask") - dependsOn tasks.named("${baseName}#fullRestartClusterTask") -} - def opensearch_tmp_dir = rootProject.file('build/private/es_tmp').absoluteFile opensearch_tmp_dir.mkdirs() @@ -354,12 +198,6 @@ task integTestRemote(type: RestIntegTestTask) { // Set default query size limit systemProperty 'defaultQuerySizeLimit', '10000' - if (System.getProperty("tests.rest.bwcsuite") == null) { - filter { - excludeTestsMatching "org.opensearch.sql.bwc.*IT" - } - } - // Exclude the same tests that are excluded for integTest exclude 'org/opensearch/sql/doctest/**/*IT.class' exclude 'org/opensearch/sql/correctness/**' diff --git a/integ-test/src/test/java/org/opensearch/sql/bwc/SQLBackwardsCompatibilityIT.java b/integ-test/src/test/java/org/opensearch/sql/bwc/SQLBackwardsCompatibilityIT.java deleted file mode 100644 index 079980248f5..00000000000 --- a/integ-test/src/test/java/org/opensearch/sql/bwc/SQLBackwardsCompatibilityIT.java +++ /dev/null @@ -1,214 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - - -package org.opensearch.sql.bwc; - - -import org.json.JSONObject; -import org.junit.Assert; -import org.opensearch.client.Request; -import org.opensearch.client.RequestOptions; -import org.opensearch.client.Response; -import org.opensearch.common.settings.Settings; -import org.opensearch.sql.legacy.SQLIntegTestCase; -import org.opensearch.sql.legacy.TestsConstants; -import org.opensearch.test.rest.OpenSearchRestTestCase; - -import java.io.IOException; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; - -import static org.opensearch.sql.legacy.TestUtils.createIndexByRestClient; -import static org.opensearch.sql.legacy.TestUtils.isIndexExist; -import static org.opensearch.sql.legacy.TestUtils.loadDataByRestClient; -import static org.opensearch.sql.legacy.plugin.RestSqlAction.LEGACY_QUERY_API_ENDPOINT; -import static org.opensearch.sql.legacy.plugin.RestSqlAction.QUERY_API_ENDPOINT; -import static org.opensearch.sql.plugin.rest.RestQuerySettingsAction.LEGACY_SQL_SETTINGS_API_ENDPOINT; -import static org.opensearch.sql.util.MatcherUtils.rows; -import static org.opensearch.sql.util.MatcherUtils.schema; -import static org.opensearch.sql.util.MatcherUtils.verifyDataRows; -import static org.opensearch.sql.util.MatcherUtils.verifySchema; -import static org.opensearch.sql.util.TestUtils.getResponseBody; - -public class SQLBackwardsCompatibilityIT extends SQLIntegTestCase { - - private static final ClusterType CLUSTER_TYPE = ClusterType.parse(System.getProperty("tests.rest.bwcsuite")); - private static final String CLUSTER_NAME = System.getProperty("tests.clustername"); - - @Override - protected final boolean preserveIndicesUponCompletion() { - return true; - } - - @Override - protected final boolean preserveReposUponCompletion() { - return true; - } - - @Override - protected boolean preserveTemplatesUponCompletion() { - return true; - } - - @Override - protected final Settings restClientSettings() { - return Settings - .builder() - .put(super.restClientSettings()) - // increase the timeout here to 90 seconds to handle long waits for a green - // cluster health. the waits for green need to be longer than a minute to - // account for delayed shards - .put(OpenSearchRestTestCase.CLIENT_SOCKET_TIMEOUT, "90s") - .build(); - } - - private enum ClusterType { - OLD, - MIXED, - UPGRADED; - - public static ClusterType parse(String value) { - switch (value) { - case "old_cluster": - return OLD; - case "mixed_cluster": - return MIXED; - case "upgraded_cluster": - return UPGRADED; - default: - throw new AssertionError("unknown cluster type: " + value); - } - } - } - - @SuppressWarnings("unchecked") - public void testBackwardsCompatibility() throws Exception { - String uri = getUri(); - Map> responseMap = (Map>) getAsMap(uri).get("nodes"); - for (Map response : responseMap.values()) { - List> plugins = (List>) response.get("plugins"); - Set pluginNames = plugins.stream().map(map -> map.get("name")).collect(Collectors.toSet()); - switch (CLUSTER_TYPE) { - case OLD: - Assert.assertTrue(pluginNames.contains("opendistro-sql")); - updateLegacySQLSettings(); - loadIndex(Index.ACCOUNT); - verifySQLQueries(LEGACY_QUERY_API_ENDPOINT); - break; - case MIXED: - Assert.assertTrue(pluginNames.contains("opensearch-sql")); - verifySQLSettings(); - verifySQLQueries(LEGACY_QUERY_API_ENDPOINT); - break; - case UPGRADED: - Assert.assertTrue(pluginNames.contains("opensearch-sql")); - verifySQLSettings(); - verifySQLQueries(QUERY_API_ENDPOINT); - break; - } - break; - } - } - - private String getUri() { - switch (CLUSTER_TYPE) { - case OLD: - return "_nodes/" + CLUSTER_NAME + "-0/plugins"; - case MIXED: - String round = System.getProperty("tests.rest.bwcsuite_round"); - if (round.equals("second")) { - return "_nodes/" + CLUSTER_NAME + "-1/plugins"; - } else if (round.equals("third")) { - return "_nodes/" + CLUSTER_NAME + "-2/plugins"; - } else { - return "_nodes/" + CLUSTER_NAME + "-0/plugins"; - } - case UPGRADED: - return "_nodes/plugins"; - default: - throw new AssertionError("unknown cluster type: " + CLUSTER_TYPE); - } - } - - private void updateLegacySQLSettings() throws IOException { - Request request = new Request("PUT", LEGACY_SQL_SETTINGS_API_ENDPOINT); - request.setJsonEntity(String.format(Locale.ROOT, "{\n" + - " \"persistent\" : {\n" + - " \"%s\" : \"%s\"\n" + - " }\n" + - "}", "opendistro.sql.cursor.keep_alive", "7m")); - - RequestOptions.Builder restOptionsBuilder = RequestOptions.DEFAULT.toBuilder(); - restOptionsBuilder.addHeader("Content-Type", "application/json"); - request.setOptions(restOptionsBuilder); - - Response response = client().performRequest(request); - JSONObject jsonObject = new JSONObject(getResponseBody(response)); - Assert.assertTrue((boolean) jsonObject.get("acknowledged")); - } - - private void verifySQLSettings() throws IOException { - Request request = new Request("GET", "_cluster/settings?flat_settings"); - - RequestOptions.Builder restOptionsBuilder = RequestOptions.DEFAULT.toBuilder(); - restOptionsBuilder.addHeader("Content-Type", "application/json"); - request.setOptions(restOptionsBuilder); - - Response response = client().performRequest(request); - JSONObject jsonObject = new JSONObject(getResponseBody(response)); - Assert.assertEquals("{\"transient\":{},\"persistent\":{\"opendistro.sql.cursor.keep_alive\":\"7m\"}}", jsonObject.toString()); - } - - private void verifySQLQueries(String endpoint) throws IOException { - JSONObject filterResponse = executeSQLQuery(endpoint, "SELECT COUNT(*) FILTER(WHERE age > 35) FROM " + TestsConstants.TEST_INDEX_ACCOUNT); - verifySchema(filterResponse, schema("COUNT(*) FILTER(WHERE age > 35)", null, "integer")); - verifyDataRows(filterResponse, rows(238)); - - JSONObject aggResponse = executeSQLQuery(endpoint, "SELECT COUNT(DISTINCT age) FROM " + TestsConstants.TEST_INDEX_ACCOUNT); - verifySchema(aggResponse, schema("COUNT(DISTINCT age)", null, "integer")); - verifyDataRows(aggResponse, rows(21)); - - JSONObject groupByResponse = executeSQLQuery(endpoint, "select a.gender from " + TestsConstants.TEST_INDEX_ACCOUNT + " a group by a.gender having count(*) > 0"); - verifySchema(groupByResponse, schema("gender", null, "text")); - Assert.assertEquals("[[\"F\"],[\"M\"]]", groupByResponse.getJSONArray("datarows").toString()); - } - - private JSONObject executeSQLQuery(String endpoint, String query) throws IOException { - Request request = new Request("POST", endpoint); - request.setJsonEntity(String.format(Locale.ROOT, "{" + - " \"query\" : \"%s\"" + - "}", query)); - - RequestOptions.Builder restOptionsBuilder = RequestOptions.DEFAULT.toBuilder(); - restOptionsBuilder.addHeader("Content-Type", "application/json"); - request.setOptions(restOptionsBuilder); - - Response response = client().performRequest(request); - return new JSONObject(getResponseBody(response)); - } - - @Override - public boolean shouldResetQuerySizeLimit() { - return false; - } - - @Override - protected synchronized void loadIndex(Index index) throws IOException { - String indexName = index.getName(); - String mapping = index.getMapping(); - // current directory becomes 'integ-test/build/testrun/sqlBwcCluster#' during bwc - String dataSet = "../../../" + index.getDataSet(); - - if (!isIndexExist(client(), indexName)) { - createIndexByRestClient(client(), indexName, mapping); - loadDataByRestClient(client(), indexName, dataSet); - } - } - -} diff --git a/integ-test/src/test/java/org/opensearch/sql/legacy/SQLIntegTestCase.java b/integ-test/src/test/java/org/opensearch/sql/legacy/SQLIntegTestCase.java index 15f0261f0d0..7c4d1a93d13 100644 --- a/integ-test/src/test/java/org/opensearch/sql/legacy/SQLIntegTestCase.java +++ b/integ-test/src/test/java/org/opensearch/sql/legacy/SQLIntegTestCase.java @@ -139,10 +139,8 @@ public static void dumpCoverage() { */ @AfterClass public static void cleanUpIndices() throws IOException { - if (System.getProperty("tests.rest.bwcsuite") == null) { - wipeAllOpenSearchIndices(); - wipeAllClusterSettings(); - } + wipeAllOpenSearchIndices(); + wipeAllClusterSettings(); } protected void setQuerySizeLimit(Integer limit) throws IOException { diff --git a/scripts/bwctest.sh b/scripts/bwctest.sh deleted file mode 100755 index 4f017ae5e89..00000000000 --- a/scripts/bwctest.sh +++ /dev/null @@ -1,58 +0,0 @@ -#!/bin/bash - -set -e - -function usage() { - echo "" - echo "This script is used to run Backwards Compatibility tests" - echo "--------------------------------------------------------------------------" - echo "Usage: $0 [args]" - echo "" - echo "Required arguments:" - echo "None" - echo "" - echo -e "-h\tPrint this message." - echo "--------------------------------------------------------------------------" -} - -while getopts ":h" arg; do - case $arg in - h) - usage - exit 1 - ;; - ?) - echo "Invalid option: -${OPTARG}" - exit 1 - ;; - esac -done - -# Place SQL artifact for the current version for bwc -function setup_bwc_artifact() { - # This gets opensearch version from build.gradle (e.g. 1.2.0-SNAPSHOT), - # then converts to plugin version by appending ".0" (e.g. 1.2.0.0-SNAPSHOT), - # assuming one line in build.gradle is 'opensearch_version = System.getProperty("opensearch.version", "")'. - plugin_version=$(grep 'opensearch_version = System.getProperty' build.gradle | \ - grep -o '[0-9]\+\.[0-9]\+\.[0-9]\+[^"]*' | sed -e 's/\(.*\)\(\.[0-9]\)/\1\2.0/') - plugin_artifact="./plugin/build/distributions/opensearch-sql-$plugin_version.zip" - bwc_artifact_dir="./integ-test/src/test/resources/bwc/$plugin_version" - - if [ -z "${plugin_version// }" ]; then - echo "Error: failed to retrieve plugin version from build.gradle." >&2 - exit 1 - fi - - # copy current artifact to bwc artifact directory if it's not there - if [ ! -f "$bwc_artifact_dir/opensearch-sql-$plugin_version.zip" ]; then - if [ ! -f "$plugin_artifact" ]; then - ./gradlew assemble - fi - mkdir -p "$bwc_artifact_dir" - cp "$plugin_artifact" "$bwc_artifact_dir" - fi -} - -setup_bwc_artifact -./gradlew bwcTestSuite -Dtests.security.manager=false - From 58993e108d333d7e6fd21bb1192fea0660e623f1 Mon Sep 17 00:00:00 2001 From: Yury Fridlyand Date: Wed, 3 Aug 2022 13:00:00 -0700 Subject: [PATCH 2/2] Recover BWC test suite, but keep it disabled. Signed-off-by: Yury Fridlyand --- integ-test/build.gradle | 166 ++++++++++++++ .../sql/bwc/SQLBackwardsCompatibilityIT.java | 214 ++++++++++++++++++ .../sql/legacy/SQLIntegTestCase.java | 6 +- scripts/bwctest.sh | 58 +++++ 4 files changed, 442 insertions(+), 2 deletions(-) create mode 100644 integ-test/src/test/java/org/opensearch/sql/bwc/SQLBackwardsCompatibilityIT.java create mode 100644 scripts/bwctest.sh diff --git a/integ-test/build.gradle b/integ-test/build.gradle index 1b2998ccd29..0baf1af6cc8 100644 --- a/integ-test/build.gradle +++ b/integ-test/build.gradle @@ -123,6 +123,12 @@ integTest { jvmArgs '-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:5005' } + if (System.getProperty("tests.rest.bwcsuite") == null) { + filter { + excludeTestsMatching "org.opensearch.sql.bwc.*IT" + } + } + exclude 'org/opensearch/sql/doctest/**/*IT.class' exclude 'org/opensearch/sql/correctness/**' @@ -181,6 +187,160 @@ task compileJdbc(type: Exec) { } } +/* +BWC test suite was running on OpenDistro which was discontinued and no available anymore for testing. +Test suite is not removed, because it could be reused later between different OpenSearch versions. +*/ +String bwcVersion = "1.13.2.0"; +String baseName = "sqlBwcCluster" +String bwcFilePath = "src/test/resources/bwc/" +String bwcOpenDistroPlugin = "opendistro-sql-" + bwcVersion + ".zip" +String bwcRemoteFile = 'https://d3g5vo6xdbdb9a.cloudfront.net/downloads/elasticsearch-plugins/opendistro-sql/' + bwcOpenDistroPlugin + +2.times { i -> + testClusters { + "${baseName}$i" { + testDistribution = "ARCHIVE" + versions = ["7.10.2", opensearch_version] + numberOfNodes = 3 + plugin(provider(new Callable() { + @Override + RegularFile call() throws Exception { + return new RegularFile() { + @Override + File getAsFile() { + File dir = new File('./integ-test/' + bwcFilePath + bwcVersion) + if (!dir.exists()) { + dir.mkdirs() + } + File f = new File(dir, bwcOpenDistroPlugin) + if (!f.exists()) { + new URL(bwcRemoteFile).withInputStream{ ins -> f.withOutputStream{ it << ins }} + } + return fileTree(bwcFilePath + bwcVersion).getSingleFile() + } + } + } + })) + setting 'path.repo', "${buildDir}/cluster/shared/repo/${baseName}" + setting 'http.content_type.required', 'true' + } + } +} + +List> plugins = [ + provider(new Callable() { + @Override + RegularFile call() throws Exception { + return new RegularFile() { + @Override + File getAsFile() { + return fileTree(bwcFilePath + project.version).getSingleFile() + } + } + } + }) +] + +// Creates 2 test clusters with 3 nodes of the old version. +2.times { i -> + task "${baseName}#oldVersionClusterTask$i"(type: StandaloneRestIntegTestTask) { + useCluster testClusters."${baseName}$i" + filter { + includeTestsMatching "org.opensearch.sql.bwc.*IT" + } + systemProperty 'tests.rest.bwcsuite', 'old_cluster' + systemProperty 'tests.rest.bwcsuite_round', 'old' + systemProperty 'tests.plugin_bwc_version', bwcVersion + nonInputProperties.systemProperty('tests.rest.cluster', "${-> testClusters."${baseName}$i".allHttpSocketURI.join(",")}") + nonInputProperties.systemProperty('tests.clustername', "${-> testClusters."${baseName}$i".getName()}") + } +} + +// Upgrade one node of the old cluster to new OpenSearch version with upgraded plugin version. +// This results in a mixed cluster with 2 nodes on the old version and 1 upgraded node. +// This is also used as a one third upgraded cluster for a rolling upgrade. +task "${baseName}#mixedClusterTask"(type: StandaloneRestIntegTestTask) { + useCluster testClusters."${baseName}0" + dependsOn "${baseName}#oldVersionClusterTask0" + doFirst { + testClusters."${baseName}0".upgradeNodeAndPluginToNextVersion(plugins) + } + filter { + includeTestsMatching "org.opensearch.sql.bwc.*IT" + } + systemProperty 'tests.rest.bwcsuite', 'mixed_cluster' + systemProperty 'tests.rest.bwcsuite_round', 'first' + systemProperty 'tests.plugin_bwc_version', bwcVersion + nonInputProperties.systemProperty('tests.rest.cluster', "${-> testClusters."${baseName}0".allHttpSocketURI.join(",")}") + nonInputProperties.systemProperty('tests.clustername', "${-> testClusters."${baseName}0".getName()}") +} + +// Upgrade the second node to new OpenSearch version with upgraded plugin version after the first node is upgraded. +// This results in a mixed cluster with 1 node on the old version and 2 upgraded nodes. +// This is used for rolling upgrade. +task "${baseName}#twoThirdsUpgradedClusterTask"(type: StandaloneRestIntegTestTask) { + dependsOn "${baseName}#mixedClusterTask" + useCluster testClusters."${baseName}0" + doFirst { + testClusters."${baseName}0".upgradeNodeAndPluginToNextVersion(plugins) + } + filter { + includeTestsMatching "org.opensearch.sql.bwc.*IT" + } + systemProperty 'tests.rest.bwcsuite', 'mixed_cluster' + systemProperty 'tests.rest.bwcsuite_round', 'second' + systemProperty 'tests.plugin_bwc_version', bwcVersion + nonInputProperties.systemProperty('tests.rest.cluster', "${-> testClusters."${baseName}0".allHttpSocketURI.join(",")}") + nonInputProperties.systemProperty('tests.clustername', "${-> testClusters."${baseName}0".getName()}") +} + +// Upgrade the third node to new OpenSearch version with upgraded plugin version after the second node is upgraded. +// This results in a fully upgraded cluster. +// This is used for rolling upgrade. +task "${baseName}#rollingUpgradeClusterTask"(type: StandaloneRestIntegTestTask) { + dependsOn "${baseName}#twoThirdsUpgradedClusterTask" + useCluster testClusters."${baseName}0" + doFirst { + testClusters."${baseName}0".upgradeNodeAndPluginToNextVersion(plugins) + } + filter { + includeTestsMatching "org.opensearch.sql.bwc.*IT" + } + mustRunAfter "${baseName}#mixedClusterTask" + systemProperty 'tests.rest.bwcsuite', 'mixed_cluster' + systemProperty 'tests.rest.bwcsuite_round', 'third' + systemProperty 'tests.plugin_bwc_version', bwcVersion + nonInputProperties.systemProperty('tests.rest.cluster', "${-> testClusters."${baseName}0".allHttpSocketURI.join(",")}") + nonInputProperties.systemProperty('tests.clustername', "${-> testClusters."${baseName}0".getName()}") +} + +// Upgrade all the nodes of the old cluster to new OpenSearch version with upgraded plugin version +// at the same time resulting in a fully upgraded cluster. +task "${baseName}#fullRestartClusterTask"(type: StandaloneRestIntegTestTask) { + dependsOn "${baseName}#oldVersionClusterTask1" + useCluster testClusters."${baseName}1" + doFirst { + testClusters."${baseName}1".upgradeAllNodesAndPluginsToNextVersion(plugins) + } + filter { + includeTestsMatching "org.opensearch.sql.bwc.*IT" + } + systemProperty 'tests.rest.bwcsuite', 'upgraded_cluster' + systemProperty 'tests.plugin_bwc_version', bwcVersion + nonInputProperties.systemProperty('tests.rest.cluster', "${-> testClusters."${baseName}1".allHttpSocketURI.join(",")}") + nonInputProperties.systemProperty('tests.clustername', "${-> testClusters."${baseName}1".getName()}") +} + +// A bwc test suite which runs all the bwc tasks combined +task bwcTestSuite(type: StandaloneRestIntegTestTask) { + exclude '**/*Test*' + exclude '**/*IT*' + dependsOn tasks.named("${baseName}#mixedClusterTask") + dependsOn tasks.named("${baseName}#rollingUpgradeClusterTask") + dependsOn tasks.named("${baseName}#fullRestartClusterTask") +} + def opensearch_tmp_dir = rootProject.file('build/private/es_tmp').absoluteFile opensearch_tmp_dir.mkdirs() @@ -198,6 +358,12 @@ task integTestRemote(type: RestIntegTestTask) { // Set default query size limit systemProperty 'defaultQuerySizeLimit', '10000' + if (System.getProperty("tests.rest.bwcsuite") == null) { + filter { + excludeTestsMatching "org.opensearch.sql.bwc.*IT" + } + } + // Exclude the same tests that are excluded for integTest exclude 'org/opensearch/sql/doctest/**/*IT.class' exclude 'org/opensearch/sql/correctness/**' diff --git a/integ-test/src/test/java/org/opensearch/sql/bwc/SQLBackwardsCompatibilityIT.java b/integ-test/src/test/java/org/opensearch/sql/bwc/SQLBackwardsCompatibilityIT.java new file mode 100644 index 00000000000..079980248f5 --- /dev/null +++ b/integ-test/src/test/java/org/opensearch/sql/bwc/SQLBackwardsCompatibilityIT.java @@ -0,0 +1,214 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + + +package org.opensearch.sql.bwc; + + +import org.json.JSONObject; +import org.junit.Assert; +import org.opensearch.client.Request; +import org.opensearch.client.RequestOptions; +import org.opensearch.client.Response; +import org.opensearch.common.settings.Settings; +import org.opensearch.sql.legacy.SQLIntegTestCase; +import org.opensearch.sql.legacy.TestsConstants; +import org.opensearch.test.rest.OpenSearchRestTestCase; + +import java.io.IOException; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import static org.opensearch.sql.legacy.TestUtils.createIndexByRestClient; +import static org.opensearch.sql.legacy.TestUtils.isIndexExist; +import static org.opensearch.sql.legacy.TestUtils.loadDataByRestClient; +import static org.opensearch.sql.legacy.plugin.RestSqlAction.LEGACY_QUERY_API_ENDPOINT; +import static org.opensearch.sql.legacy.plugin.RestSqlAction.QUERY_API_ENDPOINT; +import static org.opensearch.sql.plugin.rest.RestQuerySettingsAction.LEGACY_SQL_SETTINGS_API_ENDPOINT; +import static org.opensearch.sql.util.MatcherUtils.rows; +import static org.opensearch.sql.util.MatcherUtils.schema; +import static org.opensearch.sql.util.MatcherUtils.verifyDataRows; +import static org.opensearch.sql.util.MatcherUtils.verifySchema; +import static org.opensearch.sql.util.TestUtils.getResponseBody; + +public class SQLBackwardsCompatibilityIT extends SQLIntegTestCase { + + private static final ClusterType CLUSTER_TYPE = ClusterType.parse(System.getProperty("tests.rest.bwcsuite")); + private static final String CLUSTER_NAME = System.getProperty("tests.clustername"); + + @Override + protected final boolean preserveIndicesUponCompletion() { + return true; + } + + @Override + protected final boolean preserveReposUponCompletion() { + return true; + } + + @Override + protected boolean preserveTemplatesUponCompletion() { + return true; + } + + @Override + protected final Settings restClientSettings() { + return Settings + .builder() + .put(super.restClientSettings()) + // increase the timeout here to 90 seconds to handle long waits for a green + // cluster health. the waits for green need to be longer than a minute to + // account for delayed shards + .put(OpenSearchRestTestCase.CLIENT_SOCKET_TIMEOUT, "90s") + .build(); + } + + private enum ClusterType { + OLD, + MIXED, + UPGRADED; + + public static ClusterType parse(String value) { + switch (value) { + case "old_cluster": + return OLD; + case "mixed_cluster": + return MIXED; + case "upgraded_cluster": + return UPGRADED; + default: + throw new AssertionError("unknown cluster type: " + value); + } + } + } + + @SuppressWarnings("unchecked") + public void testBackwardsCompatibility() throws Exception { + String uri = getUri(); + Map> responseMap = (Map>) getAsMap(uri).get("nodes"); + for (Map response : responseMap.values()) { + List> plugins = (List>) response.get("plugins"); + Set pluginNames = plugins.stream().map(map -> map.get("name")).collect(Collectors.toSet()); + switch (CLUSTER_TYPE) { + case OLD: + Assert.assertTrue(pluginNames.contains("opendistro-sql")); + updateLegacySQLSettings(); + loadIndex(Index.ACCOUNT); + verifySQLQueries(LEGACY_QUERY_API_ENDPOINT); + break; + case MIXED: + Assert.assertTrue(pluginNames.contains("opensearch-sql")); + verifySQLSettings(); + verifySQLQueries(LEGACY_QUERY_API_ENDPOINT); + break; + case UPGRADED: + Assert.assertTrue(pluginNames.contains("opensearch-sql")); + verifySQLSettings(); + verifySQLQueries(QUERY_API_ENDPOINT); + break; + } + break; + } + } + + private String getUri() { + switch (CLUSTER_TYPE) { + case OLD: + return "_nodes/" + CLUSTER_NAME + "-0/plugins"; + case MIXED: + String round = System.getProperty("tests.rest.bwcsuite_round"); + if (round.equals("second")) { + return "_nodes/" + CLUSTER_NAME + "-1/plugins"; + } else if (round.equals("third")) { + return "_nodes/" + CLUSTER_NAME + "-2/plugins"; + } else { + return "_nodes/" + CLUSTER_NAME + "-0/plugins"; + } + case UPGRADED: + return "_nodes/plugins"; + default: + throw new AssertionError("unknown cluster type: " + CLUSTER_TYPE); + } + } + + private void updateLegacySQLSettings() throws IOException { + Request request = new Request("PUT", LEGACY_SQL_SETTINGS_API_ENDPOINT); + request.setJsonEntity(String.format(Locale.ROOT, "{\n" + + " \"persistent\" : {\n" + + " \"%s\" : \"%s\"\n" + + " }\n" + + "}", "opendistro.sql.cursor.keep_alive", "7m")); + + RequestOptions.Builder restOptionsBuilder = RequestOptions.DEFAULT.toBuilder(); + restOptionsBuilder.addHeader("Content-Type", "application/json"); + request.setOptions(restOptionsBuilder); + + Response response = client().performRequest(request); + JSONObject jsonObject = new JSONObject(getResponseBody(response)); + Assert.assertTrue((boolean) jsonObject.get("acknowledged")); + } + + private void verifySQLSettings() throws IOException { + Request request = new Request("GET", "_cluster/settings?flat_settings"); + + RequestOptions.Builder restOptionsBuilder = RequestOptions.DEFAULT.toBuilder(); + restOptionsBuilder.addHeader("Content-Type", "application/json"); + request.setOptions(restOptionsBuilder); + + Response response = client().performRequest(request); + JSONObject jsonObject = new JSONObject(getResponseBody(response)); + Assert.assertEquals("{\"transient\":{},\"persistent\":{\"opendistro.sql.cursor.keep_alive\":\"7m\"}}", jsonObject.toString()); + } + + private void verifySQLQueries(String endpoint) throws IOException { + JSONObject filterResponse = executeSQLQuery(endpoint, "SELECT COUNT(*) FILTER(WHERE age > 35) FROM " + TestsConstants.TEST_INDEX_ACCOUNT); + verifySchema(filterResponse, schema("COUNT(*) FILTER(WHERE age > 35)", null, "integer")); + verifyDataRows(filterResponse, rows(238)); + + JSONObject aggResponse = executeSQLQuery(endpoint, "SELECT COUNT(DISTINCT age) FROM " + TestsConstants.TEST_INDEX_ACCOUNT); + verifySchema(aggResponse, schema("COUNT(DISTINCT age)", null, "integer")); + verifyDataRows(aggResponse, rows(21)); + + JSONObject groupByResponse = executeSQLQuery(endpoint, "select a.gender from " + TestsConstants.TEST_INDEX_ACCOUNT + " a group by a.gender having count(*) > 0"); + verifySchema(groupByResponse, schema("gender", null, "text")); + Assert.assertEquals("[[\"F\"],[\"M\"]]", groupByResponse.getJSONArray("datarows").toString()); + } + + private JSONObject executeSQLQuery(String endpoint, String query) throws IOException { + Request request = new Request("POST", endpoint); + request.setJsonEntity(String.format(Locale.ROOT, "{" + + " \"query\" : \"%s\"" + + "}", query)); + + RequestOptions.Builder restOptionsBuilder = RequestOptions.DEFAULT.toBuilder(); + restOptionsBuilder.addHeader("Content-Type", "application/json"); + request.setOptions(restOptionsBuilder); + + Response response = client().performRequest(request); + return new JSONObject(getResponseBody(response)); + } + + @Override + public boolean shouldResetQuerySizeLimit() { + return false; + } + + @Override + protected synchronized void loadIndex(Index index) throws IOException { + String indexName = index.getName(); + String mapping = index.getMapping(); + // current directory becomes 'integ-test/build/testrun/sqlBwcCluster#' during bwc + String dataSet = "../../../" + index.getDataSet(); + + if (!isIndexExist(client(), indexName)) { + createIndexByRestClient(client(), indexName, mapping); + loadDataByRestClient(client(), indexName, dataSet); + } + } + +} diff --git a/integ-test/src/test/java/org/opensearch/sql/legacy/SQLIntegTestCase.java b/integ-test/src/test/java/org/opensearch/sql/legacy/SQLIntegTestCase.java index 7c4d1a93d13..15f0261f0d0 100644 --- a/integ-test/src/test/java/org/opensearch/sql/legacy/SQLIntegTestCase.java +++ b/integ-test/src/test/java/org/opensearch/sql/legacy/SQLIntegTestCase.java @@ -139,8 +139,10 @@ public static void dumpCoverage() { */ @AfterClass public static void cleanUpIndices() throws IOException { - wipeAllOpenSearchIndices(); - wipeAllClusterSettings(); + if (System.getProperty("tests.rest.bwcsuite") == null) { + wipeAllOpenSearchIndices(); + wipeAllClusterSettings(); + } } protected void setQuerySizeLimit(Integer limit) throws IOException { diff --git a/scripts/bwctest.sh b/scripts/bwctest.sh new file mode 100644 index 00000000000..4f017ae5e89 --- /dev/null +++ b/scripts/bwctest.sh @@ -0,0 +1,58 @@ +#!/bin/bash + +set -e + +function usage() { + echo "" + echo "This script is used to run Backwards Compatibility tests" + echo "--------------------------------------------------------------------------" + echo "Usage: $0 [args]" + echo "" + echo "Required arguments:" + echo "None" + echo "" + echo -e "-h\tPrint this message." + echo "--------------------------------------------------------------------------" +} + +while getopts ":h" arg; do + case $arg in + h) + usage + exit 1 + ;; + ?) + echo "Invalid option: -${OPTARG}" + exit 1 + ;; + esac +done + +# Place SQL artifact for the current version for bwc +function setup_bwc_artifact() { + # This gets opensearch version from build.gradle (e.g. 1.2.0-SNAPSHOT), + # then converts to plugin version by appending ".0" (e.g. 1.2.0.0-SNAPSHOT), + # assuming one line in build.gradle is 'opensearch_version = System.getProperty("opensearch.version", "")'. + plugin_version=$(grep 'opensearch_version = System.getProperty' build.gradle | \ + grep -o '[0-9]\+\.[0-9]\+\.[0-9]\+[^"]*' | sed -e 's/\(.*\)\(\.[0-9]\)/\1\2.0/') + plugin_artifact="./plugin/build/distributions/opensearch-sql-$plugin_version.zip" + bwc_artifact_dir="./integ-test/src/test/resources/bwc/$plugin_version" + + if [ -z "${plugin_version// }" ]; then + echo "Error: failed to retrieve plugin version from build.gradle." >&2 + exit 1 + fi + + # copy current artifact to bwc artifact directory if it's not there + if [ ! -f "$bwc_artifact_dir/opensearch-sql-$plugin_version.zip" ]; then + if [ ! -f "$plugin_artifact" ]; then + ./gradlew assemble + fi + mkdir -p "$bwc_artifact_dir" + cp "$plugin_artifact" "$bwc_artifact_dir" + fi +} + +setup_bwc_artifact +./gradlew bwcTestSuite -Dtests.security.manager=false +