From 737b379dd8196ec3b0dcc648a94acfdd4c579965 Mon Sep 17 00:00:00 2001 From: Andreas Keefer Date: Fri, 26 Mar 2021 16:25:18 +0100 Subject: [PATCH 01/54] release notes update --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index b4a48f67..0ae4475a 100644 --- a/README.md +++ b/README.md @@ -284,6 +284,7 @@ ElasticsearchEvolution.configure() ### v0.3.2 - fixed issue [#36](https://github.com/senacor/elasticsearch-evolution/issues/36) +- version updates (spring-boot 2.4.4) - added java 16 compatibility tests - added Elasticsearch 7.12 compatibility tests From 32bff6459dfe36fa56d24ec5e04144df38c4b82e Mon Sep 17 00:00:00 2001 From: Andreas Keefer Date: Fri, 26 Mar 2021 16:27:55 +0100 Subject: [PATCH 02/54] prepare next release --- elasticsearch-evolution-core/pom.xml | 2 +- pom.xml | 2 +- spring-boot-starter-elasticsearch-evolution/pom.xml | 2 +- tests/migration-scripts/pom.xml | 2 +- tests/pom.xml | 2 +- tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml | 2 +- tests/test-spring-boot-2.2/pom.xml | 2 +- tests/test-spring-boot-2.3/pom.xml | 2 +- tests/test-spring-boot-2.4/pom.xml | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/elasticsearch-evolution-core/pom.xml b/elasticsearch-evolution-core/pom.xml index d4d203e1..c51b66c3 100644 --- a/elasticsearch-evolution-core/pom.xml +++ b/elasticsearch-evolution-core/pom.xml @@ -6,7 +6,7 @@ com.senacor.elasticsearch.evolution elasticsearch-evolution-parent - 0.3.2 + 0.3.3-SNAPSHOT ../ elasticsearch-evolution-core diff --git a/pom.xml b/pom.xml index 0d481d11..9c788e3a 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.senacor.elasticsearch.evolution elasticsearch-evolution-parent - 0.3.2 + 0.3.3-SNAPSHOT pom org.springframework.boot diff --git a/spring-boot-starter-elasticsearch-evolution/pom.xml b/spring-boot-starter-elasticsearch-evolution/pom.xml index 34817f3b..2efd3989 100644 --- a/spring-boot-starter-elasticsearch-evolution/pom.xml +++ b/spring-boot-starter-elasticsearch-evolution/pom.xml @@ -6,7 +6,7 @@ com.senacor.elasticsearch.evolution elasticsearch-evolution-parent - 0.3.2 + 0.3.3-SNAPSHOT ../ spring-boot-starter-elasticsearch-evolution diff --git a/tests/migration-scripts/pom.xml b/tests/migration-scripts/pom.xml index 4dbf35d4..a565b064 100644 --- a/tests/migration-scripts/pom.xml +++ b/tests/migration-scripts/pom.xml @@ -4,7 +4,7 @@ 4.0.0 com.senacor.elasticsearch.evolution migration-scripts - 0.3.2 + 0.3.3-SNAPSHOT jar containing migration files jar diff --git a/tests/pom.xml b/tests/pom.xml index faa7adf6..e5603668 100644 --- a/tests/pom.xml +++ b/tests/pom.xml @@ -6,7 +6,7 @@ com.senacor.elasticsearch.evolution elasticsearch-evolution-parent - 0.3.2 + 0.3.3-SNAPSHOT ../ tests diff --git a/tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml b/tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml index 02718e86..adde13cf 100644 --- a/tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml +++ b/tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml @@ -10,7 +10,7 @@ com.senacor.elasticsearch.evolution test-spring-boot-2.1-scriptsInJarFile - 0.3.2 + 0.3.3-SNAPSHOT Demo project for Spring Boot diff --git a/tests/test-spring-boot-2.2/pom.xml b/tests/test-spring-boot-2.2/pom.xml index 931d6841..77f792c7 100644 --- a/tests/test-spring-boot-2.2/pom.xml +++ b/tests/test-spring-boot-2.2/pom.xml @@ -10,7 +10,7 @@ com.senacor.elasticsearch.evolution test-spring-boot-2.2 - 0.3.2 + 0.3.3-SNAPSHOT Demo project for Spring Boot diff --git a/tests/test-spring-boot-2.3/pom.xml b/tests/test-spring-boot-2.3/pom.xml index efbd24e9..3e08bd66 100644 --- a/tests/test-spring-boot-2.3/pom.xml +++ b/tests/test-spring-boot-2.3/pom.xml @@ -10,7 +10,7 @@ com.senacor.elasticsearch.evolution test-spring-boot-2.3 - 0.3.2 + 0.3.3-SNAPSHOT Demo project for Spring Boot diff --git a/tests/test-spring-boot-2.4/pom.xml b/tests/test-spring-boot-2.4/pom.xml index 5e5f46e7..28a8b180 100644 --- a/tests/test-spring-boot-2.4/pom.xml +++ b/tests/test-spring-boot-2.4/pom.xml @@ -10,7 +10,7 @@ com.senacor.elasticsearch.evolution test-spring-boot-2.4 - 0.3.2 + 0.3.3-SNAPSHOT Demo project for Spring Boot From c05084ae8276a82eab5d9a0b348d9abbe9ed7713 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 16 Apr 2021 04:50:21 +0000 Subject: [PATCH 03/54] Bump elasticsearch from 1.15.2 to 1.15.3 Bumps [elasticsearch](https://github.com/testcontainers/testcontainers-java) from 1.15.2 to 1.15.3. - [Release notes](https://github.com/testcontainers/testcontainers-java/releases) - [Changelog](https://github.com/testcontainers/testcontainers-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/testcontainers/testcontainers-java/compare/1.15.2...1.15.3) Signed-off-by: dependabot-preview[bot] --- pom.xml | 2 +- tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml | 2 +- tests/test-spring-boot-2.2/pom.xml | 2 +- tests/test-spring-boot-2.3/pom.xml | 2 +- tests/test-spring-boot-2.4/pom.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 9c788e3a..b2c6ca95 100644 --- a/pom.xml +++ b/pom.xml @@ -91,7 +91,7 @@ 7.5.2 0.9.12 - 1.15.2 + 1.15.3 diff --git a/tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml b/tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml index adde13cf..f4d68770 100644 --- a/tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml +++ b/tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml @@ -18,7 +18,7 @@ 2.8.0 7.5.2 - 1.15.2 + 1.15.3 diff --git a/tests/test-spring-boot-2.2/pom.xml b/tests/test-spring-boot-2.2/pom.xml index 77f792c7..9ca5d9b2 100644 --- a/tests/test-spring-boot-2.2/pom.xml +++ b/tests/test-spring-boot-2.2/pom.xml @@ -18,7 +18,7 @@ 2.8.0 7.5.2 - 1.15.2 + 1.15.3 diff --git a/tests/test-spring-boot-2.3/pom.xml b/tests/test-spring-boot-2.3/pom.xml index 3e08bd66..21bee95d 100644 --- a/tests/test-spring-boot-2.3/pom.xml +++ b/tests/test-spring-boot-2.3/pom.xml @@ -18,7 +18,7 @@ 2.8.0 7.5.2 - 1.15.2 + 1.15.3 diff --git a/tests/test-spring-boot-2.4/pom.xml b/tests/test-spring-boot-2.4/pom.xml index 28a8b180..29bda045 100644 --- a/tests/test-spring-boot-2.4/pom.xml +++ b/tests/test-spring-boot-2.4/pom.xml @@ -18,7 +18,7 @@ 2.8.0 7.5.2 - 1.15.2 + 1.15.3 From ac998ef9a2f570450eb084f1b75da5b360b62f24 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 28 Apr 2021 22:34:06 +0000 Subject: [PATCH 04/54] Upgrade to GitHub-native Dependabot --- .github/dependabot.yml | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..c0419020 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,25 @@ +version: 2 +updates: +- package-ecosystem: maven + directory: "/" + schedule: + interval: daily + time: "04:00" + open-pull-requests-limit: 10 + ignore: + - dependency-name: org.elasticsearch.client:elasticsearch-rest-high-level-client + versions: + - 7.11.0 + - 7.11.1 + - 7.11.2 + - 7.12.0 + - dependency-name: org.elasticsearch:elasticsearch + versions: + - 7.11.0 + - 7.11.1 + - 7.11.2 + - 7.12.0 + - dependency-name: org.springframework.boot:spring-boot-starter-parent + versions: + - 2.4.2 + - 2.4.3 From 52f7a52f5004743ad7d0f79fca3988d51360076e Mon Sep 17 00:00:00 2001 From: Andreas Keefer Date: Thu, 29 Apr 2021 05:45:43 +0200 Subject: [PATCH 05/54] don't use master --- .github/workflows/quality.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index 42583d34..d39ef561 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -36,7 +36,7 @@ jobs: GITHUB_EVENT_PATH: ${{ github.event_path }} run: $MVN_CMD coveralls:report -DrepoToken=$COVERALLS_REPO_TOKEN -DserviceName=GitHub-Actions - name: Run codacy-coverage-reporter - uses: codacy/codacy-coverage-reporter-action@master + uses: codacy/codacy-coverage-reporter-action@v1 with: project-token: ${{ secrets.CODACY_PROJECT_TOKEN }} coverage-reports: elasticsearch-evolution-core/target/site/jacoco/jacoco.xml,spring-boot-starter-elasticsearch-evolution/target/site/jacoco/jacoco.xml From e6ccac67e68aa81a9e62ab1856854545ec919c07 Mon Sep 17 00:00:00 2001 From: Andreas Keefer Date: Thu, 29 Apr 2021 06:09:25 +0200 Subject: [PATCH 06/54] update versions; cleanup dependabot config --- .github/dependabot.yml | 43 +++++++++---------- .github/workflows/maven-matrix.yml | 2 +- README.md | 2 +- .../test/EmbeddedElasticsearchExtension.java | 2 +- pom.xml | 2 +- tests/test-spring-boot-2.3/pom.xml | 2 +- tests/test-spring-boot-2.4/pom.xml | 2 +- 7 files changed, 26 insertions(+), 29 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index c0419020..7ee93406 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,25 +1,22 @@ version: 2 updates: -- package-ecosystem: maven - directory: "/" - schedule: - interval: daily - time: "04:00" - open-pull-requests-limit: 10 - ignore: - - dependency-name: org.elasticsearch.client:elasticsearch-rest-high-level-client - versions: - - 7.11.0 - - 7.11.1 - - 7.11.2 - - 7.12.0 - - dependency-name: org.elasticsearch:elasticsearch - versions: - - 7.11.0 - - 7.11.1 - - 7.11.2 - - 7.12.0 - - dependency-name: org.springframework.boot:spring-boot-starter-parent - versions: - - 2.4.2 - - 2.4.3 + - package-ecosystem: maven + directory: "/" + schedule: + interval: daily + open-pull-requests-limit: 10 + ignore: + - dependency-name: org.elasticsearch.client:elasticsearch-rest-high-level-client + versions: + - 7.11.x + - 7.12.x + - 7.13.x + - 7.14.x + - 7.15.x + - dependency-name: org.elasticsearch:elasticsearch + versions: + - 7.11.x + - 7.12.x + - 7.13.x + - 7.14.x + - 7.15.x diff --git a/.github/workflows/maven-matrix.yml b/.github/workflows/maven-matrix.yml index f7562bcf..d5f1171e 100644 --- a/.github/workflows/maven-matrix.yml +++ b/.github/workflows/maven-matrix.yml @@ -40,7 +40,7 @@ jobs: build-and-test-with-es-version: strategy: matrix: - elasticsearchVersion: [ "7.12.0", "7.11.2", "7.10.2", "7.9.3", "7.8.1", + elasticsearchVersion: [ "7.12.1", "7.11.2", "7.10.2", "7.9.3", "7.8.1", "7.7.1", "7.6.2", "7.5.2" ] fail-fast: false runs-on: ubuntu-18.04 diff --git a/README.md b/README.md index 0ae4475a..483f0b19 100644 --- a/README.md +++ b/README.md @@ -284,7 +284,7 @@ ElasticsearchEvolution.configure() ### v0.3.2 - fixed issue [#36](https://github.com/senacor/elasticsearch-evolution/issues/36) -- version updates (spring-boot 2.4.4) +- version updates (spring-boot 2.4.5) - added java 16 compatibility tests - added Elasticsearch 7.12 compatibility tests diff --git a/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/test/EmbeddedElasticsearchExtension.java b/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/test/EmbeddedElasticsearchExtension.java index c51854d0..ab7ff24d 100644 --- a/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/test/EmbeddedElasticsearchExtension.java +++ b/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/test/EmbeddedElasticsearchExtension.java @@ -35,7 +35,7 @@ public class EmbeddedElasticsearchExtension implements TestInstancePostProcessor private static final Logger logger = LoggerFactory.getLogger(EmbeddedElasticsearchExtension.class); private static final Namespace NAMESPACE = Namespace.create(ExtensionContext.class); private static final SortedSet SUPPORTED_ES_VERSIONS = Collections.unmodifiableSortedSet(new TreeSet<>(Arrays.asList( - "7.12.0", + "7.12.1", "7.11.2", "7.10.2", "7.9.3", diff --git a/pom.xml b/pom.xml index b2c6ca95..f943fc38 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ org.springframework.boot spring-boot-dependencies - 2.4.4 + 2.4.5 elasticsearch-evolution diff --git a/tests/test-spring-boot-2.3/pom.xml b/tests/test-spring-boot-2.3/pom.xml index 21bee95d..3bc2af63 100644 --- a/tests/test-spring-boot-2.3/pom.xml +++ b/tests/test-spring-boot-2.3/pom.xml @@ -5,7 +5,7 @@ org.springframework.boot spring-boot-starter-parent - 2.3.9.RELEASE + 2.3.10.RELEASE com.senacor.elasticsearch.evolution diff --git a/tests/test-spring-boot-2.4/pom.xml b/tests/test-spring-boot-2.4/pom.xml index 29bda045..895af45a 100644 --- a/tests/test-spring-boot-2.4/pom.xml +++ b/tests/test-spring-boot-2.4/pom.xml @@ -5,7 +5,7 @@ org.springframework.boot spring-boot-starter-parent - 2.4.4 + 2.4.5 com.senacor.elasticsearch.evolution From eb6ddec0d48b8812143cb342ec6b0b85867d22a6 Mon Sep 17 00:00:00 2001 From: Andreas Keefer Date: Sun, 23 May 2021 10:33:02 +0200 Subject: [PATCH 07/54] update to spring boot 2.5 --- README.md | 5 +- pom.xml | 2 +- tests/pom.xml | 1 + tests/test-spring-boot-2.3/pom.xml | 2 +- tests/test-spring-boot-2.4/pom.xml | 2 +- .../pom.xml | 100 ++++++++++++++++++ .../evolution/springboot25/Application.java | 13 +++ .../src/main/resources/application.properties | 3 + .../springboot25/ApplicationTest.java | 54 ++++++++++ .../evolution/springboot25/EsUtils.java | 72 +++++++++++++ 10 files changed, 249 insertions(+), 5 deletions(-) create mode 100644 tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml create mode 100644 tests/test-spring-boot-2.5-scriptsInJarFile/src/main/java/com/senacor/elasticsearch/evolution/springboot25/Application.java create mode 100644 tests/test-spring-boot-2.5-scriptsInJarFile/src/main/resources/application.properties create mode 100644 tests/test-spring-boot-2.5-scriptsInJarFile/src/test/java/com/senacor/elasticsearch/evolution/springboot25/ApplicationTest.java create mode 100644 tests/test-spring-boot-2.5-scriptsInJarFile/src/test/java/com/senacor/elasticsearch/evolution/springboot25/EsUtils.java diff --git a/README.md b/README.md index 483f0b19..dd9dbe28 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Successful executed migration scripts will not be executed again! ## 2 Features - tested on Java 8, 9, 10, 11, 12, 13, 14, 15 and 16 -- runs on Spring-Boot 2.1, 2.2, 2.3 and 2.4 (and of course without Spring-Boot) +- runs on Spring-Boot 2.1, 2.2, 2.3, 2.4 and 2.5 (and of course without Spring-Boot) - runs on Elasticsearch version 7.5.0+ - highly configurable (e.g. location(s) of your migration files, migration files format pattern) - placeholder substitution in migration scripts @@ -279,7 +279,8 @@ ElasticsearchEvolution.configure() ### v0.3.3-SNAPSHOT -- ... +- version updates (spring-boot 2.5.0) +- spring boot 2.5 compatibility tests ### v0.3.2 diff --git a/pom.xml b/pom.xml index f943fc38..032a6549 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ org.springframework.boot spring-boot-dependencies - 2.4.5 + 2.5.0 elasticsearch-evolution diff --git a/tests/pom.xml b/tests/pom.xml index e5603668..dbdf12cc 100644 --- a/tests/pom.xml +++ b/tests/pom.xml @@ -19,6 +19,7 @@ migration-scripts + test-spring-boot-2.5-scriptsInJarFile test-spring-boot-2.4 test-spring-boot-2.3 test-spring-boot-2.2 diff --git a/tests/test-spring-boot-2.3/pom.xml b/tests/test-spring-boot-2.3/pom.xml index 3bc2af63..dbdf341e 100644 --- a/tests/test-spring-boot-2.3/pom.xml +++ b/tests/test-spring-boot-2.3/pom.xml @@ -5,7 +5,7 @@ org.springframework.boot spring-boot-starter-parent - 2.3.10.RELEASE + 2.3.11.RELEASE com.senacor.elasticsearch.evolution diff --git a/tests/test-spring-boot-2.4/pom.xml b/tests/test-spring-boot-2.4/pom.xml index 895af45a..88ac3e54 100644 --- a/tests/test-spring-boot-2.4/pom.xml +++ b/tests/test-spring-boot-2.4/pom.xml @@ -5,7 +5,7 @@ org.springframework.boot spring-boot-starter-parent - 2.4.5 + 2.4.6 com.senacor.elasticsearch.evolution diff --git a/tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml b/tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml new file mode 100644 index 00000000..32e4d775 --- /dev/null +++ b/tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml @@ -0,0 +1,100 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.5.0 + + + com.senacor.elasticsearch.evolution + test-spring-boot-2.5-scriptsInJarFile + 0.3.3-SNAPSHOT + Demo project for Spring Boot + + + 1.8 + + 2.8.0 + 7.5.2 + 1.15.3 + + + + + + ${project.groupId} + migration-scripts + ${project.version} + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + ${project.groupId} + spring-boot-starter-elasticsearch-evolution + ${project.version} + + + + + org.testcontainers + elasticsearch + ${testcontainers.elasticsearch.version} + test + + + commons-io + commons-io + ${commons-io.version} + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + org.jacoco + jacoco-maven-plugin + 0.8.6 + + + prepare-agent + + prepare-agent + + + + report + post-integration-test + + report + + + + + + maven-surefire-plugin + + true + + + + + + diff --git a/tests/test-spring-boot-2.5-scriptsInJarFile/src/main/java/com/senacor/elasticsearch/evolution/springboot25/Application.java b/tests/test-spring-boot-2.5-scriptsInJarFile/src/main/java/com/senacor/elasticsearch/evolution/springboot25/Application.java new file mode 100644 index 00000000..15055b27 --- /dev/null +++ b/tests/test-spring-boot-2.5-scriptsInJarFile/src/main/java/com/senacor/elasticsearch/evolution/springboot25/Application.java @@ -0,0 +1,13 @@ +package com.senacor.elasticsearch.evolution.springboot25; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + +} \ No newline at end of file diff --git a/tests/test-spring-boot-2.5-scriptsInJarFile/src/main/resources/application.properties b/tests/test-spring-boot-2.5-scriptsInJarFile/src/main/resources/application.properties new file mode 100644 index 00000000..5db1f692 --- /dev/null +++ b/tests/test-spring-boot-2.5-scriptsInJarFile/src/main/resources/application.properties @@ -0,0 +1,3 @@ +spring.elasticsearch.evolution.locations[0]=classpath:es/mig +spring.elasticsearch.evolution.placeholders.index=test_1 +logging.level.com.senacor.elasticsearch.evolution=DEBUG \ No newline at end of file diff --git a/tests/test-spring-boot-2.5-scriptsInJarFile/src/test/java/com/senacor/elasticsearch/evolution/springboot25/ApplicationTest.java b/tests/test-spring-boot-2.5-scriptsInJarFile/src/test/java/com/senacor/elasticsearch/evolution/springboot25/ApplicationTest.java new file mode 100644 index 00000000..3126b063 --- /dev/null +++ b/tests/test-spring-boot-2.5-scriptsInJarFile/src/test/java/com/senacor/elasticsearch/evolution/springboot25/ApplicationTest.java @@ -0,0 +1,54 @@ +package com.senacor.elasticsearch.evolution.springboot25; + +import org.apache.http.HttpHost; +import org.elasticsearch.client.RestClient; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.context.annotation.Bean; +import org.testcontainers.elasticsearch.ElasticsearchContainer; +import org.testcontainers.utility.DockerImageName; + +import java.util.Collections; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +@SpringBootTest(properties = {"spring.elasticsearch.rest.uris=http://localhost:" + ApplicationTest.ELASTICSEARCH_PORT}) +class ApplicationTest { + + static final int ELASTICSEARCH_PORT = 18769; + + @Autowired + private EsUtils esUtils; + + @Test + void contextLoads() { + esUtils.refreshIndices(); + + List documents = esUtils.fetchAllDocuments("test_1"); + + assertThat(documents).hasSize(1); + } + + @TestConfiguration + static class Config { + @Bean(destroyMethod = "stop") + public ElasticsearchContainer elasticsearchContainer(@Value("${elasticsearch.version:7.5.2}") String esVersion) { + ElasticsearchContainer container = new ElasticsearchContainer(DockerImageName + .parse("docker.elastic.co/elasticsearch/elasticsearch") + .withTag(esVersion) + ).withEnv("ES_JAVA_OPTS", "-Xms128m -Xmx128m"); + container.setPortBindings(Collections.singletonList(ELASTICSEARCH_PORT + ":9200")); + container.start(); + return container; + } + + @Bean + public EsUtils esUtils(ElasticsearchContainer elasticsearchContainer) { + return new EsUtils(RestClient.builder(HttpHost.create(elasticsearchContainer.getHttpHostAddress())).build()); + } + } +} \ No newline at end of file diff --git a/tests/test-spring-boot-2.5-scriptsInJarFile/src/test/java/com/senacor/elasticsearch/evolution/springboot25/EsUtils.java b/tests/test-spring-boot-2.5-scriptsInJarFile/src/test/java/com/senacor/elasticsearch/evolution/springboot25/EsUtils.java new file mode 100644 index 00000000..55b59996 --- /dev/null +++ b/tests/test-spring-boot-2.5-scriptsInJarFile/src/test/java/com/senacor/elasticsearch/evolution/springboot25/EsUtils.java @@ -0,0 +1,72 @@ +package com.senacor.elasticsearch.evolution.springboot25; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.commons.io.IOUtils; +import org.elasticsearch.client.Request; +import org.elasticsearch.client.Response; +import org.elasticsearch.client.RestClient; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +/** + * @author Andreas Keefer + */ +public class EsUtils { + + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + + private final RestClient restClient; + + public EsUtils(RestClient restClient) { + this.restClient = restClient; + } + + public void refreshIndices() { + try { + restClient.performRequest(new Request("POST", "/_refresh")); + } catch (IOException e) { + throw new IllegalStateException("refreshIndices failed", e); + } + } + + public List fetchAllDocuments(String index) { + try { + Request post = new Request("POST", "/" + index + "/_search"); + post.setJsonEntity("{" + + " \"query\": {" + + " \"match_all\": {}" + + " }" + + "}"); + Response response = restClient.performRequest(post); + int statusCode = response.getStatusLine().getStatusCode(); + if (statusCode < 200 || statusCode >= 300) { + throw new IllegalStateException("fetchAllDocuments(" + index + ") failed with HTTP status " + + statusCode + ": " + response.toString()); + } + String body = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8); + + return parseDocuments(body) + .collect(Collectors.toList()); + } catch (IOException e) { + throw new IllegalStateException("fetchAllDocuments(" + index + ") failed", e); + } + } + + private Stream parseDocuments(String body) { + try { + JsonNode jsonNode = OBJECT_MAPPER.readTree(body); + return StreamSupport.stream(jsonNode.get("hits").get("hits").spliterator(), false) + .map(hitNode -> hitNode.get("_source")) + .map(JsonNode::toString); + } catch (IOException e) { + throw new IllegalStateException("parseDocuments failed. body=" + body, e); + } + } +} + From 440467d91abd02fb3537eee9efc406fa4e4e0e67 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 23 May 2021 08:56:56 +0000 Subject: [PATCH 08/54] Bump jacoco-maven-plugin from 0.8.6 to 0.8.7 Bumps [jacoco-maven-plugin](https://github.com/jacoco/jacoco) from 0.8.6 to 0.8.7. - [Release notes](https://github.com/jacoco/jacoco/releases) - [Commits](https://github.com/jacoco/jacoco/compare/v0.8.6...v0.8.7) Signed-off-by: dependabot[bot] --- pom.xml | 2 +- tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml | 2 +- tests/test-spring-boot-2.2/pom.xml | 2 +- tests/test-spring-boot-2.3/pom.xml | 2 +- tests/test-spring-boot-2.4/pom.xml | 2 +- tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 032a6549..15d4c9a1 100644 --- a/pom.xml +++ b/pom.xml @@ -80,7 +80,7 @@ 4.3.0 - 0.8.6 + 0.8.7 1.6 3.2.0 diff --git a/tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml b/tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml index f4d68770..96be7a7a 100644 --- a/tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml +++ b/tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml @@ -71,7 +71,7 @@ org.jacoco jacoco-maven-plugin - 0.8.6 + 0.8.7 prepare-agent diff --git a/tests/test-spring-boot-2.2/pom.xml b/tests/test-spring-boot-2.2/pom.xml index 9ca5d9b2..f81e1842 100644 --- a/tests/test-spring-boot-2.2/pom.xml +++ b/tests/test-spring-boot-2.2/pom.xml @@ -64,7 +64,7 @@ org.jacoco jacoco-maven-plugin - 0.8.6 + 0.8.7 prepare-agent diff --git a/tests/test-spring-boot-2.3/pom.xml b/tests/test-spring-boot-2.3/pom.xml index dbdf341e..45fc1eea 100644 --- a/tests/test-spring-boot-2.3/pom.xml +++ b/tests/test-spring-boot-2.3/pom.xml @@ -64,7 +64,7 @@ org.jacoco jacoco-maven-plugin - 0.8.6 + 0.8.7 prepare-agent diff --git a/tests/test-spring-boot-2.4/pom.xml b/tests/test-spring-boot-2.4/pom.xml index 88ac3e54..56844f26 100644 --- a/tests/test-spring-boot-2.4/pom.xml +++ b/tests/test-spring-boot-2.4/pom.xml @@ -64,7 +64,7 @@ org.jacoco jacoco-maven-plugin - 0.8.6 + 0.8.7 prepare-agent diff --git a/tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml b/tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml index 32e4d775..81a3d01d 100644 --- a/tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml +++ b/tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml @@ -71,7 +71,7 @@ org.jacoco jacoco-maven-plugin - 0.8.6 + 0.8.7 prepare-agent From ed3014f5c0daf9dd72711c45f082eb1c90418924 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 23 May 2021 09:40:13 +0000 Subject: [PATCH 09/54] Bump maven-gpg-plugin from 1.6 to 3.0.1 Bumps [maven-gpg-plugin](https://github.com/apache/maven-gpg-plugin) from 1.6 to 3.0.1. - [Release notes](https://github.com/apache/maven-gpg-plugin/releases) - [Commits](https://github.com/apache/maven-gpg-plugin/compare/maven-gpg-plugin-1.6...maven-gpg-plugin-3.0.1) Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 15d4c9a1..6d9874a2 100644 --- a/pom.xml +++ b/pom.xml @@ -81,7 +81,7 @@ 4.3.0 0.8.7 - 1.6 + 3.0.1 3.2.0 1.6.8 From 28d078a093cef6f778bc33b01df75457d4ef4ebc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 May 2021 07:33:02 +0000 Subject: [PATCH 10/54] Bump maven-javadoc-plugin from 3.2.0 to 3.3.0 Bumps [maven-javadoc-plugin](https://github.com/apache/maven-javadoc-plugin) from 3.2.0 to 3.3.0. - [Release notes](https://github.com/apache/maven-javadoc-plugin/releases) - [Commits](https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.2.0...maven-javadoc-plugin-3.3.0) Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6d9874a2..bb9a1159 100644 --- a/pom.xml +++ b/pom.xml @@ -83,7 +83,7 @@ 0.8.7 3.0.1 - 3.2.0 + 3.3.0 1.6.8 From 4a42d9de0e4a3121a0a2f8e81fa15db908062325 Mon Sep 17 00:00:00 2001 From: Andreas Keefer Date: Mon, 24 May 2021 10:29:56 +0200 Subject: [PATCH 11/54] added spring-boot 2.5 compatibility in docu --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index dd9dbe28..acaf47c2 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ Successful executed migration scripts will not be executed again! | Compatibility | Spring Boot | Elasticsearch | |----------------------------------|------------------------------|----------------------| -| elasticsearch-evolution >= 0.3.0 | 2.1, 2.2, 2.3, 2.4 | 7.5.x and later | +| elasticsearch-evolution >= 0.3.0 | 2.1, 2.2, 2.3, 2.4, 2.5 | 7.5.x and later | | elasticsearch-evolution 0.2.x | 1.5, 2.0, 2.1, 2.2, 2.3, 2.4 | 7.0.x - 7.4.x, 6.8.x | NOTE: When you run on Java 11 and using spring-boot 2.2 or 2.3 and you hit [this issue](https://github.com/ronmamo/reflections/issues/279), you have 2 options: From b6e7b3f59d040cf772cca979223ba4812fc2e847 Mon Sep 17 00:00:00 2001 From: Andreas Keefer Date: Mon, 24 May 2021 10:30:32 +0200 Subject: [PATCH 12/54] added spring-boot 2.5 compatibility in docu --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index acaf47c2..06673c72 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ Successful executed migration scripts will not be executed again! | Compatibility | Spring Boot | Elasticsearch | |----------------------------------|------------------------------|----------------------| -| elasticsearch-evolution >= 0.3.0 | 2.1, 2.2, 2.3, 2.4, 2.5 | 7.5.x and later | +| elasticsearch-evolution >= 0.3.0 | 2.1, 2.2, 2.3, 2.4, 2.5 | 7.5.x and later | | elasticsearch-evolution 0.2.x | 1.5, 2.0, 2.1, 2.2, 2.3, 2.4 | 7.0.x - 7.4.x, 6.8.x | NOTE: When you run on Java 11 and using spring-boot 2.2 or 2.3 and you hit [this issue](https://github.com/ronmamo/reflections/issues/279), you have 2 options: From 780c9596f271c2c08ee6032f652676cf6f49d40a Mon Sep 17 00:00:00 2001 From: Andreas Keefer Date: Mon, 24 May 2021 10:53:27 +0200 Subject: [PATCH 13/54] fixed typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 06673c72..89767f16 100644 --- a/README.md +++ b/README.md @@ -170,7 +170,7 @@ The filename has to follow a pattern: Elasticsearch-Evolution uses the version for ordering your scripts and enforces strict ordered execution of your scripts. Out-of-Order execution is not supported. Elasticsearch-Evolution interprets the version parts as Integers, so each version part must be between 1 (inclusive) and 2,147,483,647 (inclusive). -Here is and example which indicates the ordering: `1.0.1` < `1.1` < `1.2.1` < (`2.0.0` == `2`). +Here is an example which indicates the ordering: `1.0.1` < `1.1` < `1.2.1` < (`2.0.0` == `2`). In this example version `1.0.1` is the smallest version and is executed first, after that version `1.1`, `1.2.1` and in the end `2`. `2` is the same as `2.0` or `2.0.0` - so leading zeros will be trimed. From e924b71729108be584ef09823b2ba4261df77a40 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 26 May 2021 06:10:19 +0000 Subject: [PATCH 14/54] Bump commons-io from 2.8.0 to 2.9.0 Bumps commons-io from 2.8.0 to 2.9.0. Signed-off-by: dependabot[bot] --- pom.xml | 2 +- tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml | 2 +- tests/test-spring-boot-2.2/pom.xml | 2 +- tests/test-spring-boot-2.3/pom.xml | 2 +- tests/test-spring-boot-2.4/pom.xml | 2 +- tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index bb9a1159..37dac5af 100644 --- a/pom.xml +++ b/pom.xml @@ -87,7 +87,7 @@ 1.6.8 - 2.8.0 + 2.9.0 7.5.2 0.9.12 diff --git a/tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml b/tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml index 96be7a7a..0843542b 100644 --- a/tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml +++ b/tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml @@ -16,7 +16,7 @@ 1.8 - 2.8.0 + 2.9.0 7.5.2 1.15.3 diff --git a/tests/test-spring-boot-2.2/pom.xml b/tests/test-spring-boot-2.2/pom.xml index f81e1842..6f5ff141 100644 --- a/tests/test-spring-boot-2.2/pom.xml +++ b/tests/test-spring-boot-2.2/pom.xml @@ -16,7 +16,7 @@ 1.8 - 2.8.0 + 2.9.0 7.5.2 1.15.3 diff --git a/tests/test-spring-boot-2.3/pom.xml b/tests/test-spring-boot-2.3/pom.xml index 45fc1eea..84058607 100644 --- a/tests/test-spring-boot-2.3/pom.xml +++ b/tests/test-spring-boot-2.3/pom.xml @@ -16,7 +16,7 @@ 1.8 - 2.8.0 + 2.9.0 7.5.2 1.15.3 diff --git a/tests/test-spring-boot-2.4/pom.xml b/tests/test-spring-boot-2.4/pom.xml index 56844f26..2f134519 100644 --- a/tests/test-spring-boot-2.4/pom.xml +++ b/tests/test-spring-boot-2.4/pom.xml @@ -16,7 +16,7 @@ 1.8 - 2.8.0 + 2.9.0 7.5.2 1.15.3 diff --git a/tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml b/tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml index 81a3d01d..fcf9e63b 100644 --- a/tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml +++ b/tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml @@ -16,7 +16,7 @@ 1.8 - 2.8.0 + 2.9.0 7.5.2 1.15.3 From 909c306c55c53d6731e8be8a5064a3a094ebbe68 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 11 Jun 2021 06:00:22 +0000 Subject: [PATCH 15/54] Bump spring-boot-dependencies from 2.5.0 to 2.5.1 Bumps [spring-boot-dependencies](https://github.com/spring-projects/spring-boot) from 2.5.0 to 2.5.1. - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v2.5.0...v2.5.1) --- updated-dependencies: - dependency-name: org.springframework.boot:spring-boot-dependencies dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 37dac5af..d73ccaa9 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ org.springframework.boot spring-boot-dependencies - 2.5.0 + 2.5.1 elasticsearch-evolution From 382fa9ed7d6e969f3573442d467ac3f44cd1dd18 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Jun 2021 07:45:40 +0000 Subject: [PATCH 16/54] Bump commons-io from 2.9.0 to 2.10.0 Bumps commons-io from 2.9.0 to 2.10.0. --- updated-dependencies: - dependency-name: commons-io:commons-io dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml | 2 +- tests/test-spring-boot-2.2/pom.xml | 2 +- tests/test-spring-boot-2.3/pom.xml | 2 +- tests/test-spring-boot-2.4/pom.xml | 2 +- tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 37dac5af..a5812540 100644 --- a/pom.xml +++ b/pom.xml @@ -87,7 +87,7 @@ 1.6.8 - 2.9.0 + 2.10.0 7.5.2 0.9.12 diff --git a/tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml b/tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml index 0843542b..009f53c6 100644 --- a/tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml +++ b/tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml @@ -16,7 +16,7 @@ 1.8 - 2.9.0 + 2.10.0 7.5.2 1.15.3 diff --git a/tests/test-spring-boot-2.2/pom.xml b/tests/test-spring-boot-2.2/pom.xml index 6f5ff141..ceefcc63 100644 --- a/tests/test-spring-boot-2.2/pom.xml +++ b/tests/test-spring-boot-2.2/pom.xml @@ -16,7 +16,7 @@ 1.8 - 2.9.0 + 2.10.0 7.5.2 1.15.3 diff --git a/tests/test-spring-boot-2.3/pom.xml b/tests/test-spring-boot-2.3/pom.xml index 84058607..5630d964 100644 --- a/tests/test-spring-boot-2.3/pom.xml +++ b/tests/test-spring-boot-2.3/pom.xml @@ -16,7 +16,7 @@ 1.8 - 2.9.0 + 2.10.0 7.5.2 1.15.3 diff --git a/tests/test-spring-boot-2.4/pom.xml b/tests/test-spring-boot-2.4/pom.xml index 2f134519..c14975f0 100644 --- a/tests/test-spring-boot-2.4/pom.xml +++ b/tests/test-spring-boot-2.4/pom.xml @@ -16,7 +16,7 @@ 1.8 - 2.9.0 + 2.10.0 7.5.2 1.15.3 diff --git a/tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml b/tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml index fcf9e63b..6fad66c9 100644 --- a/tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml +++ b/tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml @@ -16,7 +16,7 @@ 1.8 - 2.9.0 + 2.10.0 7.5.2 1.15.3 From 0510efebaeb670c122ddc81810b0a855c8a769c9 Mon Sep 17 00:00:00 2001 From: Andreas Keefer Date: Fri, 18 Jun 2021 23:14:40 +0200 Subject: [PATCH 17/54] - updated spring-boot test versions - added Elasticsearch 7.13 compatibility tests --- .github/workflows/maven-matrix.yml | 2 +- README.md | 3 ++- .../evolution/core/test/EmbeddedElasticsearchExtension.java | 1 + tests/test-spring-boot-2.3/pom.xml | 2 +- tests/test-spring-boot-2.4/pom.xml | 2 +- tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml | 2 +- 6 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/workflows/maven-matrix.yml b/.github/workflows/maven-matrix.yml index d5f1171e..c5c6ab25 100644 --- a/.github/workflows/maven-matrix.yml +++ b/.github/workflows/maven-matrix.yml @@ -40,7 +40,7 @@ jobs: build-and-test-with-es-version: strategy: matrix: - elasticsearchVersion: [ "7.12.1", "7.11.2", "7.10.2", "7.9.3", "7.8.1", + elasticsearchVersion: [ "7.13.2", "7.12.1", "7.11.2", "7.10.2", "7.9.3", "7.8.1", "7.7.1", "7.6.2", "7.5.2" ] fail-fast: false runs-on: ubuntu-18.04 diff --git a/README.md b/README.md index 89767f16..4bbb08ba 100644 --- a/README.md +++ b/README.md @@ -279,8 +279,9 @@ ElasticsearchEvolution.configure() ### v0.3.3-SNAPSHOT -- version updates (spring-boot 2.5.0) +- version updates (spring-boot 2.5.1) - spring boot 2.5 compatibility tests +- added Elasticsearch 7.13 compatibility tests ### v0.3.2 diff --git a/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/test/EmbeddedElasticsearchExtension.java b/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/test/EmbeddedElasticsearchExtension.java index ab7ff24d..d7459a95 100644 --- a/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/test/EmbeddedElasticsearchExtension.java +++ b/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/test/EmbeddedElasticsearchExtension.java @@ -35,6 +35,7 @@ public class EmbeddedElasticsearchExtension implements TestInstancePostProcessor private static final Logger logger = LoggerFactory.getLogger(EmbeddedElasticsearchExtension.class); private static final Namespace NAMESPACE = Namespace.create(ExtensionContext.class); private static final SortedSet SUPPORTED_ES_VERSIONS = Collections.unmodifiableSortedSet(new TreeSet<>(Arrays.asList( + "7.13.2", "7.12.1", "7.11.2", "7.10.2", diff --git a/tests/test-spring-boot-2.3/pom.xml b/tests/test-spring-boot-2.3/pom.xml index 5630d964..6d5229d0 100644 --- a/tests/test-spring-boot-2.3/pom.xml +++ b/tests/test-spring-boot-2.3/pom.xml @@ -5,7 +5,7 @@ org.springframework.boot spring-boot-starter-parent - 2.3.11.RELEASE + 2.3.12.RELEASE com.senacor.elasticsearch.evolution diff --git a/tests/test-spring-boot-2.4/pom.xml b/tests/test-spring-boot-2.4/pom.xml index c14975f0..740dae70 100644 --- a/tests/test-spring-boot-2.4/pom.xml +++ b/tests/test-spring-boot-2.4/pom.xml @@ -5,7 +5,7 @@ org.springframework.boot spring-boot-starter-parent - 2.4.6 + 2.4.7 com.senacor.elasticsearch.evolution diff --git a/tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml b/tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml index 6fad66c9..a8f18f61 100644 --- a/tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml +++ b/tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml @@ -5,7 +5,7 @@ org.springframework.boot spring-boot-starter-parent - 2.5.0 + 2.5.1 com.senacor.elasticsearch.evolution From a8d4d5896cb69e279d4398fc93be5b0415c8f439 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 25 Jun 2021 01:06:25 +0000 Subject: [PATCH 18/54] Bump spring-boot-dependencies from 2.5.1 to 2.5.2 Bumps [spring-boot-dependencies](https://github.com/spring-projects/spring-boot) from 2.5.1 to 2.5.2. - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v2.5.1...v2.5.2) --- updated-dependencies: - dependency-name: org.springframework.boot:spring-boot-dependencies dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 36dfa95a..e4c12180 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ org.springframework.boot spring-boot-dependencies - 2.5.1 + 2.5.2 elasticsearch-evolution From 83c889d27edbc3007bd5ca6373385d5cedd7906a Mon Sep 17 00:00:00 2001 From: Andreas Keefer Date: Sat, 26 Jun 2021 12:12:30 +0200 Subject: [PATCH 19/54] - updated spring-boot test versions --- README.md | 2 +- tests/test-spring-boot-2.4/pom.xml | 2 +- tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 4bbb08ba..6a6e87a3 100644 --- a/README.md +++ b/README.md @@ -279,7 +279,7 @@ ElasticsearchEvolution.configure() ### v0.3.3-SNAPSHOT -- version updates (spring-boot 2.5.1) +- version updates (spring-boot 2.5.2) - spring boot 2.5 compatibility tests - added Elasticsearch 7.13 compatibility tests diff --git a/tests/test-spring-boot-2.4/pom.xml b/tests/test-spring-boot-2.4/pom.xml index 740dae70..5908ac41 100644 --- a/tests/test-spring-boot-2.4/pom.xml +++ b/tests/test-spring-boot-2.4/pom.xml @@ -5,7 +5,7 @@ org.springframework.boot spring-boot-starter-parent - 2.4.7 + 2.4.8 com.senacor.elasticsearch.evolution diff --git a/tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml b/tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml index a8f18f61..f7b11f04 100644 --- a/tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml +++ b/tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml @@ -5,7 +5,7 @@ org.springframework.boot spring-boot-starter-parent - 2.5.1 + 2.5.2 com.senacor.elasticsearch.evolution From 41f415fb8d5c04dbbe59f583aabd54636fc34b01 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Jul 2021 01:06:19 +0000 Subject: [PATCH 20/54] Bump commons-io from 2.10.0 to 2.11.0 Bumps commons-io from 2.10.0 to 2.11.0. --- updated-dependencies: - dependency-name: commons-io:commons-io dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml | 2 +- tests/test-spring-boot-2.2/pom.xml | 2 +- tests/test-spring-boot-2.3/pom.xml | 2 +- tests/test-spring-boot-2.4/pom.xml | 2 +- tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index e4c12180..deb49f71 100644 --- a/pom.xml +++ b/pom.xml @@ -87,7 +87,7 @@ 1.6.8 - 2.10.0 + 2.11.0 7.5.2 0.9.12 diff --git a/tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml b/tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml index 009f53c6..9bbb89a9 100644 --- a/tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml +++ b/tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml @@ -16,7 +16,7 @@ 1.8 - 2.10.0 + 2.11.0 7.5.2 1.15.3 diff --git a/tests/test-spring-boot-2.2/pom.xml b/tests/test-spring-boot-2.2/pom.xml index ceefcc63..630a219f 100644 --- a/tests/test-spring-boot-2.2/pom.xml +++ b/tests/test-spring-boot-2.2/pom.xml @@ -16,7 +16,7 @@ 1.8 - 2.10.0 + 2.11.0 7.5.2 1.15.3 diff --git a/tests/test-spring-boot-2.3/pom.xml b/tests/test-spring-boot-2.3/pom.xml index 6d5229d0..8c5a5b8d 100644 --- a/tests/test-spring-boot-2.3/pom.xml +++ b/tests/test-spring-boot-2.3/pom.xml @@ -16,7 +16,7 @@ 1.8 - 2.10.0 + 2.11.0 7.5.2 1.15.3 diff --git a/tests/test-spring-boot-2.4/pom.xml b/tests/test-spring-boot-2.4/pom.xml index 5908ac41..98120a7f 100644 --- a/tests/test-spring-boot-2.4/pom.xml +++ b/tests/test-spring-boot-2.4/pom.xml @@ -16,7 +16,7 @@ 1.8 - 2.10.0 + 2.11.0 7.5.2 1.15.3 diff --git a/tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml b/tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml index f7b11f04..ab06cb08 100644 --- a/tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml +++ b/tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml @@ -16,7 +16,7 @@ 1.8 - 2.10.0 + 2.11.0 7.5.2 1.15.3 From 38e27a5bfdcb10df5e3e1a58c74600e37e4d89fa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Jul 2021 01:07:23 +0000 Subject: [PATCH 21/54] Bump elasticsearch from 1.15.3 to 1.16.0 Bumps [elasticsearch](https://github.com/testcontainers/testcontainers-java) from 1.15.3 to 1.16.0. - [Release notes](https://github.com/testcontainers/testcontainers-java/releases) - [Changelog](https://github.com/testcontainers/testcontainers-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/testcontainers/testcontainers-java/compare/1.15.3...1.16.0) --- updated-dependencies: - dependency-name: org.testcontainers:elasticsearch dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml | 2 +- tests/test-spring-boot-2.2/pom.xml | 2 +- tests/test-spring-boot-2.3/pom.xml | 2 +- tests/test-spring-boot-2.4/pom.xml | 2 +- tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index deb49f71..a29732a2 100644 --- a/pom.xml +++ b/pom.xml @@ -91,7 +91,7 @@ 7.5.2 0.9.12 - 1.15.3 + 1.16.0 diff --git a/tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml b/tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml index 9bbb89a9..7c252ee2 100644 --- a/tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml +++ b/tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml @@ -18,7 +18,7 @@ 2.11.0 7.5.2 - 1.15.3 + 1.16.0 diff --git a/tests/test-spring-boot-2.2/pom.xml b/tests/test-spring-boot-2.2/pom.xml index 630a219f..430ff5cd 100644 --- a/tests/test-spring-boot-2.2/pom.xml +++ b/tests/test-spring-boot-2.2/pom.xml @@ -18,7 +18,7 @@ 2.11.0 7.5.2 - 1.15.3 + 1.16.0 diff --git a/tests/test-spring-boot-2.3/pom.xml b/tests/test-spring-boot-2.3/pom.xml index 8c5a5b8d..e2c39301 100644 --- a/tests/test-spring-boot-2.3/pom.xml +++ b/tests/test-spring-boot-2.3/pom.xml @@ -18,7 +18,7 @@ 2.11.0 7.5.2 - 1.15.3 + 1.16.0 diff --git a/tests/test-spring-boot-2.4/pom.xml b/tests/test-spring-boot-2.4/pom.xml index 98120a7f..2fb13f2e 100644 --- a/tests/test-spring-boot-2.4/pom.xml +++ b/tests/test-spring-boot-2.4/pom.xml @@ -18,7 +18,7 @@ 2.11.0 7.5.2 - 1.15.3 + 1.16.0 diff --git a/tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml b/tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml index ab06cb08..bf51f753 100644 --- a/tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml +++ b/tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml @@ -18,7 +18,7 @@ 2.11.0 7.5.2 - 1.15.3 + 1.16.0 From e37d8f5c95486f19d0c963ae760192395cdf452f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 23 Jul 2021 01:06:28 +0000 Subject: [PATCH 22/54] Bump spring-boot-dependencies from 2.5.2 to 2.5.3 Bumps [spring-boot-dependencies](https://github.com/spring-projects/spring-boot) from 2.5.2 to 2.5.3. - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v2.5.2...v2.5.3) --- updated-dependencies: - dependency-name: org.springframework.boot:spring-boot-dependencies dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a29732a2..4b79b51d 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ org.springframework.boot spring-boot-dependencies - 2.5.2 + 2.5.3 elasticsearch-evolution From f622e8b784703603fa89a433e80b23ead66e0957 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 20 Aug 2021 01:07:33 +0000 Subject: [PATCH 23/54] Bump spring-boot-dependencies from 2.5.3 to 2.5.4 Bumps [spring-boot-dependencies](https://github.com/spring-projects/spring-boot) from 2.5.3 to 2.5.4. - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v2.5.3...v2.5.4) --- updated-dependencies: - dependency-name: org.springframework.boot:spring-boot-dependencies dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 4b79b51d..8cc64341 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ org.springframework.boot spring-boot-dependencies - 2.5.3 + 2.5.4 elasticsearch-evolution From 3b0fe261643e4bf4bf310aed9bfb3e0d8f26fcd9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 8 Sep 2021 01:08:54 +0000 Subject: [PATCH 24/54] Bump maven-javadoc-plugin from 3.3.0 to 3.3.1 Bumps [maven-javadoc-plugin](https://github.com/apache/maven-javadoc-plugin) from 3.3.0 to 3.3.1. - [Release notes](https://github.com/apache/maven-javadoc-plugin/releases) - [Commits](https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.3.0...maven-javadoc-plugin-3.3.1) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-javadoc-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 8cc64341..b46441d1 100644 --- a/pom.xml +++ b/pom.xml @@ -83,7 +83,7 @@ 0.8.7 3.0.1 - 3.3.0 + 3.3.1 1.6.8 From 12216af4db616959e46c0cbcbc142a34d62df4fc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Sep 2021 01:07:35 +0000 Subject: [PATCH 25/54] Bump spring-boot-dependencies from 2.5.4 to 2.5.5 Bumps [spring-boot-dependencies](https://github.com/spring-projects/spring-boot) from 2.5.4 to 2.5.5. - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v2.5.4...v2.5.5) --- updated-dependencies: - dependency-name: org.springframework.boot:spring-boot-dependencies dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b46441d1..af35a150 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ org.springframework.boot spring-boot-dependencies - 2.5.4 + 2.5.5 elasticsearch-evolution From cdc7e3b2da34137847ce6fe6a93e3dd934df5b12 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Oct 2021 01:07:33 +0000 Subject: [PATCH 26/54] Bump elasticsearch from 1.16.0 to 1.16.1 Bumps [elasticsearch](https://github.com/testcontainers/testcontainers-java) from 1.16.0 to 1.16.1. - [Release notes](https://github.com/testcontainers/testcontainers-java/releases) - [Changelog](https://github.com/testcontainers/testcontainers-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/testcontainers/testcontainers-java/compare/1.16.0...1.16.1) --- updated-dependencies: - dependency-name: org.testcontainers:elasticsearch dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml | 2 +- tests/test-spring-boot-2.2/pom.xml | 2 +- tests/test-spring-boot-2.3/pom.xml | 2 +- tests/test-spring-boot-2.4/pom.xml | 2 +- tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index af35a150..b1dcfd37 100644 --- a/pom.xml +++ b/pom.xml @@ -91,7 +91,7 @@ 7.5.2 0.9.12 - 1.16.0 + 1.16.1 diff --git a/tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml b/tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml index 7c252ee2..a0717669 100644 --- a/tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml +++ b/tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml @@ -18,7 +18,7 @@ 2.11.0 7.5.2 - 1.16.0 + 1.16.1 diff --git a/tests/test-spring-boot-2.2/pom.xml b/tests/test-spring-boot-2.2/pom.xml index 430ff5cd..99be217a 100644 --- a/tests/test-spring-boot-2.2/pom.xml +++ b/tests/test-spring-boot-2.2/pom.xml @@ -18,7 +18,7 @@ 2.11.0 7.5.2 - 1.16.0 + 1.16.1 diff --git a/tests/test-spring-boot-2.3/pom.xml b/tests/test-spring-boot-2.3/pom.xml index e2c39301..27fe2203 100644 --- a/tests/test-spring-boot-2.3/pom.xml +++ b/tests/test-spring-boot-2.3/pom.xml @@ -18,7 +18,7 @@ 2.11.0 7.5.2 - 1.16.0 + 1.16.1 diff --git a/tests/test-spring-boot-2.4/pom.xml b/tests/test-spring-boot-2.4/pom.xml index 2fb13f2e..20bba9d2 100644 --- a/tests/test-spring-boot-2.4/pom.xml +++ b/tests/test-spring-boot-2.4/pom.xml @@ -18,7 +18,7 @@ 2.11.0 7.5.2 - 1.16.0 + 1.16.1 diff --git a/tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml b/tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml index bf51f753..5d504593 100644 --- a/tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml +++ b/tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml @@ -18,7 +18,7 @@ 2.11.0 7.5.2 - 1.16.0 + 1.16.1 From 6f8d587b4373e8e188ae30b150149040b62b8a52 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 22 Oct 2021 01:09:08 +0000 Subject: [PATCH 27/54] Bump spring-boot-dependencies from 2.5.5 to 2.5.6 Bumps [spring-boot-dependencies](https://github.com/spring-projects/spring-boot) from 2.5.5 to 2.5.6. - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v2.5.5...v2.5.6) --- updated-dependencies: - dependency-name: org.springframework.boot:spring-boot-dependencies dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b1dcfd37..51bea927 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ org.springframework.boot spring-boot-dependencies - 2.5.5 + 2.5.6 elasticsearch-evolution From 4ce7703acae27db178acd2759311951abac400e7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Oct 2021 01:33:54 +0000 Subject: [PATCH 28/54] Bump elasticsearch from 1.16.1 to 1.16.2 Bumps [elasticsearch](https://github.com/testcontainers/testcontainers-java) from 1.16.1 to 1.16.2. - [Release notes](https://github.com/testcontainers/testcontainers-java/releases) - [Changelog](https://github.com/testcontainers/testcontainers-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/testcontainers/testcontainers-java/compare/1.16.1...1.16.2) --- updated-dependencies: - dependency-name: org.testcontainers:elasticsearch dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml | 2 +- tests/test-spring-boot-2.2/pom.xml | 2 +- tests/test-spring-boot-2.3/pom.xml | 2 +- tests/test-spring-boot-2.4/pom.xml | 2 +- tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 51bea927..5bbd551a 100644 --- a/pom.xml +++ b/pom.xml @@ -91,7 +91,7 @@ 7.5.2 0.9.12 - 1.16.1 + 1.16.2 diff --git a/tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml b/tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml index a0717669..80363c12 100644 --- a/tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml +++ b/tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml @@ -18,7 +18,7 @@ 2.11.0 7.5.2 - 1.16.1 + 1.16.2 diff --git a/tests/test-spring-boot-2.2/pom.xml b/tests/test-spring-boot-2.2/pom.xml index 99be217a..215a32fb 100644 --- a/tests/test-spring-boot-2.2/pom.xml +++ b/tests/test-spring-boot-2.2/pom.xml @@ -18,7 +18,7 @@ 2.11.0 7.5.2 - 1.16.1 + 1.16.2 diff --git a/tests/test-spring-boot-2.3/pom.xml b/tests/test-spring-boot-2.3/pom.xml index 27fe2203..dfce2a8a 100644 --- a/tests/test-spring-boot-2.3/pom.xml +++ b/tests/test-spring-boot-2.3/pom.xml @@ -18,7 +18,7 @@ 2.11.0 7.5.2 - 1.16.1 + 1.16.2 diff --git a/tests/test-spring-boot-2.4/pom.xml b/tests/test-spring-boot-2.4/pom.xml index 20bba9d2..bb654d01 100644 --- a/tests/test-spring-boot-2.4/pom.xml +++ b/tests/test-spring-boot-2.4/pom.xml @@ -18,7 +18,7 @@ 2.11.0 7.5.2 - 1.16.1 + 1.16.2 diff --git a/tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml b/tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml index 5d504593..1ef3bcba 100644 --- a/tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml +++ b/tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml @@ -18,7 +18,7 @@ 2.11.0 7.5.2 - 1.16.1 + 1.16.2 From 3569570ff868b2e731257d44c97e60d008b257db Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Nov 2021 01:20:02 +0000 Subject: [PATCH 29/54] Bump spring-boot-dependencies from 2.5.6 to 2.6.0 Bumps [spring-boot-dependencies](https://github.com/spring-projects/spring-boot) from 2.5.6 to 2.6.0. - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v2.5.6...v2.6.0) --- updated-dependencies: - dependency-name: org.springframework.boot:spring-boot-dependencies dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5bbd551a..80427d83 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ org.springframework.boot spring-boot-dependencies - 2.5.6 + 2.6.0 elasticsearch-evolution From 70446ee8364d2936341e84f3a630728ca5bf94f1 Mon Sep 17 00:00:00 2001 From: Andreas Keefer Date: Wed, 30 Mar 2022 17:42:54 +0200 Subject: [PATCH 30/54] - added spring boot 2.6 compatibility tests - added java 17 and 18 compatibility tests - added Elasticsearch 7.17, 7.16, 7.15 and 7.14 compatibility tests - updated maven to 3.8.5 - updated jacoco to 0.8.8-SNAPSHOT to support java 17 and 18 --- .github/workflows/maven-matrix.yml | 6 +- .mvn/wrapper/maven-wrapper.jar | Bin 47610 -> 58727 bytes .mvn/wrapper/maven-wrapper.properties | 19 +++- README.md | 8 +- .../test/EmbeddedElasticsearchExtension.java | 6 +- mvnw | 103 ++++++++++++++++- mvnw.cmd | 71 +++++++++--- pom.xml | 16 ++- tests/pom.xml | 1 + .../pom.xml | 16 ++- tests/test-spring-boot-2.2/pom.xml | 16 ++- .../springboot22/ApplicationTests.java | 4 +- tests/test-spring-boot-2.3/pom.xml | 16 ++- .../springboot23/ApplicationTests.java | 4 +- tests/test-spring-boot-2.4/pom.xml | 16 ++- .../springboot24/ApplicationTests.java | 4 +- .../pom.xml | 16 ++- .../springboot25/ApplicationTest.java | 2 +- tests/test-spring-boot-2.6/pom.xml | 107 ++++++++++++++++++ .../evolution/springboot24/Application.java | 14 +++ ...01.00__createTemplateWithIndexMapping.http | 35 ++++++ .../es/migration/V001.01__addDocument.http | 17 +++ .../springboot24/ApplicationTests.java | 54 +++++++++ .../evolution/springboot24/EsUtils.java | 72 ++++++++++++ 24 files changed, 583 insertions(+), 40 deletions(-) mode change 100755 => 100644 mvnw.cmd create mode 100644 tests/test-spring-boot-2.6/pom.xml create mode 100644 tests/test-spring-boot-2.6/src/main/java/com/senacor/elasticsearch/evolution/springboot24/Application.java create mode 100644 tests/test-spring-boot-2.6/src/main/resources/es/migration/V001.00__createTemplateWithIndexMapping.http create mode 100644 tests/test-spring-boot-2.6/src/main/resources/es/migration/V001.01__addDocument.http create mode 100644 tests/test-spring-boot-2.6/src/test/java/com/senacor/elasticsearch/evolution/springboot24/ApplicationTests.java create mode 100644 tests/test-spring-boot-2.6/src/test/java/com/senacor/elasticsearch/evolution/springboot24/EsUtils.java diff --git a/.github/workflows/maven-matrix.yml b/.github/workflows/maven-matrix.yml index c5c6ab25..8083d842 100644 --- a/.github/workflows/maven-matrix.yml +++ b/.github/workflows/maven-matrix.yml @@ -14,7 +14,7 @@ jobs: build-and-test-with-jdk: strategy: matrix: - java: [ 8, 9, 10, 11, 12, 13, 14, 15, 16 ] + java: [ 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 ] fail-fast: false runs-on: ubuntu-18.04 steps: @@ -40,8 +40,8 @@ jobs: build-and-test-with-es-version: strategy: matrix: - elasticsearchVersion: [ "7.13.2", "7.12.1", "7.11.2", "7.10.2", "7.9.3", "7.8.1", - "7.7.1", "7.6.2", "7.5.2" ] + elasticsearchVersion: [ "7.17.1", "7.16.3", "7.15.2", "7.14.2", "7.13.4", "7.12.1", "7.11.2", "7.10.2", + "7.9.3", "7.8.1", "7.7.1", "7.6.2", "7.5.2" ] fail-fast: false runs-on: ubuntu-18.04 steps: diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar index 9cc84ea9b4d95453115d0c26488d6a78694e0bc6..c1dd12f17644411d6e840bd5a10c6ecda0175f18 100644 GIT binary patch literal 58727 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%0ANsA|A3-jj|r`KAo%oTajX3>^E` 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 YM{}PByk10KD_nuQ4E7Du?}+~TKh4V)`~Uy| literal 47610 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 diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index be4fea70..db95c131 100644 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -1 +1,18 @@ -distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.5/apache-maven-3.8.5-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar diff --git a/README.md b/README.md index 6a6e87a3..fd006205 100644 --- a/README.md +++ b/README.md @@ -279,9 +279,11 @@ ElasticsearchEvolution.configure() ### v0.3.3-SNAPSHOT -- version updates (spring-boot 2.5.2) -- spring boot 2.5 compatibility tests -- added Elasticsearch 7.13 compatibility tests +- version updates (spring-boot 2.5.2) +- spring boot 2.5 compatibility tests +- spring boot 2.6 compatibility tests +- added java 17 and 18 compatibility tests +- added Elasticsearch 7.17, 7.16, 7.15, 7.14 and 7.13 compatibility tests ### v0.3.2 diff --git a/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/test/EmbeddedElasticsearchExtension.java b/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/test/EmbeddedElasticsearchExtension.java index d7459a95..f4bf7019 100644 --- a/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/test/EmbeddedElasticsearchExtension.java +++ b/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/test/EmbeddedElasticsearchExtension.java @@ -35,7 +35,11 @@ public class EmbeddedElasticsearchExtension implements TestInstancePostProcessor private static final Logger logger = LoggerFactory.getLogger(EmbeddedElasticsearchExtension.class); private static final Namespace NAMESPACE = Namespace.create(ExtensionContext.class); private static final SortedSet SUPPORTED_ES_VERSIONS = Collections.unmodifiableSortedSet(new TreeSet<>(Arrays.asList( - "7.13.2", + "7.17.1", + "7.16.3", + "7.15.2", + "7.14.2", + "7.13.4", "7.12.1", "7.11.2", "7.10.2", diff --git a/mvnw b/mvnw index ec72bb9a..5643201c 100755 --- a/mvnw +++ b/mvnw @@ -19,7 +19,7 @@ # ---------------------------------------------------------------------------- # ---------------------------------------------------------------------------- -# Maven2 Start Up Batch script +# Maven Start Up Batch script # # Required ENV vars: # ------------------ @@ -36,6 +36,10 @@ if [ -z "$MAVEN_SKIP_RC" ] ; then + if [ -f /usr/local/etc/mavenrc ] ; then + . /usr/local/etc/mavenrc + fi + if [ -f /etc/mavenrc ] ; then . /etc/mavenrc fi @@ -108,13 +112,12 @@ if $cygwin ; then CLASSPATH=`cygpath --path --unix "$CLASSPATH"` fi -# For Migwn, ensure paths are in UNIX format before anything is touched +# For Mingw, ensure paths are in UNIX format before anything is touched if $mingw ; then [ -n "$M2_HOME" ] && M2_HOME="`(cd "$M2_HOME"; pwd)`" [ -n "$JAVA_HOME" ] && JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" - # TODO classpath? fi if [ -z "$JAVA_HOME" ]; then @@ -146,7 +149,7 @@ if [ -z "$JAVACMD" ] ; then JAVACMD="$JAVA_HOME/bin/java" fi else - JAVACMD="`which java`" + JAVACMD="`\\unset -f command; \\command -v java`" fi fi @@ -200,8 +203,89 @@ if [ -z "$BASE_DIR" ]; then exit 1; fi +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} -#echo $MAVEN_PROJECTBASEDIR +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" # For Cygwin, switch paths to Windows format before running java @@ -216,10 +300,17 @@ if $cygwin; then MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` fi +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain exec "$JAVACMD" \ $MAVEN_OPTS \ + $MAVEN_DEBUG_OPTS \ -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ - "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + "-Dmaven.home=${M2_HOME}" \ + "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/mvnw.cmd b/mvnw.cmd old mode 100755 new mode 100644 index 019bd74d..8a15b7f3 --- a/mvnw.cmd +++ b/mvnw.cmd @@ -18,7 +18,7 @@ @REM ---------------------------------------------------------------------------- @REM ---------------------------------------------------------------------------- -@REM Maven2 Start Up Batch script +@REM Maven Start Up Batch script @REM @REM Required ENV vars: @REM JAVA_HOME - location of a JDK home dir @@ -26,7 +26,7 @@ @REM Optional ENV vars @REM M2_HOME - location of maven2's installed home dir @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands -@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven @REM e.g. to debug Maven itself, use @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 @@ -35,7 +35,9 @@ @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' @echo off -@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% @REM set %HOME% to equivalent of $HOME @@ -44,8 +46,8 @@ if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") @REM Execute a user defined script before this one if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre @REM check for pre script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" -if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* +if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* :skipRcPre @setlocal @@ -115,11 +117,54 @@ for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do s :endReadAdditionalConfig SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" - set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain -%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% ^ + %JVM_CONFIG_MAVEN_PROPS% ^ + %MAVEN_OPTS% ^ + %MAVEN_DEBUG_OPTS% ^ + -classpath %WRAPPER_JAR% ^ + "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ + %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* if ERRORLEVEL 1 goto error goto end @@ -129,15 +174,15 @@ set ERROR_CODE=1 :end @endlocal & set ERROR_CODE=%ERROR_CODE% -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost @REM check for post script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" -if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" +if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" :skipRcPost @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' -if "%MAVEN_BATCH_PAUSE%" == "on" pause +if "%MAVEN_BATCH_PAUSE%"=="on" pause -if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% +if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% -exit /B %ERROR_CODE% +cmd /C exit /B %ERROR_CODE% diff --git a/pom.xml b/pom.xml index 80427d83..fced78a5 100644 --- a/pom.xml +++ b/pom.xml @@ -36,6 +36,19 @@ https://github.com/senacor/elasticsearch-evolution + + + oss.sonatype.org-snapshot + https://oss.sonatype.org/content/repositories/snapshots + + false + + + true + + + + readme @@ -80,7 +93,8 @@ 4.3.0 - 0.8.7 + + 0.8.8-SNAPSHOT 3.0.1 3.3.1 diff --git a/tests/pom.xml b/tests/pom.xml index dbdf12cc..b436bbaf 100644 --- a/tests/pom.xml +++ b/tests/pom.xml @@ -19,6 +19,7 @@ migration-scripts + test-spring-boot-2.6 test-spring-boot-2.5-scriptsInJarFile test-spring-boot-2.4 test-spring-boot-2.3 diff --git a/tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml b/tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml index 80363c12..5d1e3e07 100644 --- a/tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml +++ b/tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml @@ -62,6 +62,19 @@ + + + oss.sonatype.org-snapshot + https://oss.sonatype.org/content/repositories/snapshots + + false + + + true + + + + @@ -71,7 +84,8 @@ org.jacoco jacoco-maven-plugin - 0.8.7 + + 0.8.8-SNAPSHOT prepare-agent diff --git a/tests/test-spring-boot-2.2/pom.xml b/tests/test-spring-boot-2.2/pom.xml index 215a32fb..0de7374b 100644 --- a/tests/test-spring-boot-2.2/pom.xml +++ b/tests/test-spring-boot-2.2/pom.xml @@ -55,6 +55,19 @@ + + + oss.sonatype.org-snapshot + https://oss.sonatype.org/content/repositories/snapshots + + false + + + true + + + + @@ -64,7 +77,8 @@ org.jacoco jacoco-maven-plugin - 0.8.7 + + 0.8.8-SNAPSHOT prepare-agent diff --git a/tests/test-spring-boot-2.2/src/test/java/com/senacor/elasticsearch/evolution/springboot22/ApplicationTests.java b/tests/test-spring-boot-2.2/src/test/java/com/senacor/elasticsearch/evolution/springboot22/ApplicationTests.java index 2c85fe4f..035e96aa 100644 --- a/tests/test-spring-boot-2.2/src/test/java/com/senacor/elasticsearch/evolution/springboot22/ApplicationTests.java +++ b/tests/test-spring-boot-2.2/src/test/java/com/senacor/elasticsearch/evolution/springboot22/ApplicationTests.java @@ -17,7 +17,7 @@ import static org.assertj.core.api.Assertions.assertThat; @SpringBootTest(properties = {"spring.elasticsearch.rest.uris=http://localhost:" + ApplicationTests.ELASTICSEARCH_PORT}) -public class ApplicationTests { +class ApplicationTests { static final int ELASTICSEARCH_PORT = 18759; @@ -25,7 +25,7 @@ public class ApplicationTests { private EsUtils esUtils; @Test - public void contextLoads() { + void contextLoads() { esUtils.refreshIndices(); List documents = esUtils.fetchAllDocuments("test_1"); diff --git a/tests/test-spring-boot-2.3/pom.xml b/tests/test-spring-boot-2.3/pom.xml index dfce2a8a..8e1bcaf8 100644 --- a/tests/test-spring-boot-2.3/pom.xml +++ b/tests/test-spring-boot-2.3/pom.xml @@ -55,6 +55,19 @@ + + + oss.sonatype.org-snapshot + https://oss.sonatype.org/content/repositories/snapshots + + false + + + true + + + + @@ -64,7 +77,8 @@ org.jacoco jacoco-maven-plugin - 0.8.7 + + 0.8.8-SNAPSHOT prepare-agent diff --git a/tests/test-spring-boot-2.3/src/test/java/com/senacor/elasticsearch/evolution/springboot23/ApplicationTests.java b/tests/test-spring-boot-2.3/src/test/java/com/senacor/elasticsearch/evolution/springboot23/ApplicationTests.java index bc2ffee9..7d3cadc5 100644 --- a/tests/test-spring-boot-2.3/src/test/java/com/senacor/elasticsearch/evolution/springboot23/ApplicationTests.java +++ b/tests/test-spring-boot-2.3/src/test/java/com/senacor/elasticsearch/evolution/springboot23/ApplicationTests.java @@ -17,7 +17,7 @@ import static org.assertj.core.api.Assertions.assertThat; @SpringBootTest(properties = {"spring.elasticsearch.rest.uris=http://localhost:" + ApplicationTests.ELASTICSEARCH_PORT}) -public class ApplicationTests { +class ApplicationTests { static final int ELASTICSEARCH_PORT = 18761; @@ -25,7 +25,7 @@ public class ApplicationTests { private EsUtils esUtils; @Test - public void contextLoads() { + void contextLoads() { esUtils.refreshIndices(); List documents = esUtils.fetchAllDocuments("test_1"); diff --git a/tests/test-spring-boot-2.4/pom.xml b/tests/test-spring-boot-2.4/pom.xml index bb654d01..ab3a9c56 100644 --- a/tests/test-spring-boot-2.4/pom.xml +++ b/tests/test-spring-boot-2.4/pom.xml @@ -55,6 +55,19 @@ + + + oss.sonatype.org-snapshot + https://oss.sonatype.org/content/repositories/snapshots + + false + + + true + + + + @@ -64,7 +77,8 @@ org.jacoco jacoco-maven-plugin - 0.8.7 + + 0.8.8-SNAPSHOT prepare-agent diff --git a/tests/test-spring-boot-2.4/src/test/java/com/senacor/elasticsearch/evolution/springboot24/ApplicationTests.java b/tests/test-spring-boot-2.4/src/test/java/com/senacor/elasticsearch/evolution/springboot24/ApplicationTests.java index ba06d4ce..ebad3ed4 100644 --- a/tests/test-spring-boot-2.4/src/test/java/com/senacor/elasticsearch/evolution/springboot24/ApplicationTests.java +++ b/tests/test-spring-boot-2.4/src/test/java/com/senacor/elasticsearch/evolution/springboot24/ApplicationTests.java @@ -17,7 +17,7 @@ import static org.assertj.core.api.Assertions.assertThat; @SpringBootTest(properties = {"spring.elasticsearch.rest.uris=http://localhost:" + ApplicationTests.ELASTICSEARCH_PORT}) -public class ApplicationTests { +class ApplicationTests { static final int ELASTICSEARCH_PORT = 18762; @@ -25,7 +25,7 @@ public class ApplicationTests { private EsUtils esUtils; @Test - public void contextLoads() { + void contextLoads() { esUtils.refreshIndices(); List documents = esUtils.fetchAllDocuments("test_1"); diff --git a/tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml b/tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml index 1ef3bcba..659be013 100644 --- a/tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml +++ b/tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml @@ -62,6 +62,19 @@ + + + oss.sonatype.org-snapshot + https://oss.sonatype.org/content/repositories/snapshots + + false + + + true + + + + @@ -71,7 +84,8 @@ org.jacoco jacoco-maven-plugin - 0.8.7 + + 0.8.8-SNAPSHOT prepare-agent diff --git a/tests/test-spring-boot-2.5-scriptsInJarFile/src/test/java/com/senacor/elasticsearch/evolution/springboot25/ApplicationTest.java b/tests/test-spring-boot-2.5-scriptsInJarFile/src/test/java/com/senacor/elasticsearch/evolution/springboot25/ApplicationTest.java index 3126b063..72115d31 100644 --- a/tests/test-spring-boot-2.5-scriptsInJarFile/src/test/java/com/senacor/elasticsearch/evolution/springboot25/ApplicationTest.java +++ b/tests/test-spring-boot-2.5-scriptsInJarFile/src/test/java/com/senacor/elasticsearch/evolution/springboot25/ApplicationTest.java @@ -19,7 +19,7 @@ @SpringBootTest(properties = {"spring.elasticsearch.rest.uris=http://localhost:" + ApplicationTest.ELASTICSEARCH_PORT}) class ApplicationTest { - static final int ELASTICSEARCH_PORT = 18769; + static final int ELASTICSEARCH_PORT = 18768; @Autowired private EsUtils esUtils; diff --git a/tests/test-spring-boot-2.6/pom.xml b/tests/test-spring-boot-2.6/pom.xml new file mode 100644 index 00000000..aaeb93a3 --- /dev/null +++ b/tests/test-spring-boot-2.6/pom.xml @@ -0,0 +1,107 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.6.5 + + + com.senacor.elasticsearch.evolution + test-spring-boot-2.6 + 0.3.3-SNAPSHOT + Demo project for Spring Boot + + + 1.8 + + 2.11.0 + 7.5.2 + 1.16.2 + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + ${project.groupId} + spring-boot-starter-elasticsearch-evolution + ${project.version} + + + + + org.testcontainers + elasticsearch + ${testcontainers.elasticsearch.version} + test + + + commons-io + commons-io + ${commons-io.version} + test + + + + + + oss.sonatype.org-snapshot + https://oss.sonatype.org/content/repositories/snapshots + + false + + + true + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + org.jacoco + jacoco-maven-plugin + + 0.8.8-SNAPSHOT + + + prepare-agent + + prepare-agent + + + + report + post-integration-test + + report + + + + + + maven-surefire-plugin + + true + + + + + + diff --git a/tests/test-spring-boot-2.6/src/main/java/com/senacor/elasticsearch/evolution/springboot24/Application.java b/tests/test-spring-boot-2.6/src/main/java/com/senacor/elasticsearch/evolution/springboot24/Application.java new file mode 100644 index 00000000..6778bd46 --- /dev/null +++ b/tests/test-spring-boot-2.6/src/main/java/com/senacor/elasticsearch/evolution/springboot24/Application.java @@ -0,0 +1,14 @@ +package com.senacor.elasticsearch.evolution.springboot24; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + + +} \ No newline at end of file diff --git a/tests/test-spring-boot-2.6/src/main/resources/es/migration/V001.00__createTemplateWithIndexMapping.http b/tests/test-spring-boot-2.6/src/main/resources/es/migration/V001.00__createTemplateWithIndexMapping.http new file mode 100644 index 00000000..fbd7df47 --- /dev/null +++ b/tests/test-spring-boot-2.6/src/main/resources/es/migration/V001.00__createTemplateWithIndexMapping.http @@ -0,0 +1,35 @@ +PUT _template/test_1 +Content-Type: application/json + +{ + "index_patterns" : [ + "test_*" + ], + "order" : 1, + "version" : 1, + "settings" : { + "number_of_shards" : 1 + }, + "mappings" : { + "dynamic" : "strict", + "properties" : { + "doc" : { + "dynamic" : false, + "properties" : {} + }, + "searchable" : { + "dynamic" : false, + "properties" : { + "version" : { + "type" : "keyword", + "ignore_above" : 20, + "similarity" : "boolean" + }, + "locked" : { + "type" : "boolean" + } + } + } + } + } +} \ No newline at end of file diff --git a/tests/test-spring-boot-2.6/src/main/resources/es/migration/V001.01__addDocument.http b/tests/test-spring-boot-2.6/src/main/resources/es/migration/V001.01__addDocument.http new file mode 100644 index 00000000..4f18ba0a --- /dev/null +++ b/tests/test-spring-boot-2.6/src/main/resources/es/migration/V001.01__addDocument.http @@ -0,0 +1,17 @@ +PUT /test_1/_doc/1?refresh +Content-Type: application/json + +{ + "searchable": { + "version": "1", + "locked": false + }, + "doc": { + "version": "1", + "locked": false, + "success": true, + "a": "a a a", + "b": true, + "c": "c" + } +} \ No newline at end of file diff --git a/tests/test-spring-boot-2.6/src/test/java/com/senacor/elasticsearch/evolution/springboot24/ApplicationTests.java b/tests/test-spring-boot-2.6/src/test/java/com/senacor/elasticsearch/evolution/springboot24/ApplicationTests.java new file mode 100644 index 00000000..4aa57e4e --- /dev/null +++ b/tests/test-spring-boot-2.6/src/test/java/com/senacor/elasticsearch/evolution/springboot24/ApplicationTests.java @@ -0,0 +1,54 @@ +package com.senacor.elasticsearch.evolution.springboot24; + +import org.apache.http.HttpHost; +import org.elasticsearch.client.RestClient; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.context.annotation.Bean; +import org.testcontainers.elasticsearch.ElasticsearchContainer; +import org.testcontainers.utility.DockerImageName; + +import java.util.Collections; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +@SpringBootTest(properties = {"spring.elasticsearch.rest.uris=http://localhost:" + ApplicationTests.ELASTICSEARCH_PORT}) +class ApplicationTests { + + static final int ELASTICSEARCH_PORT = 18772; + + @Autowired + private EsUtils esUtils; + + @Test + void contextLoads() { + esUtils.refreshIndices(); + + List documents = esUtils.fetchAllDocuments("test_1"); + + assertThat(documents).hasSize(1); + } + + @TestConfiguration + static class Config { + @Bean(destroyMethod = "stop") + public ElasticsearchContainer elasticsearchContainer(@Value("${elasticsearch.version:7.5.2}") String esVersion) { + ElasticsearchContainer container = new ElasticsearchContainer(DockerImageName + .parse("docker.elastic.co/elasticsearch/elasticsearch") + .withTag(esVersion) + ).withEnv("ES_JAVA_OPTS", "-Xms128m -Xmx128m"); + container.setPortBindings(Collections.singletonList(ELASTICSEARCH_PORT + ":9200")); + container.start(); + return container; + } + + @Bean + public EsUtils esUtils(ElasticsearchContainer elasticsearchContainer) { + return new EsUtils(RestClient.builder(HttpHost.create(elasticsearchContainer.getHttpHostAddress())).build()); + } + } +} \ No newline at end of file diff --git a/tests/test-spring-boot-2.6/src/test/java/com/senacor/elasticsearch/evolution/springboot24/EsUtils.java b/tests/test-spring-boot-2.6/src/test/java/com/senacor/elasticsearch/evolution/springboot24/EsUtils.java new file mode 100644 index 00000000..5750fc23 --- /dev/null +++ b/tests/test-spring-boot-2.6/src/test/java/com/senacor/elasticsearch/evolution/springboot24/EsUtils.java @@ -0,0 +1,72 @@ +package com.senacor.elasticsearch.evolution.springboot24; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.commons.io.IOUtils; +import org.elasticsearch.client.Request; +import org.elasticsearch.client.Response; +import org.elasticsearch.client.RestClient; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +/** + * @author Andreas Keefer + */ +public class EsUtils { + + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + + private final RestClient restClient; + + public EsUtils(RestClient restClient) { + this.restClient = restClient; + } + + public void refreshIndices() { + try { + restClient.performRequest(new Request("POST", "/_refresh")); + } catch (IOException e) { + throw new IllegalStateException("refreshIndices failed", e); + } + } + + public List fetchAllDocuments(String index) { + try { + Request post = new Request("POST", "/" + index + "/_search"); + post.setJsonEntity("{" + + " \"query\": {" + + " \"match_all\": {}" + + " }" + + "}"); + Response response = restClient.performRequest(post); + int statusCode = response.getStatusLine().getStatusCode(); + if (statusCode < 200 || statusCode >= 300) { + throw new IllegalStateException("fetchAllDocuments(" + index + ") failed with HTTP status " + + statusCode + ": " + response.toString()); + } + String body = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8); + + return parseDocuments(body) + .collect(Collectors.toList()); + } catch (IOException e) { + throw new IllegalStateException("fetchAllDocuments(" + index + ") failed", e); + } + } + + private Stream parseDocuments(String body) { + try { + JsonNode jsonNode = OBJECT_MAPPER.readTree(body); + return StreamSupport.stream(jsonNode.get("hits").get("hits").spliterator(), false) + .map(hitNode -> hitNode.get("_source")) + .map(JsonNode::toString); + } catch (IOException e) { + throw new IllegalStateException("parseDocuments failed. body=" + body, e); + } + } +} + From 2ef6be6de7294cb62d17038d129f8f83e732d8ef Mon Sep 17 00:00:00 2001 From: ttj4 Date: Wed, 30 Mar 2022 21:43:01 +0530 Subject: [PATCH 31/54] issue-114: update refresh method to GET from POST --- .../execution/HistoryRepositoryImpl.java | 46 +++++++++++++++---- .../core/ElasticsearchEvolutionTest.java | 5 +- .../execution/HistoryRepositoryImplTest.java | 14 ++++-- 3 files changed, 52 insertions(+), 13 deletions(-) diff --git a/elasticsearch-evolution-core/src/main/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/HistoryRepositoryImpl.java b/elasticsearch-evolution-core/src/main/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/HistoryRepositoryImpl.java index 4d5ddef6..437aaa69 100644 --- a/elasticsearch-evolution-core/src/main/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/HistoryRepositoryImpl.java +++ b/elasticsearch-evolution-core/src/main/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/HistoryRepositoryImpl.java @@ -4,8 +4,9 @@ import com.senacor.elasticsearch.evolution.core.api.migration.HistoryRepository; import com.senacor.elasticsearch.evolution.core.internal.model.MigrationVersion; import com.senacor.elasticsearch.evolution.core.internal.model.dbhistory.MigrationScriptProtocol; -import org.elasticsearch.action.admin.indices.refresh.RefreshRequest; -import org.elasticsearch.action.admin.indices.refresh.RefreshResponse; +import org.apache.http.NameValuePair; +import org.apache.http.client.utils.URLEncodedUtils; +import org.apache.http.message.BasicNameValuePair; import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.action.index.IndexResponse; import org.elasticsearch.action.search.SearchRequest; @@ -28,6 +29,7 @@ import org.slf4j.LoggerFactory; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.time.OffsetDateTime; import java.util.*; @@ -232,13 +234,41 @@ void validateHttpStatus2xxOK(RestStatus status, String description) throws Migra */ void refresh(String... indices) { try { - RefreshResponse res = restHighLevelClient.indices().refresh( - new RefreshRequest(indices) - .indicesOptions(IndicesOptions.lenientExpandOpen()) - , DEFAULT); - validateHttpStatus2xxOK(res.getStatus(), "refresh"); + System.out.println(refreshParams(IndicesOptions.lenientExpandOpen())); + Response res_ = restHighLevelClient.getLowLevelClient() + .performRequest(new Request("GET", expandIndicesForUrl(indices) + "/_refresh?" + refreshParams(IndicesOptions.lenientExpandOpen()) )); + int statusCode = res_.getStatusLine().getStatusCode(); + if (statusCode < 200 || statusCode >= 300) + throw new MigrationException(String.format("%s - response status is not OK: %s", res_.getStatusLine().getReasonPhrase(), statusCode)); } catch (IOException e) { throw new MigrationException("refresh failed!", e); } } -} + + private String expandIndicesForUrl(String... indices) { + String combinedIndices = String.join(",", indices); + return "/" + combinedIndices; + } + + private String refreshParams(IndicesOptions indicesOptions) { + List nameValuePairs = new ArrayList<>(); + nameValuePairs.add(new BasicNameValuePair("ignore_unavailable", Boolean.toString(indicesOptions.ignoreUnavailable()))); + nameValuePairs.add(new BasicNameValuePair("allow_no_indices", Boolean.toString(indicesOptions.allowNoIndices()))); + nameValuePairs.add(new BasicNameValuePair("ignore_throttled", Boolean.toString(indicesOptions.ignoreThrottled()))); + String expandWildcards; + if (!indicesOptions.expandWildcardsOpen() && !indicesOptions.expandWildcardsClosed()) { + expandWildcards = "none"; + } else { + StringJoiner joiner = new StringJoiner(","); + if (indicesOptions.expandWildcardsOpen()) { + joiner.add("open"); + } + if (indicesOptions.expandWildcardsClosed()) { + joiner.add("closed"); + } + expandWildcards = joiner.toString(); + } + nameValuePairs.add(new BasicNameValuePair("expand_wildcards", expandWildcards)); + return URLEncodedUtils.format(nameValuePairs, StandardCharsets.UTF_8); + } +} \ No newline at end of file diff --git a/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/ElasticsearchEvolutionTest.java b/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/ElasticsearchEvolutionTest.java index 98067eee..e9ce1423 100644 --- a/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/ElasticsearchEvolutionTest.java +++ b/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/ElasticsearchEvolutionTest.java @@ -63,6 +63,8 @@ void migrate_Failed() throws IOException { .thenReturn(new SearchHit[0]); when(searchResponse.status()) .thenReturn(RestStatus.OK); + when(restHighLevelClient.getLowLevelClient().performRequest(new Request("GET","/" + indexName + "/_refresh?ignore_unavailable=true&allow_no_indices=true&ignore_throttled=false&expand_wildcards=open"))) + .thenReturn(existsMock); assertThatThrownBy(underTest::migrate) .isInstanceOf(MigrationException.class) @@ -73,7 +75,8 @@ void migrate_Failed() throws IOException { order.verify(restClient).getNodes(); order.verify(restClient).performRequest(any()); order.verify(restHighLevelClient).index(any(), eq(DEFAULT)); - order.verify(restHighLevelClient).indices(); + order.verify(restHighLevelClient, times(1)).getLowLevelClient(); + order.verify(restClient).performRequest(any()); order.verify(restHighLevelClient, times(2)).updateByQuery(any(), eq(DEFAULT)); order.verifyNoMoreInteractions(); } diff --git a/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/HistoryRepositoryImplTest.java b/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/HistoryRepositoryImplTest.java index 1ef48e3b..ba791378 100644 --- a/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/HistoryRepositoryImplTest.java +++ b/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/HistoryRepositoryImplTest.java @@ -4,7 +4,6 @@ import com.senacor.elasticsearch.evolution.core.internal.model.dbhistory.MigrationScriptProtocol; import com.senacor.elasticsearch.evolution.core.test.ArgumentProviders; import com.senacor.elasticsearch.evolution.core.test.MockitoExtension; -import org.elasticsearch.client.IndicesClient; import org.elasticsearch.client.Request; import org.elasticsearch.client.RequestOptions; import org.elasticsearch.client.RestHighLevelClient; @@ -24,7 +23,6 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.when; /** @@ -75,6 +73,9 @@ void failed() throws IOException { class isLocked { @Test void failed() throws IOException { + when( + restHighLevelClient.getLowLevelClient().performRequest(any()).getStatusLine().getStatusCode() + ).thenReturn(200); when(restHighLevelClient.indices().refresh(any(), eq(RequestOptions.DEFAULT)).getStatus()) .thenReturn(RestStatus.OK); when(restHighLevelClient.count(any(), eq(RequestOptions.DEFAULT))) @@ -101,6 +102,10 @@ void failed() throws IOException { class unlock { @Test void failed() throws IOException { + when( + restHighLevelClient.getLowLevelClient().performRequest(any()).getStatusLine().getStatusCode() + ).thenReturn(200); + when(restHighLevelClient.indices().refresh(any(), eq(RequestOptions.DEFAULT)).getStatus()) .thenReturn(RestStatus.OK); when(restHighLevelClient.updateByQuery(any(), eq(RequestOptions.DEFAULT))) @@ -146,8 +151,9 @@ void failed(RestStatus status) { class refresh { @Test void allIndices_failed() throws IOException { - IndicesClient indices = restHighLevelClient.indices(); - doThrow(new IOException("foo")).when(indices).refresh(any(), eq(RequestOptions.DEFAULT)); + when( + restHighLevelClient.getLowLevelClient().performRequest(any()) + ).thenThrow(new IOException("foo")); assertThatThrownBy(() -> underTest.refresh()) .isInstanceOf(MigrationException.class) From f4c214fb7c18e7012a19359fa0877aaddaa9114a Mon Sep 17 00:00:00 2001 From: Andreas Keefer Date: Wed, 30 Mar 2022 18:13:57 +0200 Subject: [PATCH 32/54] remove codacy --- .github/dependabot.yml | 8 +++++++- .github/workflows/quality.yml | 7 +------ README.md | 1 - 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 7ee93406..4a8a9ecd 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -4,7 +4,7 @@ updates: directory: "/" schedule: interval: daily - open-pull-requests-limit: 10 + open-pull-requests-limit: 20 ignore: - dependency-name: org.elasticsearch.client:elasticsearch-rest-high-level-client versions: @@ -13,6 +13,9 @@ updates: - 7.13.x - 7.14.x - 7.15.x + - 7.16.x + - 7.17.x + - 8.x - dependency-name: org.elasticsearch:elasticsearch versions: - 7.11.x @@ -20,3 +23,6 @@ updates: - 7.13.x - 7.14.x - 7.15.x + - 7.16.x + - 7.17.x + - 8.x diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index d39ef561..09c6176b 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -34,9 +34,4 @@ jobs: COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} GITHUB_EVENT_NAME: ${{ github.event_name }} GITHUB_EVENT_PATH: ${{ github.event_path }} - run: $MVN_CMD coveralls:report -DrepoToken=$COVERALLS_REPO_TOKEN -DserviceName=GitHub-Actions - - name: Run codacy-coverage-reporter - uses: codacy/codacy-coverage-reporter-action@v1 - with: - project-token: ${{ secrets.CODACY_PROJECT_TOKEN }} - coverage-reports: elasticsearch-evolution-core/target/site/jacoco/jacoco.xml,spring-boot-starter-elasticsearch-evolution/target/site/jacoco/jacoco.xml + run: $MVN_CMD coveralls:report -DrepoToken=$COVERALLS_REPO_TOKEN -DserviceName=GitHub-Actions \ No newline at end of file diff --git a/README.md b/README.md index fd006205..ec2fcaf8 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,6 @@ [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.senacor.elasticsearch.evolution/elasticsearch-evolution-core/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.senacor.elasticsearch.evolution/elasticsearch-evolution-core) [![Javadocs](https://www.javadoc.io/badge/com.senacor.elasticsearch.evolution/elasticsearch-evolution-core.svg)](https://www.javadoc.io/doc/com.senacor.elasticsearch.evolution/elasticsearch-evolution-core) [![Github build](https://github.com/senacor/elasticsearch-evolution/workflows/Maven%20Matrix%20Build/badge.svg?branch=master)](https://github.com/senacor/elasticsearch-evolution/actions?query=branch%3Amaster) -[![Codacy Badge](https://api.codacy.com/project/badge/Grade/a629ba3201104ecc81c6af7671b29b05)](https://www.codacy.com/app/xtermi2/elasticsearch-evolution?utm_source=github.com&utm_medium=referral&utm_content=senacor/elasticsearch-evolution&utm_campaign=Badge_Grade) [![codebeat badge](https://codebeat.co/badges/29dc74db-88e2-4b26-963b-14eb340ae275)](https://codebeat.co/projects/github.aaakk.us.kg-senacor-elasticsearch-evolution-master) [![Coverage Status](https://coveralls.io/repos/github/senacor/elasticsearch-evolution/badge.svg?branch=master)](https://coveralls.io/github/senacor/elasticsearch-evolution?branch=master) ![Lines of code](https://img.shields.io/tokei/lines/github/senacor/elasticsearch-evolution) From b28057f5d93d07dd0e22d51c323393ecde99d011 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 30 Mar 2022 17:03:45 +0000 Subject: [PATCH 33/54] Bump spring-boot-dependencies from 2.6.0 to 2.6.5 Bumps [spring-boot-dependencies](https://github.com/spring-projects/spring-boot) from 2.6.0 to 2.6.5. - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v2.6.0...v2.6.5) --- updated-dependencies: - dependency-name: org.springframework.boot:spring-boot-dependencies dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index fced78a5..41e8ca73 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ org.springframework.boot spring-boot-dependencies - 2.6.0 + 2.6.5 elasticsearch-evolution From 1a4fbf8b01611d4d7fd5c76d6ff3392c8f7f24f0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 30 Mar 2022 17:15:54 +0000 Subject: [PATCH 34/54] Bump nexus-staging-maven-plugin from 1.6.8 to 1.6.12 Bumps nexus-staging-maven-plugin from 1.6.8 to 1.6.12. --- updated-dependencies: - dependency-name: org.sonatype.plugins:nexus-staging-maven-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index fced78a5..dcb92605 100644 --- a/pom.xml +++ b/pom.xml @@ -98,7 +98,7 @@ 3.0.1 3.3.1 - 1.6.8 + 1.6.12 2.11.0 From e343de543b649c3c52fd932af6ac7bfd9801d6ca Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 30 Mar 2022 17:16:52 +0000 Subject: [PATCH 35/54] Bump elasticsearch from 1.16.2 to 1.16.3 Bumps [elasticsearch](https://github.com/testcontainers/testcontainers-java) from 1.16.2 to 1.16.3. - [Release notes](https://github.com/testcontainers/testcontainers-java/releases) - [Changelog](https://github.com/testcontainers/testcontainers-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/testcontainers/testcontainers-java/compare/1.16.2...1.16.3) --- updated-dependencies: - dependency-name: org.testcontainers:elasticsearch dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml | 2 +- tests/test-spring-boot-2.2/pom.xml | 2 +- tests/test-spring-boot-2.3/pom.xml | 2 +- tests/test-spring-boot-2.4/pom.xml | 2 +- tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml | 2 +- tests/test-spring-boot-2.6/pom.xml | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pom.xml b/pom.xml index fced78a5..99ff4dcd 100644 --- a/pom.xml +++ b/pom.xml @@ -105,7 +105,7 @@ 7.5.2 0.9.12 - 1.16.2 + 1.16.3 diff --git a/tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml b/tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml index 5d1e3e07..7fa95956 100644 --- a/tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml +++ b/tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml @@ -18,7 +18,7 @@ 2.11.0 7.5.2 - 1.16.2 + 1.16.3 diff --git a/tests/test-spring-boot-2.2/pom.xml b/tests/test-spring-boot-2.2/pom.xml index 0de7374b..48069ef2 100644 --- a/tests/test-spring-boot-2.2/pom.xml +++ b/tests/test-spring-boot-2.2/pom.xml @@ -18,7 +18,7 @@ 2.11.0 7.5.2 - 1.16.2 + 1.16.3 diff --git a/tests/test-spring-boot-2.3/pom.xml b/tests/test-spring-boot-2.3/pom.xml index 8e1bcaf8..ab169293 100644 --- a/tests/test-spring-boot-2.3/pom.xml +++ b/tests/test-spring-boot-2.3/pom.xml @@ -18,7 +18,7 @@ 2.11.0 7.5.2 - 1.16.2 + 1.16.3 diff --git a/tests/test-spring-boot-2.4/pom.xml b/tests/test-spring-boot-2.4/pom.xml index ab3a9c56..97eb7929 100644 --- a/tests/test-spring-boot-2.4/pom.xml +++ b/tests/test-spring-boot-2.4/pom.xml @@ -18,7 +18,7 @@ 2.11.0 7.5.2 - 1.16.2 + 1.16.3 diff --git a/tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml b/tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml index 659be013..32cb2bbf 100644 --- a/tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml +++ b/tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml @@ -18,7 +18,7 @@ 2.11.0 7.5.2 - 1.16.2 + 1.16.3 diff --git a/tests/test-spring-boot-2.6/pom.xml b/tests/test-spring-boot-2.6/pom.xml index aaeb93a3..a364b1ae 100644 --- a/tests/test-spring-boot-2.6/pom.xml +++ b/tests/test-spring-boot-2.6/pom.xml @@ -18,7 +18,7 @@ 2.11.0 7.5.2 - 1.16.2 + 1.16.3 From a72cdaae5c5e6141a5bb19438d9afd012fdb98d7 Mon Sep 17 00:00:00 2001 From: Andreas Keefer Date: Wed, 30 Mar 2022 20:44:06 +0200 Subject: [PATCH 36/54] update build tool versions --- .github/workflows/maven-matrix.yml | 42 ++++++++++++++++-------------- .github/workflows/quality.yml | 7 ++--- 2 files changed, 27 insertions(+), 22 deletions(-) diff --git a/.github/workflows/maven-matrix.yml b/.github/workflows/maven-matrix.yml index 8083d842..e9b68dda 100644 --- a/.github/workflows/maven-matrix.yml +++ b/.github/workflows/maven-matrix.yml @@ -19,10 +19,11 @@ jobs: runs-on: ubuntu-18.04 steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Set up JDK ${{ matrix.java }} - uses: actions/setup-java@v1 + uses: actions/setup-java@v3 with: + distribution: zulu java-version: ${{ matrix.java }} - name: Cache local Maven repository uses: actions/cache@v2 @@ -46,13 +47,14 @@ jobs: runs-on: ubuntu-18.04 steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Set up JDK 11 - uses: actions/setup-java@v1 + uses: actions/setup-java@v3 with: + distribution: zulu java-version: 11 - name: Cache local Maven repository - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: | ~/.m2/repository @@ -73,21 +75,22 @@ jobs: runs-on: ubuntu-18.04 steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Import GPG key to sign maven build artifacts - uses: crazy-max/ghaction-import-gpg@v3 + uses: crazy-max/ghaction-import-gpg@v4 with: - gpg-private-key: ${{ secrets.GPG_SECRET_KEYS }} + gpg_private_key: ${{ secrets.GPG_SECRET_KEYS }} passphrase: ${{ secrets.GPG_PASSPHRASE }} - git-user-signingkey: true - git-commit-gpgsign: true + git_user_signingkey: true + git_commit_gpgsign: true - name: Set up JDK 8 # with JDK 11 the maven-javadoc-plugin > 3.0.1 fails with "cannot find symbol org.elasticsearch.*" - uses: actions/setup-java@v1 + uses: actions/setup-java@v3 with: + distribution: zulu java-version: 8 - name: Cache local Maven repository - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: | ~/.m2/repository @@ -113,21 +116,22 @@ jobs: runs-on: ubuntu-18.04 steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Import GPG key to sign maven build artifacts - uses: crazy-max/ghaction-import-gpg@v3 + uses: crazy-max/ghaction-import-gpg@v4 with: - gpg-private-key: ${{ secrets.GPG_SECRET_KEYS }} + gpg_private_key: ${{ secrets.GPG_SECRET_KEYS }} passphrase: ${{ secrets.GPG_PASSPHRASE }} - git-user-signingkey: true - git-commit-gpgsign: true + git_user_signingkey: true + git_commit_gpgsign: true - name: Set up JDK 8 # with JDK 11 the maven-javadoc-plugin > 3.0.1 fails with "cannot find symbol org.elasticsearch.*" - uses: actions/setup-java@v1 + uses: actions/setup-java@v3 with: + distribution: zulu java-version: 8 - name: Cache local Maven repository - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: | ~/.m2/repository diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index 09c6176b..3fb0e128 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -11,14 +11,15 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Set up JDK 8 # build with JDK 8 because of issue https://github.com/trautonen/coveralls-maven-plugin/issues/112 - uses: actions/setup-java@v1 + uses: actions/setup-java@v3 with: + distribution: zulu java-version: 8 - name: Cache local Maven repository - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: | ~/.m2/repository From 0c7e9ee38bb2c2da2e68bb110a651c4a6903d1bf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 30 Mar 2022 19:20:45 +0000 Subject: [PATCH 37/54] Bump maven-javadoc-plugin from 3.3.1 to 3.3.2 Bumps [maven-javadoc-plugin](https://github.com/apache/maven-javadoc-plugin) from 3.3.1 to 3.3.2. - [Release notes](https://github.com/apache/maven-javadoc-plugin/releases) - [Commits](https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.3.1...maven-javadoc-plugin-3.3.2) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-javadoc-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6b3ccda4..0a6b7728 100644 --- a/pom.xml +++ b/pom.xml @@ -97,7 +97,7 @@ 0.8.8-SNAPSHOT 3.0.1 - 3.3.1 + 3.3.2 1.6.12 From 1fe0ec51a7c8156077315bebd04de6c7ca971d49 Mon Sep 17 00:00:00 2001 From: Andreas Keefer Date: Thu, 31 Mar 2022 00:10:09 +0200 Subject: [PATCH 38/54] extend pipeline --- .github/workflows/maven-matrix.yml | 8 +++++--- .github/workflows/quality.yml | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/maven-matrix.yml b/.github/workflows/maven-matrix.yml index e9b68dda..89a04fd8 100644 --- a/.github/workflows/maven-matrix.yml +++ b/.github/workflows/maven-matrix.yml @@ -2,6 +2,8 @@ name: Maven Matrix Build on: push: + # pull_request to run the pipeline on PRs from external, because "push" does not create this pipeline on external PRs + pull_request: schedule: # * is a special character in YAML so you have to quote this string - cron: '30 5 * * *' @@ -70,8 +72,8 @@ jobs: release-dry-run: # this will just build like the real release job, but not do a release (dry run) - needs: [build-and-test-with-jdk, build-and-test-with-es-version] - if: ${{ github.ref != 'refs/heads/release' }} + needs: [ build-and-test-with-jdk, build-and-test-with-es-version ] + if: ${{ github.event_name != 'pull_request' && github.ref != 'refs/heads/release' }} runs-on: ubuntu-18.04 steps: - name: Checkout @@ -111,7 +113,7 @@ jobs: release: # Release to maven central and create Github release - needs: [build-and-test-with-jdk, build-and-test-with-es-version] + needs: [ build-and-test-with-jdk, build-and-test-with-es-version ] if: ${{ github.repository == 'senacor/elasticsearch-evolution' && github.event_name == 'push' && github.ref == 'refs/heads/release' }} runs-on: ubuntu-18.04 steps: diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index 3fb0e128..1c799e9f 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -1,6 +1,6 @@ name: Quality analysis -on: ["push", "pull_request"] +on: [ "push", "pull_request" ] env: MVN_CMD: "./mvnw --settings .cicd.settings.xml -e -B -V" From d56e87fbe3f03876227aa832f02fd2a7ced2d935 Mon Sep 17 00:00:00 2001 From: Andreas Keefer Date: Thu, 31 Mar 2022 00:23:51 +0200 Subject: [PATCH 39/54] run coveralls only on home repo --- .github/workflows/quality.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index 1c799e9f..6ef40cbe 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -31,6 +31,7 @@ jobs: - name: Build and test with Maven run: $MVN_CMD install - name: Execute Maven coveralls Plugin + if: ${{ github.repository == 'senacor/elasticsearch-evolution' }} env: COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} GITHUB_EVENT_NAME: ${{ github.event_name }} From f10570db008f42a36727a8d57620b8675d716d99 Mon Sep 17 00:00:00 2001 From: Andreas Keefer Date: Thu, 31 Mar 2022 00:43:48 +0200 Subject: [PATCH 40/54] docu update --- README.md | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index ec2fcaf8..1a20aeea 100644 --- a/README.md +++ b/README.md @@ -20,8 +20,8 @@ Successful executed migration scripts will not be executed again! ## 2 Features -- tested on Java 8, 9, 10, 11, 12, 13, 14, 15 and 16 -- runs on Spring-Boot 2.1, 2.2, 2.3, 2.4 and 2.5 (and of course without Spring-Boot) +- tested on Java 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 and 18 +- runs on Spring-Boot 2.1, 2.2, 2.3, 2.4, 2.5 and 2.6 (and of course without Spring-Boot) - runs on Elasticsearch version 7.5.0+ - highly configurable (e.g. location(s) of your migration files, migration files format pattern) - placeholder substitution in migration scripts @@ -32,7 +32,7 @@ Successful executed migration scripts will not be executed again! | Compatibility | Spring Boot | Elasticsearch | |----------------------------------|------------------------------|----------------------| -| elasticsearch-evolution >= 0.3.0 | 2.1, 2.2, 2.3, 2.4, 2.5 | 7.5.x and later | +| elasticsearch-evolution >= 0.3.0 | 2.1, 2.2, 2.3, 2.4, 2.5, 2.6 | 7.5.x and later | | elasticsearch-evolution 0.2.x | 1.5, 2.0, 2.1, 2.2, 2.3, 2.4 | 7.0.x - 7.4.x, 6.8.x | NOTE: When you run on Java 11 and using spring-boot 2.2 or 2.3 and you hit [this issue](https://github.com/ronmamo/reflections/issues/279), you have 2 options: @@ -211,6 +211,15 @@ spring.elasticsearch.evolution.historyIndex=es_evolution Since spring boot 2.1 AutoConfiguration for Elasticsearchs REST client is provided (see org.springframework.boot.autoconfigure.elasticsearch.rest.RestClientAutoConfiguration). You can configure the RestHighLevelClient, required for Elasticsearch-Evolution, just like that in your `application.properties`: +##### 5.1.1.1 spring boot 2.6+ +```properties +spring.elasticsearch.uris[0]=https://example.com:9200 +spring.elasticsearch.username=my-user-name +spring.elasticsearch.password=my-secret-pw +``` + +##### 5.1.1.2 spring boot < 2.7 +NOTE: these config properties are deprecated since spring boot 2.6 and may be removed in 2.7! See spring-boot 2.6 [release notes](https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.6-Release-Notes#elasticsearch-property-consolidation). ```properties spring.elasticsearch.rest.uris[0]=https://example.com:9200 spring.elasticsearch.rest.username=my-user-name @@ -278,9 +287,8 @@ ElasticsearchEvolution.configure() ### v0.3.3-SNAPSHOT -- version updates (spring-boot 2.5.2) -- spring boot 2.5 compatibility tests -- spring boot 2.6 compatibility tests +- version updates (spring-boot 2.6.5) +- added spring boot 2.5 and 2.6 compatibility tests - added java 17 and 18 compatibility tests - added Elasticsearch 7.17, 7.16, 7.15, 7.14 and 7.13 compatibility tests From a52a67153ab9a70acf280a2ce0f37376205b9627 Mon Sep 17 00:00:00 2001 From: Andreas Keefer Date: Thu, 31 Mar 2022 00:55:10 +0200 Subject: [PATCH 41/54] docu update --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1a20aeea..198f98d4 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Successful executed migration scripts will not be executed again! - tested on Java 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 and 18 - runs on Spring-Boot 2.1, 2.2, 2.3, 2.4, 2.5 and 2.6 (and of course without Spring-Boot) -- runs on Elasticsearch version 7.5.0+ +- runs on Elasticsearch version 7.5.x - 7.17.x - highly configurable (e.g. location(s) of your migration files, migration files format pattern) - placeholder substitution in migration scripts - easily extendable to your needs @@ -32,7 +32,7 @@ Successful executed migration scripts will not be executed again! | Compatibility | Spring Boot | Elasticsearch | |----------------------------------|------------------------------|----------------------| -| elasticsearch-evolution >= 0.3.0 | 2.1, 2.2, 2.3, 2.4, 2.5, 2.6 | 7.5.x and later | +| elasticsearch-evolution >= 0.3.0 | 2.1, 2.2, 2.3, 2.4, 2.5, 2.6 | 7.5.x - 7.17.x | | elasticsearch-evolution 0.2.x | 1.5, 2.0, 2.1, 2.2, 2.3, 2.4 | 7.0.x - 7.4.x, 6.8.x | NOTE: When you run on Java 11 and using spring-boot 2.2 or 2.3 and you hit [this issue](https://github.com/ronmamo/reflections/issues/279), you have 2 options: From 2383a4297469131fd8bbb792f482694b0e14038e Mon Sep 17 00:00:00 2001 From: ttj4 Date: Thu, 31 Mar 2022 06:59:55 +0530 Subject: [PATCH 42/54] issue-114: updating review comments --- .../migration/execution/HistoryRepositoryImpl.java | 13 ++++++++----- .../execution/HistoryRepositoryImplTest.java | 2 -- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/elasticsearch-evolution-core/src/main/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/HistoryRepositoryImpl.java b/elasticsearch-evolution-core/src/main/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/HistoryRepositoryImpl.java index 437aaa69..245a096b 100644 --- a/elasticsearch-evolution-core/src/main/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/HistoryRepositoryImpl.java +++ b/elasticsearch-evolution-core/src/main/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/HistoryRepositoryImpl.java @@ -234,12 +234,15 @@ void validateHttpStatus2xxOK(RestStatus status, String description) throws Migra */ void refresh(String... indices) { try { - System.out.println(refreshParams(IndicesOptions.lenientExpandOpen())); - Response res_ = restHighLevelClient.getLowLevelClient() - .performRequest(new Request("GET", expandIndicesForUrl(indices) + "/_refresh?" + refreshParams(IndicesOptions.lenientExpandOpen()) )); - int statusCode = res_.getStatusLine().getStatusCode(); + String refreshParams = refreshParams(IndicesOptions.lenientExpandOpen()); + Response res = restHighLevelClient.getLowLevelClient() + .performRequest(new Request("GET", expandIndicesForUrl(indices) + "/_refresh?" + refreshParams)); + logger.debug("refreshing indices {} with params '{}'", Arrays.toString(indices), refreshParams); + int statusCode = res.getStatusLine().getStatusCode(); if (statusCode < 200 || statusCode >= 300) - throw new MigrationException(String.format("%s - response status is not OK: %s", res_.getStatusLine().getReasonPhrase(), statusCode)); + throw new MigrationException( + String.format("%s - response status is not OK: %s", res.getStatusLine().getReasonPhrase(), statusCode) + ); } catch (IOException e) { throw new MigrationException("refresh failed!", e); } diff --git a/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/HistoryRepositoryImplTest.java b/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/HistoryRepositoryImplTest.java index ba791378..3e4bd22c 100644 --- a/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/HistoryRepositoryImplTest.java +++ b/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/HistoryRepositoryImplTest.java @@ -106,8 +106,6 @@ void failed() throws IOException { restHighLevelClient.getLowLevelClient().performRequest(any()).getStatusLine().getStatusCode() ).thenReturn(200); - when(restHighLevelClient.indices().refresh(any(), eq(RequestOptions.DEFAULT)).getStatus()) - .thenReturn(RestStatus.OK); when(restHighLevelClient.updateByQuery(any(), eq(RequestOptions.DEFAULT))) .thenThrow(new IOException("test error")); From 333d05c2cfead0b0120646be1f219aa6d0f59d9c Mon Sep 17 00:00:00 2001 From: Andreas Keefer Date: Thu, 31 Mar 2022 08:08:32 +0200 Subject: [PATCH 43/54] docu pipeline to run coveralls only when token exists --- .github/workflows/quality.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index 6ef40cbe..07b3a171 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -8,7 +8,8 @@ env: jobs: code-analysis: runs-on: ubuntu-18.04 - + env: + COVERALLS_REPO_TOKEN_EXISTS: ${{ secrets.COVERALLS_REPO_TOKEN != '' }} steps: - name: Checkout uses: actions/checkout@v3 @@ -31,7 +32,7 @@ jobs: - name: Build and test with Maven run: $MVN_CMD install - name: Execute Maven coveralls Plugin - if: ${{ github.repository == 'senacor/elasticsearch-evolution' }} + if: ${{ env.COVERALLS_REPO_TOKEN_EXISTS == 'true' }} env: COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} GITHUB_EVENT_NAME: ${{ github.event_name }} From c2afc92cdc4f3434b28e5576fac3d006f6f544bb Mon Sep 17 00:00:00 2001 From: Andreas Keefer Date: Thu, 31 Mar 2022 08:51:31 +0200 Subject: [PATCH 44/54] support new spring-boot (2.6+) elasticsearch config properties --- ...asticsearchEvolutionAutoConfiguration.java | 24 +++++++++++++++++-- .../springboot24/ApplicationTests.java | 2 +- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/spring-boot-starter-elasticsearch-evolution/src/main/java/com/senacor/elasticsearch/evolution/spring/boot/starter/autoconfigure/ElasticsearchEvolutionAutoConfiguration.java b/spring-boot-starter-elasticsearch-evolution/src/main/java/com/senacor/elasticsearch/evolution/spring/boot/starter/autoconfigure/ElasticsearchEvolutionAutoConfiguration.java index f6590a80..34ce6928 100644 --- a/spring-boot-starter-elasticsearch-evolution/src/main/java/com/senacor/elasticsearch/evolution/spring/boot/starter/autoconfigure/ElasticsearchEvolutionAutoConfiguration.java +++ b/spring-boot-starter-elasticsearch-evolution/src/main/java/com/senacor/elasticsearch/evolution/spring/boot/starter/autoconfigure/ElasticsearchEvolutionAutoConfiguration.java @@ -6,6 +6,8 @@ import org.elasticsearch.client.RestClient; import org.elasticsearch.client.RestClientBuilder; import org.elasticsearch.client.RestHighLevelClient; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureBefore; @@ -19,6 +21,7 @@ import org.springframework.context.annotation.Configuration; import java.util.Arrays; +import java.util.List; /** * {@link EnableAutoConfiguration Auto-configuration} for ElasticsearchEvolution @@ -38,6 +41,8 @@ }) public class ElasticsearchEvolutionAutoConfiguration { + private static final Logger logger = LoggerFactory.getLogger(ElasticsearchEvolutionAutoConfiguration.class); + @Bean @ConditionalOnMissingBean @ConditionalOnBean(RestHighLevelClient.class) @@ -62,8 +67,23 @@ public static class RestHighLevelClientConfiguration { */ @Bean @ConditionalOnMissingBean - public RestHighLevelClient restHighLevelClient(@Value("${spring.elasticsearch.rest.uris:http://localhost:9200}") String... uris) { - HttpHost[] httpHosts = Arrays.stream(uris) + public RestHighLevelClient restHighLevelClient( + @Value("${spring.elasticsearch.rest.uris:http://localhost:9200}") String[] urisDeprecated, + @Value("${spring.elasticsearch.uris:}") String[] uris) { + final List urisList; + if (uris != null && uris.length > 0) { + // prefer the new spring-boot (since 2.6) config properties + urisList = Arrays.asList(uris); + } else if (urisDeprecated != null && urisDeprecated.length > 0) { + // fallback to old deprecated spring-boot config properties + urisList = Arrays.asList(urisDeprecated); + } else { + throw new IllegalStateException("spring configuration 'spring.elasticsearch.uris' does not exist"); + } + + logger.info("creating RestHighLevelClient with uris {}", urisList); + + HttpHost[] httpHosts = urisList.stream() .map(HttpHost::create) .toArray(HttpHost[]::new); RestClientBuilder builder = RestClient.builder(httpHosts); diff --git a/tests/test-spring-boot-2.6/src/test/java/com/senacor/elasticsearch/evolution/springboot24/ApplicationTests.java b/tests/test-spring-boot-2.6/src/test/java/com/senacor/elasticsearch/evolution/springboot24/ApplicationTests.java index 4aa57e4e..a913baad 100644 --- a/tests/test-spring-boot-2.6/src/test/java/com/senacor/elasticsearch/evolution/springboot24/ApplicationTests.java +++ b/tests/test-spring-boot-2.6/src/test/java/com/senacor/elasticsearch/evolution/springboot24/ApplicationTests.java @@ -16,7 +16,7 @@ import static org.assertj.core.api.Assertions.assertThat; -@SpringBootTest(properties = {"spring.elasticsearch.rest.uris=http://localhost:" + ApplicationTests.ELASTICSEARCH_PORT}) +@SpringBootTest(properties = {"spring.elasticsearch.uris=http://localhost:" + ApplicationTests.ELASTICSEARCH_PORT}) class ApplicationTests { static final int ELASTICSEARCH_PORT = 18772; From f06722ed38e53590f679f1e567bd3f6873eafed1 Mon Sep 17 00:00:00 2001 From: Andreas Keefer Date: Thu, 31 Mar 2022 17:01:52 +0200 Subject: [PATCH 45/54] update spring-boot to 2.6.6 and elasticsearch 7.17.1 to 7.17.2 --- .github/workflows/maven-matrix.yml | 2 +- README.md | 2 +- .../evolution/core/test/EmbeddedElasticsearchExtension.java | 2 +- pom.xml | 2 +- tests/test-spring-boot-2.6/pom.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/maven-matrix.yml b/.github/workflows/maven-matrix.yml index 89a04fd8..346d5ce1 100644 --- a/.github/workflows/maven-matrix.yml +++ b/.github/workflows/maven-matrix.yml @@ -43,7 +43,7 @@ jobs: build-and-test-with-es-version: strategy: matrix: - elasticsearchVersion: [ "7.17.1", "7.16.3", "7.15.2", "7.14.2", "7.13.4", "7.12.1", "7.11.2", "7.10.2", + elasticsearchVersion: [ "7.17.2", "7.16.3", "7.15.2", "7.14.2", "7.13.4", "7.12.1", "7.11.2", "7.10.2", "7.9.3", "7.8.1", "7.7.1", "7.6.2", "7.5.2" ] fail-fast: false runs-on: ubuntu-18.04 diff --git a/README.md b/README.md index 198f98d4..bbfbb60c 100644 --- a/README.md +++ b/README.md @@ -287,7 +287,7 @@ ElasticsearchEvolution.configure() ### v0.3.3-SNAPSHOT -- version updates (spring-boot 2.6.5) +- version updates (spring-boot 2.6.6) - added spring boot 2.5 and 2.6 compatibility tests - added java 17 and 18 compatibility tests - added Elasticsearch 7.17, 7.16, 7.15, 7.14 and 7.13 compatibility tests diff --git a/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/test/EmbeddedElasticsearchExtension.java b/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/test/EmbeddedElasticsearchExtension.java index f4bf7019..73a9b20e 100644 --- a/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/test/EmbeddedElasticsearchExtension.java +++ b/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/test/EmbeddedElasticsearchExtension.java @@ -35,7 +35,7 @@ public class EmbeddedElasticsearchExtension implements TestInstancePostProcessor private static final Logger logger = LoggerFactory.getLogger(EmbeddedElasticsearchExtension.class); private static final Namespace NAMESPACE = Namespace.create(ExtensionContext.class); private static final SortedSet SUPPORTED_ES_VERSIONS = Collections.unmodifiableSortedSet(new TreeSet<>(Arrays.asList( - "7.17.1", + "7.17.2", "7.16.3", "7.15.2", "7.14.2", diff --git a/pom.xml b/pom.xml index c8f3f395..34aa5938 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ org.springframework.boot spring-boot-dependencies - 2.6.5 + 2.6.6 elasticsearch-evolution diff --git a/tests/test-spring-boot-2.6/pom.xml b/tests/test-spring-boot-2.6/pom.xml index a364b1ae..1d0e4c62 100644 --- a/tests/test-spring-boot-2.6/pom.xml +++ b/tests/test-spring-boot-2.6/pom.xml @@ -5,7 +5,7 @@ org.springframework.boot spring-boot-starter-parent - 2.6.5 + 2.6.6 com.senacor.elasticsearch.evolution From d081539afeb52f36a90ef55708fcd22ab8c23eb2 Mon Sep 17 00:00:00 2001 From: Andreas Keefer Date: Fri, 1 Apr 2022 01:35:56 +0200 Subject: [PATCH 46/54] impl isLocked with LowLevelClient --- elasticsearch-evolution-core/pom.xml | 10 +- .../execution/HistoryRepositoryImpl.java | 124 ++++++++++++++---- .../core/ElasticsearchEvolutionTest.java | 50 +------ .../execution/HistoryRepositoryImplTest.java | 4 +- .../EmbeddedElasticsearchConfiguration.java | 2 +- 5 files changed, 112 insertions(+), 78 deletions(-) diff --git a/elasticsearch-evolution-core/pom.xml b/elasticsearch-evolution-core/pom.xml index c51b66c3..b68d0063 100644 --- a/elasticsearch-evolution-core/pom.xml +++ b/elasticsearch-evolution-core/pom.xml @@ -44,6 +44,11 @@ reflections + + com.fasterxml.jackson.core + jackson-databind + + ch.qos.logback logback-classic @@ -72,11 +77,6 @@ commons-io test - - com.fasterxml.jackson.core - jackson-databind - test - diff --git a/elasticsearch-evolution-core/src/main/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/HistoryRepositoryImpl.java b/elasticsearch-evolution-core/src/main/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/HistoryRepositoryImpl.java index 245a096b..aaa2c4cd 100644 --- a/elasticsearch-evolution-core/src/main/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/HistoryRepositoryImpl.java +++ b/elasticsearch-evolution-core/src/main/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/HistoryRepositoryImpl.java @@ -1,5 +1,7 @@ package com.senacor.elasticsearch.evolution.core.internal.migration.execution; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; import com.senacor.elasticsearch.evolution.core.api.MigrationException; import com.senacor.elasticsearch.evolution.core.api.migration.HistoryRepository; import com.senacor.elasticsearch.evolution.core.internal.model.MigrationVersion; @@ -51,6 +53,7 @@ public class HistoryRepositoryImpl implements HistoryRepository { private final String historyIndex; private final MigrationScriptProtocolMapper migrationScriptProtocolMapper; private final int querySize; + private final ObjectMapper objectMapper; public HistoryRepositoryImpl(RestHighLevelClient restHighLevelClient, String historyIndex, @@ -60,6 +63,7 @@ public HistoryRepositoryImpl(RestHighLevelClient restHighLevelClient, this.historyIndex = requireNotBlank(historyIndex, "historyIndex must not be blank: {}", historyIndex); this.migrationScriptProtocolMapper = requireNonNull(migrationScriptProtocolMapper, "migrationScriptProtocolMapper must not be null"); this.querySize = querySize; + this.objectMapper = new ObjectMapper(); } @Override @@ -72,7 +76,7 @@ public NavigableSet findAll() throws MigrationException .indicesOptions(IndicesOptions.lenientExpandOpen()), DEFAULT); logger.debug("findAll res: {}", searchResponse); - validateHttpStatus2xxOK(searchResponse.status(), "findAll"); + validateHttpStatusIs2xx(searchResponse.status(), "findAll"); // map and order TreeSet res = new TreeSet<>(); @@ -98,7 +102,7 @@ public void saveOrUpdate(MigrationScriptProtocol migrationScriptProtocol) throws .source(source), DEFAULT); logger.debug("saveOrUpdate res: {}", res); - validateHttpStatus2xxOK(res.status(), "saveOrUpdate"); + validateHttpStatusIs2xx(res.status(), "saveOrUpdate"); } catch (IOException e) { throw new MigrationException(String.format("saveOrUpdate of '%s' failed!", migrationScriptProtocol), e); } @@ -108,17 +112,23 @@ public void saveOrUpdate(MigrationScriptProtocol migrationScriptProtocol) throws public boolean isLocked() throws MigrationException { try { refresh(historyIndex); - CountRequest countRequest = new CountRequest(historyIndex) - .query(QueryBuilders.termQuery(MigrationScriptProtocolMapper.LOCKED_FIELD_NAME, true)) - .indicesOptions(IndicesOptions.lenientExpandOpen()); - CountResponse countResponse = restHighLevelClient.count(countRequest, DEFAULT); - validateHttpStatus2xxOK(countResponse.status(), "isLocked"); - if (countResponse.getCount() == 0L) { + String indicesOptions = indicesOptions(IndexOptions.lenientExpandOpen()); + final Request countRequest = new Request("GET", "/" + historyIndex + "/_count?" + indicesOptions); + countRequest.setJsonEntity("{\"query\":{\"term\":{\"" + MigrationScriptProtocolMapper.LOCKED_FIELD_NAME + "\":{\"value\":true}}}}"); + final Response countResponse = restHighLevelClient.getLowLevelClient().performRequest(countRequest); + + validateHttpStatusIs2xx(countResponse, "isLocked"); + + final JsonNode countResBody = objectMapper.readValue(countResponse.getEntity().getContent(), JsonNode.class); + final int count = countResBody.get("count").asInt(); + + if (count == 0) { logger.debug("index '{}' is not locked: no locked documents in index.", historyIndex); return false; } - logger.debug("index '{}' is locked: {} locked documents found.", historyIndex, countResponse.getCount()); + + logger.debug("index '{}' is locked: {} locked documents found.", historyIndex, count); return true; } catch (IOException e) { throw new MigrationException("isLocked check failed!", e); @@ -131,7 +141,7 @@ public boolean lock() { CountRequest countAllReq = new CountRequest(historyIndex) .indicesOptions(IndicesOptions.lenientExpandOpen()); CountResponse countAllRes = restHighLevelClient.count(countAllReq, DEFAULT); - validateHttpStatus2xxOK(countAllRes.status(), "lock.count"); + validateHttpStatusIs2xx(countAllRes.status(), "lock.count"); if (countAllRes.getCount() == 0L) { saveOrUpdate(new MigrationScriptProtocol() @@ -204,7 +214,7 @@ public boolean createIndexIfAbsent() throws MigrationException { // create index Response createRes = restHighLevelClient.getLowLevelClient().performRequest(new Request("PUT", "/" + historyIndex)); - if (existsRes.getStatusLine().getStatusCode() < 200 || createRes.getStatusLine().getStatusCode() > 299) { + if (hasNotStatusCode2xx(createRes)) { throw new IllegalStateException("Could not create Elasticsearch-Evolution history index '" + historyIndex + "'. Create res=" + createRes); } logger.debug("created Elasticsearch-Evolution history index '{}'", historyIndex); @@ -214,6 +224,18 @@ public boolean createIndexIfAbsent() throws MigrationException { } } + private boolean hasNotStatusCode2xx(Response response) { + return isNotStatusCode2xx(response.getStatusLine().getStatusCode()); + } + + private boolean isNotStatusCode2xx(int statusCode) { + return statusCode < 200 || statusCode > 299; + } + + private void validateHttpStatusIs2xx(Response response, String description) throws MigrationException { + validateHttpStatusIs2xx(response.getStatusLine().getStatusCode(), description + " (" + response.getStatusLine().getReasonPhrase() + ")"); + } + /** * validates that HTTP status code is a 2xx code. * @@ -221,9 +243,12 @@ public boolean createIndexIfAbsent() throws MigrationException { * @param description is used in case of a non 2xx status code in the exception message. * @throws MigrationException when the given status code is not a 2xx code. */ - void validateHttpStatus2xxOK(RestStatus status, String description) throws MigrationException { - int statusCode = status.getStatus(); - if (statusCode < 200 || statusCode >= 300) { + void validateHttpStatusIs2xx(RestStatus status, String description) throws MigrationException { + validateHttpStatusIs2xx(status.getStatus(), description); + } + + private void validateHttpStatusIs2xx(int statusCode, String description) throws MigrationException { + if (isNotStatusCode2xx(statusCode)) { throw new MigrationException(String.format("%s - response status is not OK: %s", description, statusCode)); } } @@ -234,15 +259,13 @@ void validateHttpStatus2xxOK(RestStatus status, String description) throws Migra */ void refresh(String... indices) { try { - String refreshParams = refreshParams(IndicesOptions.lenientExpandOpen()); + String refreshParams = indicesOptions(IndexOptions.lenientExpandOpen()); Response res = restHighLevelClient.getLowLevelClient() - .performRequest(new Request("GET", expandIndicesForUrl(indices) + "/_refresh?" + refreshParams)); - logger.debug("refreshing indices {} with params '{}'", Arrays.toString(indices), refreshParams); - int statusCode = res.getStatusLine().getStatusCode(); - if (statusCode < 200 || statusCode >= 300) - throw new MigrationException( - String.format("%s - response status is not OK: %s", res.getStatusLine().getReasonPhrase(), statusCode) - ); + .performRequest(new Request("GET", expandIndicesForUrl(indices) + "/_refresh?" + refreshParams)); + if (logger.isDebugEnabled()) { + logger.debug("refreshing indices {} with params '{}'", Arrays.toString(indices), refreshParams); + } + validateHttpStatusIs2xx(res, "refresh"); } catch (IOException e) { throw new MigrationException("refresh failed!", e); } @@ -253,7 +276,7 @@ private String expandIndicesForUrl(String... indices) { return "/" + combinedIndices; } - private String refreshParams(IndicesOptions indicesOptions) { + private String indicesOptions(IndexOptions indicesOptions) { List nameValuePairs = new ArrayList<>(); nameValuePairs.add(new BasicNameValuePair("ignore_unavailable", Boolean.toString(indicesOptions.ignoreUnavailable()))); nameValuePairs.add(new BasicNameValuePair("allow_no_indices", Boolean.toString(indicesOptions.allowNoIndices()))); @@ -274,4 +297,59 @@ private String refreshParams(IndicesOptions indicesOptions) { nameValuePairs.add(new BasicNameValuePair("expand_wildcards", expandWildcards)); return URLEncodedUtils.format(nameValuePairs, StandardCharsets.UTF_8); } + + /** + * simple replacement for {@link org.elasticsearch.action.support.IndicesOptions} + */ + private static final class IndexOptions { + + private static final IndexOptions LENIENT_EXPAND_OPEN = new IndexOptions( + true, + true, + false, + true, + false); + + private final boolean ignoreUnavailable; + private final boolean allowNoIndices; + private final boolean ignoreThrottled; + private final boolean expandWildcardsOpen; + private final boolean expandWildcardsClosed; + + public IndexOptions(boolean ignoreUnavailable, + boolean allowNoIndices, + boolean ignoreThrottled, + boolean expandWildcardsOpen, + boolean expandWildcardsClosed) { + this.ignoreUnavailable = ignoreUnavailable; + this.allowNoIndices = allowNoIndices; + this.ignoreThrottled = ignoreThrottled; + this.expandWildcardsOpen = expandWildcardsOpen; + this.expandWildcardsClosed = expandWildcardsClosed; + } + + public static IndexOptions lenientExpandOpen() { + return LENIENT_EXPAND_OPEN; + } + + public boolean ignoreUnavailable() { + return ignoreUnavailable; + } + + public boolean allowNoIndices() { + return allowNoIndices; + } + + public boolean ignoreThrottled() { + return ignoreThrottled; + } + + public boolean expandWildcardsOpen() { + return expandWildcardsOpen; + } + + public boolean expandWildcardsClosed() { + return expandWildcardsClosed; + } + } } \ No newline at end of file diff --git a/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/ElasticsearchEvolutionTest.java b/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/ElasticsearchEvolutionTest.java index e9ce1423..f76f33e2 100644 --- a/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/ElasticsearchEvolutionTest.java +++ b/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/ElasticsearchEvolutionTest.java @@ -3,22 +3,19 @@ import com.senacor.elasticsearch.evolution.core.api.MigrationException; import com.senacor.elasticsearch.evolution.core.test.MockitoExtension; import org.apache.http.HttpHost; -import org.elasticsearch.action.search.SearchResponse; -import org.elasticsearch.client.*; -import org.elasticsearch.rest.RestStatus; -import org.elasticsearch.search.SearchHit; +import org.elasticsearch.client.Node; +import org.elasticsearch.client.RestClient; +import org.elasticsearch.client.RestHighLevelClient; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InOrder; import org.mockito.Mock; -import org.mockito.Mockito; import java.io.IOException; import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.*; -import static org.elasticsearch.client.RequestOptions.DEFAULT; import static org.mockito.Answers.RETURNS_DEEP_STUBS; import static org.mockito.Mockito.*; @@ -40,47 +37,6 @@ void setUp() { .thenReturn(singletonList(new Node(HttpHost.create("http://localhost:9200")))); } - @Test - void migrate_Failed() throws IOException { - String indexName = "es_evolution"; - ElasticsearchEvolution underTest = ElasticsearchEvolution.configure() - .setLocations(singletonList("classpath:es/ElasticsearchEvolutionTest/migrate_OK")) - .setHistoryIndex(indexName) - .load(restHighLevelClient); - when(restHighLevelClient.indices().refresh(any(), eq(DEFAULT)).getStatus()) - .thenReturn(RestStatus.OK); - Response existsMock = mock(Response.class, Mockito.RETURNS_DEEP_STUBS); - when(restHighLevelClient.getLowLevelClient().performRequest(new Request("HEAD", "/" + indexName))) - .thenReturn(existsMock); - when(existsMock.getStatusLine().getStatusCode()) - .thenReturn(200); - when(restHighLevelClient.count(any(), eq(DEFAULT)).status()) - .thenReturn(RestStatus.OK); - when(restHighLevelClient.index(any(), eq(DEFAULT)).status()) - .thenReturn(RestStatus.OK); - SearchResponse searchResponse = restHighLevelClient.search(any(), eq(DEFAULT)); - when(searchResponse.getHits().getHits()) - .thenReturn(new SearchHit[0]); - when(searchResponse.status()) - .thenReturn(RestStatus.OK); - when(restHighLevelClient.getLowLevelClient().performRequest(new Request("GET","/" + indexName + "/_refresh?ignore_unavailable=true&allow_no_indices=true&ignore_throttled=false&expand_wildcards=open"))) - .thenReturn(existsMock); - - assertThatThrownBy(underTest::migrate) - .isInstanceOf(MigrationException.class) - .hasMessageStartingWith("execution of script 'FileNameInfoImpl{version=1, description='createTemplateWithIndexMapping', scriptName='V001.00__createTemplateWithIndexMapping.http'}' failed with HTTP status 0: "); - - InOrder order = inOrder(restHighLevelClient, restClient); - order.verify(restHighLevelClient, times(3)).getLowLevelClient(); - order.verify(restClient).getNodes(); - order.verify(restClient).performRequest(any()); - order.verify(restHighLevelClient).index(any(), eq(DEFAULT)); - order.verify(restHighLevelClient, times(1)).getLowLevelClient(); - order.verify(restClient).performRequest(any()); - order.verify(restHighLevelClient, times(2)).updateByQuery(any(), eq(DEFAULT)); - order.verifyNoMoreInteractions(); - } - @Test void migrate_historyMaxQuerySizeToLow() throws IOException { String indexName = "es_evolution"; diff --git a/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/HistoryRepositoryImplTest.java b/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/HistoryRepositoryImplTest.java index 3e4bd22c..d2ed47f3 100644 --- a/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/HistoryRepositoryImplTest.java +++ b/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/HistoryRepositoryImplTest.java @@ -132,14 +132,14 @@ class validateStatus2xxOK { @ParameterizedTest @ArgumentsSource(ArgumentProviders.SuccessHttpCodesProvider.class) void isOK(RestStatus status) { - underTest.validateHttpStatus2xxOK(status, "isOK"); + underTest.validateHttpStatusIs2xx(status, "isOK"); } @ParameterizedTest @ArgumentsSource(ArgumentProviders.FailingHttpCodesProvider.class) void failed(RestStatus status) { String description = "failed"; - assertThatThrownBy(() -> underTest.validateHttpStatus2xxOK(status, description)) + assertThatThrownBy(() -> underTest.validateHttpStatusIs2xx(status, description)) .isInstanceOf(MigrationException.class) .hasMessage("%s - response status is not OK: %s", description, status.getStatus()); } diff --git a/spring-boot-starter-elasticsearch-evolution/src/test/java/com/senacor/elasticsearch/evolution/spring/boot/starter/autoconfigure/EmbeddedElasticsearchConfiguration.java b/spring-boot-starter-elasticsearch-evolution/src/test/java/com/senacor/elasticsearch/evolution/spring/boot/starter/autoconfigure/EmbeddedElasticsearchConfiguration.java index 97672c61..d33068ba 100644 --- a/spring-boot-starter-elasticsearch-evolution/src/test/java/com/senacor/elasticsearch/evolution/spring/boot/starter/autoconfigure/EmbeddedElasticsearchConfiguration.java +++ b/spring-boot-starter-elasticsearch-evolution/src/test/java/com/senacor/elasticsearch/evolution/spring/boot/starter/autoconfigure/EmbeddedElasticsearchConfiguration.java @@ -17,7 +17,7 @@ public class EmbeddedElasticsearchConfiguration { @Bean(destroyMethod = "stop") public ElasticsearchContainer elasticsearchContainer() { - ElasticsearchContainer container = new ElasticsearchContainer("docker.elastic.co/elasticsearch/elasticsearch-oss:7.5.2") + ElasticsearchContainer container = new ElasticsearchContainer("docker.elastic.co/elasticsearch/elasticsearch:7.5.2") .withEnv("ES_JAVA_OPTS", "-Xms128m -Xmx128m"); logger.info("starting embedded ElasticSearch..."); container.start(); From 71f08fa4cc7b476cfb2307684910c69c289e4b85 Mon Sep 17 00:00:00 2001 From: Andreas Keefer Date: Fri, 1 Apr 2022 21:14:41 +0200 Subject: [PATCH 47/54] impl lock() with REST LowLevelClient --- elasticsearch-evolution-core/pom.xml | 8 +- .../execution/HistoryRepositoryImpl.java | 123 +++++++++--------- .../MigrationScriptProtocolMapper.java | 20 +-- .../execution/HistoryRepositoryImplTest.java | 4 +- .../MigrationScriptProtocolMapperTest.java | 5 +- .../execution/MigrationServiceImplIT.java | 4 +- .../execution/MigrationServiceImplTest.java | 4 +- .../evolution/core/test/EsUtils.java | 2 +- .../src/test/resources/log4j2.xml | 22 ++++ .../src/test/resources/logback.xml | 2 +- .../boot/starter/autoconfigure/EsUtils.java | 2 +- .../evolution/springboot23/EsUtils.java | 2 +- .../evolution/springboot22/EsUtils.java | 2 +- .../evolution/springboot23/EsUtils.java | 2 +- tests/test-spring-boot-2.4/pom.xml | 2 +- .../evolution/springboot24/EsUtils.java | 2 +- .../pom.xml | 2 +- .../evolution/springboot25/EsUtils.java | 2 +- .../evolution/springboot24/EsUtils.java | 2 +- 19 files changed, 121 insertions(+), 91 deletions(-) create mode 100644 elasticsearch-evolution-core/src/test/resources/log4j2.xml diff --git a/elasticsearch-evolution-core/pom.xml b/elasticsearch-evolution-core/pom.xml index b68d0063..a7a71421 100644 --- a/elasticsearch-evolution-core/pom.xml +++ b/elasticsearch-evolution-core/pom.xml @@ -49,13 +49,17 @@ jackson-databind + ch.qos.logback logback-classic test - - + + org.apache.logging.log4j + log4j-core + test + org.junit.jupiter junit-jupiter-params diff --git a/elasticsearch-evolution-core/src/main/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/HistoryRepositoryImpl.java b/elasticsearch-evolution-core/src/main/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/HistoryRepositoryImpl.java index aaa2c4cd..78d2f923 100644 --- a/elasticsearch-evolution-core/src/main/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/HistoryRepositoryImpl.java +++ b/elasticsearch-evolution-core/src/main/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/HistoryRepositoryImpl.java @@ -6,19 +6,13 @@ import com.senacor.elasticsearch.evolution.core.api.migration.HistoryRepository; import com.senacor.elasticsearch.evolution.core.internal.model.MigrationVersion; import com.senacor.elasticsearch.evolution.core.internal.model.dbhistory.MigrationScriptProtocol; -import org.apache.http.NameValuePair; -import org.apache.http.client.utils.URLEncodedUtils; -import org.apache.http.message.BasicNameValuePair; -import org.elasticsearch.action.index.IndexRequest; -import org.elasticsearch.action.index.IndexResponse; +import org.apache.http.util.EntityUtils; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.client.Request; import org.elasticsearch.client.Response; import org.elasticsearch.client.RestHighLevelClient; -import org.elasticsearch.client.core.CountRequest; -import org.elasticsearch.client.core.CountResponse; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.index.reindex.BulkByScrollResponse; import org.elasticsearch.index.reindex.UpdateByQueryRequest; @@ -31,7 +25,6 @@ import org.slf4j.LoggerFactory; import java.io.IOException; -import java.nio.charset.StandardCharsets; import java.time.OffsetDateTime; import java.util.*; @@ -95,14 +88,17 @@ public NavigableSet findAll() throws MigrationException @Override public void saveOrUpdate(MigrationScriptProtocol migrationScriptProtocol) throws MigrationException { try { - HashMap source = migrationScriptProtocolMapper.mapToMap(migrationScriptProtocol); - IndexResponse res = restHighLevelClient.index( - new IndexRequest(historyIndex) - .id(requireNonNull(migrationScriptProtocol.getVersion(), "migrationScriptProtocol.version must not be null").getVersion()) - .source(source), - DEFAULT); - logger.debug("saveOrUpdate res: {}", res); - validateHttpStatusIs2xx(res.status(), "saveOrUpdate"); + final String id = requireNonNull(migrationScriptProtocol.getVersion(), "migrationScriptProtocol.version must not be null").getVersion(); + final Request indexRequest = new Request("PUT", "/" + historyIndex + "/_doc/" + id); + indexRequest.addParameter("timeout", "1m"); + final Map source = migrationScriptProtocolMapper.mapToMap(migrationScriptProtocol); + indexRequest.setJsonEntity(objectMapper.writeValueAsString(source)); + final Response res = restHighLevelClient.getLowLevelClient().performRequest(indexRequest); + + if (logger.isDebugEnabled()) { + logger.debug("saveOrUpdate res: {} (body={})", res, EntityUtils.toString(res.getEntity())); + } + validateHttpStatusIs2xx(res, "saveOrUpdate"); } catch (IOException e) { throw new MigrationException(String.format("saveOrUpdate of '%s' failed!", migrationScriptProtocol), e); } @@ -113,17 +109,10 @@ public boolean isLocked() throws MigrationException { try { refresh(historyIndex); - String indicesOptions = indicesOptions(IndexOptions.lenientExpandOpen()); - final Request countRequest = new Request("GET", "/" + historyIndex + "/_count?" + indicesOptions); - countRequest.setJsonEntity("{\"query\":{\"term\":{\"" + MigrationScriptProtocolMapper.LOCKED_FIELD_NAME + "\":{\"value\":true}}}}"); - final Response countResponse = restHighLevelClient.getLowLevelClient().performRequest(countRequest); + final String countQuery = "{\"query\":{\"term\":{\"" + MigrationScriptProtocolMapper.LOCKED_FIELD_NAME + "\":{\"value\":true}}}}"; + final long count = executeCountRequest(Optional.of(countQuery)); - validateHttpStatusIs2xx(countResponse, "isLocked"); - - final JsonNode countResBody = objectMapper.readValue(countResponse.getEntity().getContent(), JsonNode.class); - final int count = countResBody.get("count").asInt(); - - if (count == 0) { + if (count == 0L) { logger.debug("index '{}' is not locked: no locked documents in index.", historyIndex); return false; } @@ -135,15 +124,23 @@ public boolean isLocked() throws MigrationException { } } + private long executeCountRequest(Optional countQuery) throws IOException { + final Request countRequest = new Request("GET", "/" + historyIndex + "/_count"); + countRequest.addParameters(indicesOptions(IndexOptions.lenientExpandOpen())); + countQuery.ifPresent(countRequest::setJsonEntity); + final Response countResponse = restHighLevelClient.getLowLevelClient().performRequest(countRequest); + + validateHttpStatusIs2xx(countResponse, "isLocked"); + + final JsonNode countResBody = objectMapper.readValue(countResponse.getEntity().getContent(), JsonNode.class); + return countResBody.get("count").asLong(); + } + @Override public boolean lock() { try { - CountRequest countAllReq = new CountRequest(historyIndex) - .indicesOptions(IndicesOptions.lenientExpandOpen()); - CountResponse countAllRes = restHighLevelClient.count(countAllReq, DEFAULT); - validateHttpStatusIs2xx(countAllRes.status(), "lock.count"); - - if (countAllRes.getCount() == 0L) { + final long countAll = executeCountRequest(Optional.empty()); + if (countAll == 0L) { saveOrUpdate(new MigrationScriptProtocol() .setVersion(INTERNAL_LOCK_VERSION) .setScriptName("-") @@ -155,8 +152,7 @@ public boolean lock() { .setIndexName(historyIndex) .setLocked(true)); } else { - BulkByScrollResponse bulkByScrollResponse = restHighLevelClient.updateByQuery(createLockQuery(true), DEFAULT); - logger.debug("lock res: {}", bulkByScrollResponse); + executeLockRequest(true, "lock"); } return true; } catch (IOException e) { @@ -181,8 +177,7 @@ public boolean unlock() { DEFAULT); logger.debug("unlock.deleteLockEntry res: {}", deleteInternalLockRes); - BulkByScrollResponse bulkByScrollResponse = restHighLevelClient.updateByQuery(createLockQuery(false), DEFAULT); - logger.debug("unlock.removeLock res: {}", bulkByScrollResponse); + executeLockRequest(false, "unlock.removeLock"); return true; } catch (IOException e) { logger.warn("unlock failed", e); @@ -190,15 +185,25 @@ public boolean unlock() { } } - private UpdateByQueryRequest createLockQuery(boolean lock) { - return new UpdateByQueryRequest(historyIndex) - .setRefresh(true) - .setIndicesOptions(IndicesOptions.lenientExpandOpen()) - .setQuery(QueryBuilders.termQuery(MigrationScriptProtocolMapper.LOCKED_FIELD_NAME, !lock)) - .setScript(new Script(ScriptType.INLINE, - "painless", - "ctx._source." + MigrationScriptProtocolMapper.LOCKED_FIELD_NAME + " = params.lock", - Collections.singletonMap("lock", lock))); + private void executeLockRequest(boolean lock, String debugContext) throws IOException { + final Request updateByQueryRequest = new Request("POST", "/" + historyIndex + "/_update_by_query"); + updateByQueryRequest.addParameters(indicesOptions(IndexOptions.lenientExpandOpen())); + updateByQueryRequest.addParameter("requests_per_second", "-1"); + updateByQueryRequest.addParameter("refresh", "true"); + updateByQueryRequest.addParameter("timeout", "1m"); + updateByQueryRequest.setJsonEntity("{\"script\":" + + "{\"source\":\"ctx._source." + MigrationScriptProtocolMapper.LOCKED_FIELD_NAME + " = params.lock\"," + + "\"lang\":\"painless\"," + + "\"params\":{\"lock\":" + lock + "}" + + "}," + + "\"size\":1000," + + "\"query\":{\"term\":{\"" + MigrationScriptProtocolMapper.LOCKED_FIELD_NAME + "\":{\"value\":" + !lock + "}}}}"); + + final Response updateByQueryResponse = restHighLevelClient.getLowLevelClient().performRequest(updateByQueryRequest); + + if (logger.isDebugEnabled()) { + logger.debug("{} res: {} (body={})", debugContext, updateByQueryResponse, EntityUtils.toString(updateByQueryResponse.getEntity())); + } } @Override @@ -259,12 +264,11 @@ private void validateHttpStatusIs2xx(int statusCode, String description) throws */ void refresh(String... indices) { try { - String refreshParams = indicesOptions(IndexOptions.lenientExpandOpen()); - Response res = restHighLevelClient.getLowLevelClient() - .performRequest(new Request("GET", expandIndicesForUrl(indices) + "/_refresh?" + refreshParams)); - if (logger.isDebugEnabled()) { - logger.debug("refreshing indices {} with params '{}'", Arrays.toString(indices), refreshParams); - } + final Request refreshRequest = new Request("GET", "/" + expandIndicesForUrl(indices) + "/_refresh"); + refreshRequest.addParameters(indicesOptions(IndexOptions.lenientExpandOpen())); + + Response res = restHighLevelClient.getLowLevelClient().performRequest(refreshRequest); + validateHttpStatusIs2xx(res, "refresh"); } catch (IOException e) { throw new MigrationException("refresh failed!", e); @@ -272,15 +276,14 @@ void refresh(String... indices) { } private String expandIndicesForUrl(String... indices) { - String combinedIndices = String.join(",", indices); - return "/" + combinedIndices; + return String.join(",", indices); } - private String indicesOptions(IndexOptions indicesOptions) { - List nameValuePairs = new ArrayList<>(); - nameValuePairs.add(new BasicNameValuePair("ignore_unavailable", Boolean.toString(indicesOptions.ignoreUnavailable()))); - nameValuePairs.add(new BasicNameValuePair("allow_no_indices", Boolean.toString(indicesOptions.allowNoIndices()))); - nameValuePairs.add(new BasicNameValuePair("ignore_throttled", Boolean.toString(indicesOptions.ignoreThrottled()))); + private Map indicesOptions(IndexOptions indicesOptions) { + Map nameValuePairs = new HashMap<>(); + nameValuePairs.put("ignore_unavailable", Boolean.toString(indicesOptions.ignoreUnavailable())); + nameValuePairs.put("allow_no_indices", Boolean.toString(indicesOptions.allowNoIndices())); + nameValuePairs.put("ignore_throttled", Boolean.toString(indicesOptions.ignoreThrottled())); String expandWildcards; if (!indicesOptions.expandWildcardsOpen() && !indicesOptions.expandWildcardsClosed()) { expandWildcards = "none"; @@ -294,8 +297,8 @@ private String indicesOptions(IndexOptions indicesOptions) { } expandWildcards = joiner.toString(); } - nameValuePairs.add(new BasicNameValuePair("expand_wildcards", expandWildcards)); - return URLEncodedUtils.format(nameValuePairs, StandardCharsets.UTF_8); + nameValuePairs.put("expand_wildcards", expandWildcards); + return nameValuePairs; } /** diff --git a/elasticsearch-evolution-core/src/main/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/MigrationScriptProtocolMapper.java b/elasticsearch-evolution-core/src/main/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/MigrationScriptProtocolMapper.java index 0850091c..0b26dd3f 100644 --- a/elasticsearch-evolution-core/src/main/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/MigrationScriptProtocolMapper.java +++ b/elasticsearch-evolution-core/src/main/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/MigrationScriptProtocolMapper.java @@ -23,7 +23,7 @@ public class MigrationScriptProtocolMapper { public static final String INDEX_NAME_FIELD_NAME = "indexName"; public static final String SCRIPT_NAME_FIELD_NAME = "scriptName"; - public HashMap mapToMap(MigrationScriptProtocol migrationScriptProtocol) { + public Map mapToMap(MigrationScriptProtocol migrationScriptProtocol) { HashMap res = new HashMap<>(10); res.put(LOCKED_FIELD_NAME, migrationScriptProtocol.isLocked()); res.put(CHECKSUM_FIELD_NAME, migrationScriptProtocol.getChecksum()); @@ -44,31 +44,31 @@ public HashMap mapToMap(MigrationScriptProtocol migrationScriptP public MigrationScriptProtocol mapFromMap(Map mapData) { MigrationScriptProtocol protocol = new MigrationScriptProtocol(); Optional.ofNullable(mapData.get(LOCKED_FIELD_NAME)) - .map(data -> protocol.setLocked((Boolean) data)); + .ifPresent(data -> protocol.setLocked((Boolean) data)); Optional.ofNullable(mapData.get(CHECKSUM_FIELD_NAME)) - .map(data -> protocol.setChecksum((Integer) data)); + .ifPresent(data -> protocol.setChecksum((Integer) data)); Optional.ofNullable(mapData.get(DESCRIPTION_FIELD_NAME)) - .map(data -> protocol.setDescription((String) data)); + .ifPresent(data -> protocol.setDescription((String) data)); Optional.ofNullable(mapData.get(EXECUTION_RUNTIME_IN_MILLIS_FIELD_NAME)) - .map(data -> protocol.setExecutionRuntimeInMillis((Integer) data)); + .ifPresent(data -> protocol.setExecutionRuntimeInMillis((Integer) data)); Optional.ofNullable(mapData.get(EXECUTION_TIMESTAMP_FIELD_NAME)) - .map(data -> protocol.setExecutionTimestamp(OffsetDateTime.parse((CharSequence) data, DateTimeFormatter.ISO_OFFSET_DATE_TIME))); + .ifPresent(data -> protocol.setExecutionTimestamp(OffsetDateTime.parse((CharSequence) data, DateTimeFormatter.ISO_OFFSET_DATE_TIME))); Optional.ofNullable(mapData.get(SUCCESS_FIELD_NAME)) - .map(data -> protocol.setSuccess((Boolean) data)); + .ifPresent(data -> protocol.setSuccess((Boolean) data)); Optional.ofNullable(mapData.get(VERSION_FIELD_NAME)) - .map(data -> protocol.setVersion((String) data)); + .ifPresent(data -> protocol.setVersion((String) data)); Optional.ofNullable(mapData.get(INDEX_NAME_FIELD_NAME)) - .map(data -> protocol.setIndexName((String) data)); + .ifPresent(data -> protocol.setIndexName((String) data)); Optional.ofNullable(mapData.get(SCRIPT_NAME_FIELD_NAME)) - .map(data -> protocol.setScriptName((String) data)); + .ifPresent(data -> protocol.setScriptName((String) data)); return protocol; } diff --git a/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/HistoryRepositoryImplTest.java b/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/HistoryRepositoryImplTest.java index d2ed47f3..433d3c75 100644 --- a/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/HistoryRepositoryImplTest.java +++ b/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/HistoryRepositoryImplTest.java @@ -59,7 +59,7 @@ void failed() throws IOException { class saveOrUpdate { @Test void failed() throws IOException { - when(restHighLevelClient.index(any(), eq(RequestOptions.DEFAULT))) + when(restHighLevelClient.getLowLevelClient().performRequest(any())) .thenThrow(new IOException("test error")); MigrationScriptProtocol protocol = new MigrationScriptProtocol().setVersion("1"); @@ -91,7 +91,7 @@ void failed() throws IOException { class lock { @Test void failed() throws IOException { - when(restHighLevelClient.count(any(), eq(RequestOptions.DEFAULT))) + when(restHighLevelClient.getLowLevelClient().performRequest(any())) .thenThrow(new IOException("test error")); assertThat(underTest.lock()).isFalse(); diff --git a/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/MigrationScriptProtocolMapperTest.java b/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/MigrationScriptProtocolMapperTest.java index d3b8322e..f34015e9 100644 --- a/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/MigrationScriptProtocolMapperTest.java +++ b/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/MigrationScriptProtocolMapperTest.java @@ -8,6 +8,7 @@ import java.time.OffsetDateTime; import java.time.ZoneOffset; import java.util.HashMap; +import java.util.Map; import static com.senacor.elasticsearch.evolution.core.internal.migration.execution.MigrationScriptProtocolMapper.*; import static org.assertj.core.api.Assertions.assertThat; @@ -26,7 +27,7 @@ class mapToMap { void emptyProtocol() { MigrationScriptProtocol protocol = new MigrationScriptProtocol(); - HashMap res = underTest.mapToMap(protocol); + Map res = underTest.mapToMap(protocol); assertThat(res).hasSize(9) .containsEntry(CHECKSUM_FIELD_NAME, 0) @@ -53,7 +54,7 @@ void fullProtocol() { .setIndexName("index") .setScriptName("foo.http"); - HashMap res = underTest.mapToMap(protocol); + Map res = underTest.mapToMap(protocol); assertThat(res).hasSize(9) .containsEntry(CHECKSUM_FIELD_NAME, 1) diff --git a/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/MigrationServiceImplIT.java b/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/MigrationServiceImplIT.java index 0dd133f8..75330cc0 100644 --- a/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/MigrationServiceImplIT.java +++ b/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/MigrationServiceImplIT.java @@ -43,8 +43,8 @@ class MigrationServiceImplIT { @Mock private HistoryRepository historyRepositoryMock; - private Charset encoding = StandardCharsets.UTF_8; - private ContentType defaultContentType = ContentType.APPLICATION_JSON; + private final Charset encoding = StandardCharsets.UTF_8; + private final ContentType defaultContentType = ContentType.APPLICATION_JSON; @Nested class executeScript { diff --git a/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/MigrationServiceImplTest.java b/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/MigrationServiceImplTest.java index 486adfa1..baa9448b 100644 --- a/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/MigrationServiceImplTest.java +++ b/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/MigrationServiceImplTest.java @@ -59,8 +59,8 @@ class MigrationServiceImplTest { @Mock private RestClient restClient; - private Charset encoding = StandardCharsets.UTF_8; - private ContentType defaultContentType = ContentType.APPLICATION_JSON; + private final Charset encoding = StandardCharsets.UTF_8; + private final ContentType defaultContentType = ContentType.APPLICATION_JSON; @Nested class waitUntilUnlocked { diff --git a/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/test/EsUtils.java b/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/test/EsUtils.java index 78f06c58..ac108a17 100644 --- a/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/test/EsUtils.java +++ b/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/test/EsUtils.java @@ -29,7 +29,7 @@ public EsUtils(RestClient restClient) { public void refreshIndices() { try { - restClient.performRequest(new Request("POST", "/_refresh")); + restClient.performRequest(new Request("GET", "/_refresh")); } catch (IOException e) { throw new IllegalStateException("refreshIndices failed", e); } diff --git a/elasticsearch-evolution-core/src/test/resources/log4j2.xml b/elasticsearch-evolution-core/src/test/resources/log4j2.xml new file mode 100644 index 00000000..4c31a021 --- /dev/null +++ b/elasticsearch-evolution-core/src/test/resources/log4j2.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/elasticsearch-evolution-core/src/test/resources/logback.xml b/elasticsearch-evolution-core/src/test/resources/logback.xml index ba70764b..6ee3bbd3 100644 --- a/elasticsearch-evolution-core/src/test/resources/logback.xml +++ b/elasticsearch-evolution-core/src/test/resources/logback.xml @@ -8,7 +8,7 @@ - + diff --git a/spring-boot-starter-elasticsearch-evolution/src/test/java/com/senacor/elasticsearch/evolution/spring/boot/starter/autoconfigure/EsUtils.java b/spring-boot-starter-elasticsearch-evolution/src/test/java/com/senacor/elasticsearch/evolution/spring/boot/starter/autoconfigure/EsUtils.java index 260691f6..4207c9bd 100644 --- a/spring-boot-starter-elasticsearch-evolution/src/test/java/com/senacor/elasticsearch/evolution/spring/boot/starter/autoconfigure/EsUtils.java +++ b/spring-boot-starter-elasticsearch-evolution/src/test/java/com/senacor/elasticsearch/evolution/spring/boot/starter/autoconfigure/EsUtils.java @@ -18,7 +18,7 @@ public EsUtils(RestClient restClient) { public void refreshIndices() { try { - restClient.performRequest(new Request("POST", "/_refresh")); + restClient.performRequest(new Request("GET", "/_refresh")); } catch (IOException e) { throw new IllegalStateException("refreshIndices failed", e); } diff --git a/tests/test-spring-boot-2.1-scriptsInJarFile/src/test/java/com/senacor/elasticsearch/evolution/springboot23/EsUtils.java b/tests/test-spring-boot-2.1-scriptsInJarFile/src/test/java/com/senacor/elasticsearch/evolution/springboot23/EsUtils.java index 57a73f22..bde1f552 100644 --- a/tests/test-spring-boot-2.1-scriptsInJarFile/src/test/java/com/senacor/elasticsearch/evolution/springboot23/EsUtils.java +++ b/tests/test-spring-boot-2.1-scriptsInJarFile/src/test/java/com/senacor/elasticsearch/evolution/springboot23/EsUtils.java @@ -29,7 +29,7 @@ public EsUtils(RestClient restClient) { public void refreshIndices() { try { - restClient.performRequest(new Request("POST", "/_refresh")); + restClient.performRequest(new Request("GET", "/_refresh")); } catch (IOException e) { throw new IllegalStateException("refreshIndices failed", e); } diff --git a/tests/test-spring-boot-2.2/src/test/java/com/senacor/elasticsearch/evolution/springboot22/EsUtils.java b/tests/test-spring-boot-2.2/src/test/java/com/senacor/elasticsearch/evolution/springboot22/EsUtils.java index 5c0197bd..b2350948 100644 --- a/tests/test-spring-boot-2.2/src/test/java/com/senacor/elasticsearch/evolution/springboot22/EsUtils.java +++ b/tests/test-spring-boot-2.2/src/test/java/com/senacor/elasticsearch/evolution/springboot22/EsUtils.java @@ -29,7 +29,7 @@ public EsUtils(RestClient restClient) { public void refreshIndices() { try { - restClient.performRequest(new Request("POST", "/_refresh")); + restClient.performRequest(new Request("GET", "/_refresh")); } catch (IOException e) { throw new IllegalStateException("refreshIndices failed", e); } diff --git a/tests/test-spring-boot-2.3/src/test/java/com/senacor/elasticsearch/evolution/springboot23/EsUtils.java b/tests/test-spring-boot-2.3/src/test/java/com/senacor/elasticsearch/evolution/springboot23/EsUtils.java index 57a73f22..bde1f552 100644 --- a/tests/test-spring-boot-2.3/src/test/java/com/senacor/elasticsearch/evolution/springboot23/EsUtils.java +++ b/tests/test-spring-boot-2.3/src/test/java/com/senacor/elasticsearch/evolution/springboot23/EsUtils.java @@ -29,7 +29,7 @@ public EsUtils(RestClient restClient) { public void refreshIndices() { try { - restClient.performRequest(new Request("POST", "/_refresh")); + restClient.performRequest(new Request("GET", "/_refresh")); } catch (IOException e) { throw new IllegalStateException("refreshIndices failed", e); } diff --git a/tests/test-spring-boot-2.4/pom.xml b/tests/test-spring-boot-2.4/pom.xml index 97eb7929..b38d54f3 100644 --- a/tests/test-spring-boot-2.4/pom.xml +++ b/tests/test-spring-boot-2.4/pom.xml @@ -5,7 +5,7 @@ org.springframework.boot spring-boot-starter-parent - 2.4.8 + 2.4.13 com.senacor.elasticsearch.evolution diff --git a/tests/test-spring-boot-2.4/src/test/java/com/senacor/elasticsearch/evolution/springboot24/EsUtils.java b/tests/test-spring-boot-2.4/src/test/java/com/senacor/elasticsearch/evolution/springboot24/EsUtils.java index 5750fc23..93ad5096 100644 --- a/tests/test-spring-boot-2.4/src/test/java/com/senacor/elasticsearch/evolution/springboot24/EsUtils.java +++ b/tests/test-spring-boot-2.4/src/test/java/com/senacor/elasticsearch/evolution/springboot24/EsUtils.java @@ -29,7 +29,7 @@ public EsUtils(RestClient restClient) { public void refreshIndices() { try { - restClient.performRequest(new Request("POST", "/_refresh")); + restClient.performRequest(new Request("GET", "/_refresh")); } catch (IOException e) { throw new IllegalStateException("refreshIndices failed", e); } diff --git a/tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml b/tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml index 32cb2bbf..1fbceea5 100644 --- a/tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml +++ b/tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml @@ -5,7 +5,7 @@ org.springframework.boot spring-boot-starter-parent - 2.5.2 + 2.5.11 com.senacor.elasticsearch.evolution diff --git a/tests/test-spring-boot-2.5-scriptsInJarFile/src/test/java/com/senacor/elasticsearch/evolution/springboot25/EsUtils.java b/tests/test-spring-boot-2.5-scriptsInJarFile/src/test/java/com/senacor/elasticsearch/evolution/springboot25/EsUtils.java index 55b59996..8f9e5c3a 100644 --- a/tests/test-spring-boot-2.5-scriptsInJarFile/src/test/java/com/senacor/elasticsearch/evolution/springboot25/EsUtils.java +++ b/tests/test-spring-boot-2.5-scriptsInJarFile/src/test/java/com/senacor/elasticsearch/evolution/springboot25/EsUtils.java @@ -29,7 +29,7 @@ public EsUtils(RestClient restClient) { public void refreshIndices() { try { - restClient.performRequest(new Request("POST", "/_refresh")); + restClient.performRequest(new Request("GET", "/_refresh")); } catch (IOException e) { throw new IllegalStateException("refreshIndices failed", e); } diff --git a/tests/test-spring-boot-2.6/src/test/java/com/senacor/elasticsearch/evolution/springboot24/EsUtils.java b/tests/test-spring-boot-2.6/src/test/java/com/senacor/elasticsearch/evolution/springboot24/EsUtils.java index 5750fc23..93ad5096 100644 --- a/tests/test-spring-boot-2.6/src/test/java/com/senacor/elasticsearch/evolution/springboot24/EsUtils.java +++ b/tests/test-spring-boot-2.6/src/test/java/com/senacor/elasticsearch/evolution/springboot24/EsUtils.java @@ -29,7 +29,7 @@ public EsUtils(RestClient restClient) { public void refreshIndices() { try { - restClient.performRequest(new Request("POST", "/_refresh")); + restClient.performRequest(new Request("GET", "/_refresh")); } catch (IOException e) { throw new IllegalStateException("refreshIndices failed", e); } From 91b181d9bf05bce99f036d2e725f204c681a76f8 Mon Sep 17 00:00:00 2001 From: Andreas Keefer Date: Sat, 2 Apr 2022 12:23:32 +0200 Subject: [PATCH 48/54] impl unlock() with REST LowLevelClient --- .../execution/HistoryRepositoryImpl.java | 33 ++++++++++--------- .../execution/HistoryRepositoryImplTest.java | 16 ++++++--- 2 files changed, 28 insertions(+), 21 deletions(-) diff --git a/elasticsearch-evolution-core/src/main/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/HistoryRepositoryImpl.java b/elasticsearch-evolution-core/src/main/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/HistoryRepositoryImpl.java index 78d2f923..9ca341ff 100644 --- a/elasticsearch-evolution-core/src/main/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/HistoryRepositoryImpl.java +++ b/elasticsearch-evolution-core/src/main/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/HistoryRepositoryImpl.java @@ -13,12 +13,7 @@ import org.elasticsearch.client.Request; import org.elasticsearch.client.Response; import org.elasticsearch.client.RestHighLevelClient; -import org.elasticsearch.index.query.QueryBuilders; -import org.elasticsearch.index.reindex.BulkByScrollResponse; -import org.elasticsearch.index.reindex.UpdateByQueryRequest; import org.elasticsearch.rest.RestStatus; -import org.elasticsearch.script.Script; -import org.elasticsearch.script.ScriptType; import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.builder.SearchSourceBuilder; import org.slf4j.Logger; @@ -165,17 +160,23 @@ public boolean lock() { public boolean unlock() { try { refresh(historyIndex); - BulkByScrollResponse deleteInternalLockRes = restHighLevelClient.updateByQuery( - new UpdateByQueryRequest(historyIndex) - .setRefresh(true) - .setIndicesOptions(IndicesOptions.lenientExpandOpen()) - .setQuery(QueryBuilders.termQuery(MigrationScriptProtocolMapper.VERSION_FIELD_NAME, INTERNAL_LOCK_VERSION)) - .setScript(new Script(ScriptType.INLINE, - "painless", - "ctx.op = \"delete\"", - Collections.emptyMap())), - DEFAULT); - logger.debug("unlock.deleteLockEntry res: {}", deleteInternalLockRes); + + final Request updateByQueryRequest = new Request("POST", "/" + historyIndex + "/_update_by_query"); + updateByQueryRequest.addParameters(indicesOptions(IndexOptions.lenientExpandOpen())); + updateByQueryRequest.addParameter("requests_per_second", "-1"); + updateByQueryRequest.addParameter("refresh", "true"); + updateByQueryRequest.addParameter("timeout", "1m"); + updateByQueryRequest.setJsonEntity("{\"script\":{" + + "\"source\":\"ctx.op = \\\"delete\\\"\"," + + "\"lang\":\"painless\"}," + + "\"size\":1000," + + "\"query\":{\"term\":{\"" + MigrationScriptProtocolMapper.VERSION_FIELD_NAME + "\":{\"value\":\"" + INTERNAL_LOCK_VERSION + "\"}}}}"); + + final Response deleteInternalLockRes = restHighLevelClient.getLowLevelClient().performRequest(updateByQueryRequest); + + if (logger.isDebugEnabled()) { + logger.debug("unlock.deleteLockEntry res: {} (body={})", deleteInternalLockRes, EntityUtils.toString(deleteInternalLockRes.getEntity())); + } executeLockRequest(false, "unlock.removeLock"); return true; diff --git a/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/HistoryRepositoryImplTest.java b/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/HistoryRepositoryImplTest.java index 433d3c75..7252143f 100644 --- a/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/HistoryRepositoryImplTest.java +++ b/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/HistoryRepositoryImplTest.java @@ -4,8 +4,11 @@ import com.senacor.elasticsearch.evolution.core.internal.model.dbhistory.MigrationScriptProtocol; import com.senacor.elasticsearch.evolution.core.test.ArgumentProviders; import com.senacor.elasticsearch.evolution.core.test.MockitoExtension; +import org.apache.http.ProtocolVersion; +import org.apache.http.message.BasicStatusLine; import org.elasticsearch.client.Request; import org.elasticsearch.client.RequestOptions; +import org.elasticsearch.client.Response; import org.elasticsearch.client.RestHighLevelClient; import org.elasticsearch.rest.RestStatus; import org.junit.jupiter.api.BeforeEach; @@ -23,6 +26,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; /** @@ -102,12 +106,14 @@ void failed() throws IOException { class unlock { @Test void failed() throws IOException { - when( - restHighLevelClient.getLowLevelClient().performRequest(any()).getStatusLine().getStatusCode() - ).thenReturn(200); - - when(restHighLevelClient.updateByQuery(any(), eq(RequestOptions.DEFAULT))) + final Response responseMock = mock(Response.class); + when(restHighLevelClient.getLowLevelClient().performRequest(any())) + // first call is refresh, which must succeed + .thenReturn(responseMock) + // second call is updateByQuery which should fail .thenThrow(new IOException("test error")); + when(responseMock.getStatusLine()) + .thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK")); assertThat(underTest.unlock()).isFalse(); } From d3a4c56a6f9dbbc24cc25ffa9a4a74a4c62ec960 Mon Sep 17 00:00:00 2001 From: Andreas Keefer Date: Sat, 2 Apr 2022 22:09:33 +0200 Subject: [PATCH 49/54] drop RestHighLevelClient --- README.md | 30 ++--- elasticsearch-evolution-core/lombok.config | 4 + elasticsearch-evolution-core/pom.xml | 13 +- .../core/ElasticsearchEvolution.java | 35 ++++-- .../config/ElasticsearchEvolutionConfig.java | 8 +- .../execution/HistoryRepositoryImpl.java | 117 ++++++++++-------- .../core/ElasticsearchEvolutionIT.java | 13 +- .../core/ElasticsearchEvolutionTest.java | 14 +-- .../execution/HistoryRepositoryImplIT.java | 28 ++++- .../execution/HistoryRepositoryImplTest.java | 22 ++-- .../execution/MigrationServiceImplTest.java | 19 ++- .../core/test/ArgumentProviders.java | 10 +- pom.xml | 21 +++- .../pom.xml | 7 +- ...asticsearchEvolutionAutoConfiguration.java | 34 +++-- ...ticsearchEvolutionAutoConfigurationIT.java | 9 ++ ...csearchEvolutionAutoConfigurationTest.java | 6 +- .../EmbeddedElasticsearchConfiguration.java | 9 +- tests/migration-scripts/pom.xml | 2 +- tests/pom.xml | 2 +- .../pom.xml | 2 +- tests/test-spring-boot-2.2/pom.xml | 2 +- tests/test-spring-boot-2.3/pom.xml | 2 +- tests/test-spring-boot-2.4/pom.xml | 2 +- .../pom.xml | 2 +- tests/test-spring-boot-2.6/pom.xml | 2 +- 26 files changed, 261 insertions(+), 154 deletions(-) create mode 100644 elasticsearch-evolution-core/lombok.config diff --git a/README.md b/README.md index bbfbb60c..de3efddb 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ First add the latest version of Elasticsearch-Evolution spring boot starter as a ``` -Elasticsearch-Evolution uses internally Elastics RestHighLevelClient and requires at minimum version 7.5.2. Spring boot could use a older version, depending on your Spring Boot version, so update it in your pom.xml: +Elasticsearch-Evolution uses internally Elastics `RestClient` and requires at minimum version 7.5.2. Spring boot could use an older version, depending on your Spring Boot version, so update it in your pom.xml: ```xml @@ -83,12 +83,11 @@ Place your migration scripts in your application classpath at `es/evolution` Create a `ElasticsearchEvolution` instance and execute the migration. ```java -// first create a Elastic RestHighLevelClient -RestHighLevelClient restHighLevelClient = new RestHighLevelClient( - RestClient.builder(HttpHost.create("http://localhost:9200"))); +// first create a Elastic RestClient +RestClient restClient = RestClient.builder(HttpHost.create("http://localhost:9200")).build(); // then create a ElasticsearchEvolution configuration and create a instance of ElasticsearchEvolution with that configuration ElasticsearchEvolution elasticsearchEvolution = ElasticsearchEvolution.configure() - .load(restHighLevelClient); + .load(restClient); // execute the migration elasticsearchEvolution.migrate(); ``` @@ -194,7 +193,7 @@ Elasticsearch-Evolution can be configured to your needs: ### 5.1 Spring Boot -You can set the above configurations via Spring Boots default configuration way. Just use the prefix `spring.elasticsearch.evolution`. Here is a example `application.properties`: +You can set the above configurations via Spring Boots default configuration way. Just use the prefix `spring.elasticsearch.evolution`. Here is an example `application.properties`: ```properties spring.elasticsearch.evolution.locations[0]=classpath:es/migration @@ -208,8 +207,8 @@ spring.elasticsearch.evolution.historyIndex=es_evolution #### 5.1.1 Elasticsearch AutoConfiguration (since spring boot 2.1) -Since spring boot 2.1 AutoConfiguration for Elasticsearchs REST client is provided (see org.springframework.boot.autoconfigure.elasticsearch.rest.RestClientAutoConfiguration). -You can configure the RestHighLevelClient, required for Elasticsearch-Evolution, just like that in your `application.properties`: +Since spring boot 2.1 AutoConfiguration for Elasticsearchs REST client is provided (see org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchRestClientAutoConfiguration). +You can configure the `RestClient`, required for Elasticsearch-Evolution, just like that in your `application.properties`: ##### 5.1.1.1 spring boot 2.6+ ```properties @@ -228,14 +227,14 @@ spring.elasticsearch.rest.password=my-secret-pw #### 5.1.2 Customize Elasticsearch-Evolutions AutoConfiguration -##### 5.1.2.1 Custom RestHighLevelClient +##### 5.1.2.1 Custom RestClient -Elasticsearch-Evolutions just needs a `RestHighLevelClient` as spring bean. -If you don't have spring boot 2.1 or later or you need a special `RestHighLevelClient` configuration e.g. to accept self signed certificates or disable hostname validation, you can provide a custom `RestHighLevelClient` like this: +Elasticsearch-Evolutions just needs a `RestClient` as spring bean. +If you don't have spring boot 2.1 or later or you need a special `RestClient` configuration e.g. to accept self signed certificates or disable hostname validation, you can provide a custom `RestClient` like this: ```java @Bean -public RestHighLevelClient restHighLevelClient() { +public RestClient restClient() { RestClientBuilder builder = RestClient.builder(HttpHost.create("https://localhost:9200")) .setHttpClientConfigCallback(httpClientBuilder -> { CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); @@ -251,13 +250,13 @@ public RestHighLevelClient restHighLevelClient() { return httpClientBuilder; } ); - return new RestHighLevelClient(builder); + return builder.build(); } ``` ##### 5.1.2.2 Custom ElasticsearchEvolutionInitializer -Maybe you want to provide a customised Initializer for Elasticsearch-Evolution e.g with another Order: +Maybe you want to provide a customised Initializer for Elasticsearch-Evolution e.g with another order: ```java @Bean @@ -285,8 +284,9 @@ ElasticsearchEvolution.configure() ## 6 changelog -### v0.3.3-SNAPSHOT +### v0.4.0-SNAPSHOT +- **breaking change**: drop `org.elasticsearch.client.RestHighLevelClient` and replace with `org.elasticsearch.client.RestClient` (LowLevelClient). This will drop the big transitive dependency `org.elasticsearch:elasticsearch` and opens compatibility to Elasticsearch 8 and OpenSearch. - version updates (spring-boot 2.6.6) - added spring boot 2.5 and 2.6 compatibility tests - added java 17 and 18 compatibility tests diff --git a/elasticsearch-evolution-core/lombok.config b/elasticsearch-evolution-core/lombok.config new file mode 100644 index 00000000..258aa9cf --- /dev/null +++ b/elasticsearch-evolution-core/lombok.config @@ -0,0 +1,4 @@ +config.stopBubbling = true +lombok.addLombokGeneratedAnnotation = true +#required to deserialize a @Value @Builder annotated class with jackson without any other configuration +lombok.anyConstructor.addConstructorProperties = true diff --git a/elasticsearch-evolution-core/pom.xml b/elasticsearch-evolution-core/pom.xml index a7a71421..9fdeb6e4 100644 --- a/elasticsearch-evolution-core/pom.xml +++ b/elasticsearch-evolution-core/pom.xml @@ -6,7 +6,7 @@ com.senacor.elasticsearch.evolution elasticsearch-evolution-parent - 0.3.3-SNAPSHOT + 0.4.0-SNAPSHOT ../ elasticsearch-evolution-core @@ -24,9 +24,14 @@ + + org.elasticsearch.client + elasticsearch-rest-client + org.elasticsearch.client elasticsearch-rest-high-level-client + test org.testcontainers @@ -49,6 +54,12 @@ jackson-databind + + org.projectlombok + lombok + provided + + ch.qos.logback diff --git a/elasticsearch-evolution-core/src/main/java/com/senacor/elasticsearch/evolution/core/ElasticsearchEvolution.java b/elasticsearch-evolution-core/src/main/java/com/senacor/elasticsearch/evolution/core/ElasticsearchEvolution.java index e9ac06f1..ef80d791 100644 --- a/elasticsearch-evolution-core/src/main/java/com/senacor/elasticsearch/evolution/core/ElasticsearchEvolution.java +++ b/elasticsearch-evolution-core/src/main/java/com/senacor/elasticsearch/evolution/core/ElasticsearchEvolution.java @@ -1,5 +1,7 @@ package com.senacor.elasticsearch.evolution.core; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; import com.senacor.elasticsearch.evolution.core.api.MigrationException; import com.senacor.elasticsearch.evolution.core.api.config.ElasticsearchEvolutionConfig; import com.senacor.elasticsearch.evolution.core.api.migration.HistoryRepository; @@ -15,7 +17,7 @@ import com.senacor.elasticsearch.evolution.core.internal.model.migration.ParsedMigrationScript; import com.senacor.elasticsearch.evolution.core.internal.model.migration.RawMigrationScript; import org.apache.http.entity.ContentType; -import org.elasticsearch.client.RestHighLevelClient; +import org.elasticsearch.client.RestClient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -32,7 +34,7 @@ *

To get started all you need to do is

*
  * ElasticsearchEvolution esEvolution = ElasticsearchEvolution.configure()
- *   .load(new RestHighLevelClient(RestClient.builder(HttpHost.create(esUrl))));
+ *   .load(RestClient.builder(HttpHost.create(esUrl)).build());
  * esEvolution.migrate();
  * 
*

@@ -44,7 +46,7 @@ public class ElasticsearchEvolution { private static final Logger logger = LoggerFactory.getLogger(ElasticsearchEvolution.class); private final ElasticsearchEvolutionConfig config; - private final RestHighLevelClient restHighLevelClient; + private final RestClient restClient; private final MigrationScriptReader migrationScriptReader; private final MigrationScriptParser migrationScriptParser; @@ -56,7 +58,7 @@ public class ElasticsearchEvolution { *

In its simplest form, this is how you configure Flyway with all defaults to get started:

*
      * ElasticsearchEvolution esEvolution = ElasticsearchEvolution.configure()
-     *     .load(new RestHighLevelClient(RestClient.builder(HttpHost.create(esUrl))));
+     *     .load(RestClient.builder(HttpHost.create(esUrl)).build());
      *  
*

After that you have a fully-configured ElasticsearchEvolution instance at your disposal which can be used to * invoke ElasticsearchEvolution functionality such as migrate().

@@ -71,20 +73,20 @@ public static ElasticsearchEvolutionConfig configure() { * Create ElasticsearchEvolution * * @param elasticsearchEvolutionConfig configuration - * @param restHighLevelClient REST client to interact with Elasticsearch + * @param restClient REST client to interact with Elasticsearch */ public ElasticsearchEvolution(ElasticsearchEvolutionConfig elasticsearchEvolutionConfig, - RestHighLevelClient restHighLevelClient) { + RestClient restClient) { this.config = requireNonNull(elasticsearchEvolutionConfig, "elasticsearchEvolutionConfig must not be null") .validate(); - this.restHighLevelClient = requireNonNull(restHighLevelClient, "restHighLevelClient must not be null"); + this.restClient = requireNonNull(restClient, "restClient must not be null"); this.migrationScriptReader = createMigrationScriptReader(); this.migrationScriptParser = createMigrationScriptParser(); this.migrationService = createMigrationService(); logger.info("Created ElasticsearchEvolution with config='{}' and client='{}'", - this.getConfig(), this.getRestHighLevelClient().getLowLevelClient().getNodes()); + this.getConfig(), this.getRestClient().getNodes()); } /** @@ -121,8 +123,14 @@ protected ElasticsearchEvolutionConfig getConfig() { return config; } - protected RestHighLevelClient getRestHighLevelClient() { - return restHighLevelClient; + protected RestClient getRestClient() { + return restClient; + } + + protected ObjectMapper createObjectMapper(){ + return new ObjectMapper() + // not all search response properties are mapped, so they must be ignored + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); } protected MigrationScriptParser createMigrationScriptParser() { @@ -146,10 +154,11 @@ protected MigrationScriptReader createMigrationScriptReader() { protected HistoryRepository createHistoryRepository() { return new HistoryRepositoryImpl( - getRestHighLevelClient(), + getRestClient(), getConfig().getHistoryIndex(), new MigrationScriptProtocolMapper(), - getConfig().getHistoryMaxQuerySize()); + getConfig().getHistoryMaxQuerySize(), + createObjectMapper()); } protected MigrationService createMigrationService() { @@ -157,7 +166,7 @@ protected MigrationService createMigrationService() { createHistoryRepository(), 1_000, 10_000, - getRestHighLevelClient().getLowLevelClient(), + getRestClient(), ContentType.parse(getConfig().getDefaultContentType()), getConfig().getEncoding()); } diff --git a/elasticsearch-evolution-core/src/main/java/com/senacor/elasticsearch/evolution/core/api/config/ElasticsearchEvolutionConfig.java b/elasticsearch-evolution-core/src/main/java/com/senacor/elasticsearch/evolution/core/api/config/ElasticsearchEvolutionConfig.java index b09df08e..03e410db 100644 --- a/elasticsearch-evolution-core/src/main/java/com/senacor/elasticsearch/evolution/core/api/config/ElasticsearchEvolutionConfig.java +++ b/elasticsearch-evolution-core/src/main/java/com/senacor/elasticsearch/evolution/core/api/config/ElasticsearchEvolutionConfig.java @@ -1,7 +1,7 @@ package com.senacor.elasticsearch.evolution.core.api.config; import com.senacor.elasticsearch.evolution.core.ElasticsearchEvolution; -import org.elasticsearch.client.RestHighLevelClient; +import org.elasticsearch.client.RestClient; import org.springframework.boot.context.properties.ConfigurationProperties; import java.nio.charset.Charset; @@ -86,11 +86,11 @@ public class ElasticsearchEvolutionConfig { /** * Loads this configuration into a new ElasticsearchEvolution instance. * - * @param restHighLevelClient REST client to interact with Elasticsearch + * @param restClient REST client to interact with Elasticsearch * @return The new fully-configured ElasticsearchEvolution instance. */ - public ElasticsearchEvolution load(RestHighLevelClient restHighLevelClient) { - return new ElasticsearchEvolution(this, restHighLevelClient); + public ElasticsearchEvolution load(RestClient restClient) { + return new ElasticsearchEvolution(this, restClient); } /** diff --git a/elasticsearch-evolution-core/src/main/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/HistoryRepositoryImpl.java b/elasticsearch-evolution-core/src/main/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/HistoryRepositoryImpl.java index 9ca341ff..0fcf284b 100644 --- a/elasticsearch-evolution-core/src/main/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/HistoryRepositoryImpl.java +++ b/elasticsearch-evolution-core/src/main/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/HistoryRepositoryImpl.java @@ -1,31 +1,28 @@ package com.senacor.elasticsearch.evolution.core.internal.migration.execution; +import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.senacor.elasticsearch.evolution.core.api.MigrationException; import com.senacor.elasticsearch.evolution.core.api.migration.HistoryRepository; import com.senacor.elasticsearch.evolution.core.internal.model.MigrationVersion; import com.senacor.elasticsearch.evolution.core.internal.model.dbhistory.MigrationScriptProtocol; +import lombok.Value; import org.apache.http.util.EntityUtils; -import org.elasticsearch.action.search.SearchRequest; -import org.elasticsearch.action.search.SearchResponse; -import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.client.Request; import org.elasticsearch.client.Response; -import org.elasticsearch.client.RestHighLevelClient; -import org.elasticsearch.rest.RestStatus; -import org.elasticsearch.search.SearchHit; -import org.elasticsearch.search.builder.SearchSourceBuilder; +import org.elasticsearch.client.RestClient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; +import java.math.BigDecimal; import java.time.OffsetDateTime; import java.util.*; +import java.util.stream.Collectors; import static com.senacor.elasticsearch.evolution.core.internal.utils.AssertionUtils.requireNotBlank; import static java.util.Objects.requireNonNull; -import static org.elasticsearch.client.RequestOptions.DEFAULT; /** * @author Andreas Keefer @@ -37,44 +34,44 @@ public class HistoryRepositoryImpl implements HistoryRepository { private static final MigrationVersion INTERNAL_VERSIONS = MigrationVersion.fromVersion("0"); static final String INDEX_TYPE_DOC = "_doc"; - private final RestHighLevelClient restHighLevelClient; + private final RestClient restClient; private final String historyIndex; private final MigrationScriptProtocolMapper migrationScriptProtocolMapper; private final int querySize; private final ObjectMapper objectMapper; - public HistoryRepositoryImpl(RestHighLevelClient restHighLevelClient, + public HistoryRepositoryImpl(RestClient restClient, String historyIndex, MigrationScriptProtocolMapper migrationScriptProtocolMapper, - int querySize) { - this.restHighLevelClient = requireNonNull(restHighLevelClient, "restHighLevelClient must not be null"); + int querySize, + ObjectMapper objectMapper) { + this.restClient = requireNonNull(restClient, "restClient must not be null"); this.historyIndex = requireNotBlank(historyIndex, "historyIndex must not be blank: {}", historyIndex); this.migrationScriptProtocolMapper = requireNonNull(migrationScriptProtocolMapper, "migrationScriptProtocolMapper must not be null"); this.querySize = querySize; - this.objectMapper = new ObjectMapper(); + this.objectMapper = objectMapper; } @Override public NavigableSet findAll() throws MigrationException { try { - SearchResponse searchResponse = restHighLevelClient.search( - new SearchRequest(historyIndex) - .source(new SearchSourceBuilder() - .size(querySize)) - .indicesOptions(IndicesOptions.lenientExpandOpen()), - DEFAULT); - logger.debug("findAll res: {}", searchResponse); - validateHttpStatusIs2xx(searchResponse.status(), "findAll"); + final Request findAllSearchRequest = new Request("POST", "/" + historyIndex + "/_search"); + findAllSearchRequest.addParameters(indicesOptions(IndexOptions.lenientExpandOpen())); + findAllSearchRequest.setJsonEntity("{\"size\":" + querySize + "}"); + final Response searchResponse = restClient.performRequest(findAllSearchRequest); + final String bodyAsString = EntityUtils.toString(searchResponse.getEntity()); + logger.debug("findAll res: {} (body={})", searchResponse, bodyAsString); + validateHttpStatusIs2xx(searchResponse, "findAll"); + + final SearchResponse body = objectMapper.readValue(bodyAsString, SearchResponse.class); // map and order - TreeSet res = new TreeSet<>(); - Arrays.stream(searchResponse.getHits().getHits()) - .map(SearchHit::getSourceAsMap) + return body.getHits().getHitList().stream() + .map(Hit::getSource) .map(migrationScriptProtocolMapper::mapFromMap) // filter protocols with 0 major version, because they are used internal .filter(protocol -> protocol.getVersion().isMajorNewerThan(INTERNAL_VERSIONS)) - .forEach(res::add); - return res; + .collect(Collectors.toCollection(TreeSet::new)); } catch (IOException e) { throw new MigrationException("findAll failed!", e); } @@ -85,10 +82,9 @@ public void saveOrUpdate(MigrationScriptProtocol migrationScriptProtocol) throws try { final String id = requireNonNull(migrationScriptProtocol.getVersion(), "migrationScriptProtocol.version must not be null").getVersion(); final Request indexRequest = new Request("PUT", "/" + historyIndex + "/_doc/" + id); - indexRequest.addParameter("timeout", "1m"); final Map source = migrationScriptProtocolMapper.mapToMap(migrationScriptProtocol); indexRequest.setJsonEntity(objectMapper.writeValueAsString(source)); - final Response res = restHighLevelClient.getLowLevelClient().performRequest(indexRequest); + final Response res = restClient.performRequest(indexRequest); if (logger.isDebugEnabled()) { logger.debug("saveOrUpdate res: {} (body={})", res, EntityUtils.toString(res.getEntity())); @@ -123,11 +119,11 @@ private long executeCountRequest(Optional countQuery) throws IOException final Request countRequest = new Request("GET", "/" + historyIndex + "/_count"); countRequest.addParameters(indicesOptions(IndexOptions.lenientExpandOpen())); countQuery.ifPresent(countRequest::setJsonEntity); - final Response countResponse = restHighLevelClient.getLowLevelClient().performRequest(countRequest); + final Response countResponse = restClient.performRequest(countRequest); validateHttpStatusIs2xx(countResponse, "isLocked"); - final JsonNode countResBody = objectMapper.readValue(countResponse.getEntity().getContent(), JsonNode.class); + final JsonNode countResBody = objectMapper.readTree(countResponse.getEntity().getContent()); return countResBody.get("count").asLong(); } @@ -165,14 +161,13 @@ public boolean unlock() { updateByQueryRequest.addParameters(indicesOptions(IndexOptions.lenientExpandOpen())); updateByQueryRequest.addParameter("requests_per_second", "-1"); updateByQueryRequest.addParameter("refresh", "true"); - updateByQueryRequest.addParameter("timeout", "1m"); updateByQueryRequest.setJsonEntity("{\"script\":{" + "\"source\":\"ctx.op = \\\"delete\\\"\"," + "\"lang\":\"painless\"}," + "\"size\":1000," + "\"query\":{\"term\":{\"" + MigrationScriptProtocolMapper.VERSION_FIELD_NAME + "\":{\"value\":\"" + INTERNAL_LOCK_VERSION + "\"}}}}"); - final Response deleteInternalLockRes = restHighLevelClient.getLowLevelClient().performRequest(updateByQueryRequest); + final Response deleteInternalLockRes = restClient.performRequest(updateByQueryRequest); if (logger.isDebugEnabled()) { logger.debug("unlock.deleteLockEntry res: {} (body={})", deleteInternalLockRes, EntityUtils.toString(deleteInternalLockRes.getEntity())); @@ -191,7 +186,6 @@ private void executeLockRequest(boolean lock, String debugContext) throws IOExce updateByQueryRequest.addParameters(indicesOptions(IndexOptions.lenientExpandOpen())); updateByQueryRequest.addParameter("requests_per_second", "-1"); updateByQueryRequest.addParameter("refresh", "true"); - updateByQueryRequest.addParameter("timeout", "1m"); updateByQueryRequest.setJsonEntity("{\"script\":" + "{\"source\":\"ctx._source." + MigrationScriptProtocolMapper.LOCKED_FIELD_NAME + " = params.lock\"," + "\"lang\":\"painless\"," + @@ -200,7 +194,7 @@ private void executeLockRequest(boolean lock, String debugContext) throws IOExce "\"size\":1000," + "\"query\":{\"term\":{\"" + MigrationScriptProtocolMapper.LOCKED_FIELD_NAME + "\":{\"value\":" + !lock + "}}}}"); - final Response updateByQueryResponse = restHighLevelClient.getLowLevelClient().performRequest(updateByQueryRequest); + final Response updateByQueryResponse = restClient.performRequest(updateByQueryRequest); if (logger.isDebugEnabled()) { logger.debug("{} res: {} (body={})", debugContext, updateByQueryResponse, EntityUtils.toString(updateByQueryResponse.getEntity())); @@ -210,7 +204,7 @@ private void executeLockRequest(boolean lock, String debugContext) throws IOExce @Override public boolean createIndexIfAbsent() throws MigrationException { try { - Response existsRes = restHighLevelClient.getLowLevelClient().performRequest(new Request("HEAD", "/" + historyIndex)); + Response existsRes = restClient.performRequest(new Request("HEAD", "/" + historyIndex)); boolean exists = 200 == existsRes.getStatusLine().getStatusCode(); if (exists) { logger.debug("Elasticsearch-Evolution history index '{}' already exists.", historyIndex); @@ -219,7 +213,7 @@ public boolean createIndexIfAbsent() throws MigrationException { logger.debug("Elasticsearch-Evolution history index '{}' does not yet exists. Res={}", historyIndex, existsRes); // create index - Response createRes = restHighLevelClient.getLowLevelClient().performRequest(new Request("PUT", "/" + historyIndex)); + Response createRes = restClient.performRequest(new Request("PUT", "/" + historyIndex)); if (hasNotStatusCode2xx(createRes)) { throw new IllegalStateException("Could not create Elasticsearch-Evolution history index '" + historyIndex + "'. Create res=" + createRes); } @@ -242,18 +236,7 @@ private void validateHttpStatusIs2xx(Response response, String description) thro validateHttpStatusIs2xx(response.getStatusLine().getStatusCode(), description + " (" + response.getStatusLine().getReasonPhrase() + ")"); } - /** - * validates that HTTP status code is a 2xx code. - * - * @param status status - * @param description is used in case of a non 2xx status code in the exception message. - * @throws MigrationException when the given status code is not a 2xx code. - */ - void validateHttpStatusIs2xx(RestStatus status, String description) throws MigrationException { - validateHttpStatusIs2xx(status.getStatus(), description); - } - - private void validateHttpStatusIs2xx(int statusCode, String description) throws MigrationException { + void validateHttpStatusIs2xx(int statusCode, String description) throws MigrationException { if (isNotStatusCode2xx(statusCode)) { throw new MigrationException(String.format("%s - response status is not OK: %s", description, statusCode)); } @@ -268,7 +251,7 @@ void refresh(String... indices) { final Request refreshRequest = new Request("GET", "/" + expandIndicesForUrl(indices) + "/_refresh"); refreshRequest.addParameters(indicesOptions(IndexOptions.lenientExpandOpen())); - Response res = restHighLevelClient.getLowLevelClient().performRequest(refreshRequest); + Response res = restClient.performRequest(refreshRequest); validateHttpStatusIs2xx(res, "refresh"); } catch (IOException e) { @@ -356,4 +339,40 @@ public boolean expandWildcardsClosed() { return expandWildcardsClosed; } } + + @Value + static class SearchResponse { + long took; + @JsonProperty("timed_out") + boolean timedOut; + Hits hits; + } + + @Value + static class Hits { + TotalHits total; + @JsonProperty("max_score") + BigDecimal maxScore; + @JsonProperty("hits") + List hitList; + } + + @Value + static class TotalHits { + long value; + /** + * Values of relation: + * - "eq" = Accurate + * - "gte" = "Lower bound, including returned documents" + */ + String relation; + } + + @Value + static class Hit { + @JsonProperty("_id") + String id; + @JsonProperty("_source") + Map source; + } } \ No newline at end of file diff --git a/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/ElasticsearchEvolutionIT.java b/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/ElasticsearchEvolutionIT.java index 2c33c8e6..f1c457b2 100644 --- a/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/ElasticsearchEvolutionIT.java +++ b/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/ElasticsearchEvolutionIT.java @@ -1,6 +1,7 @@ package com.senacor.elasticsearch.evolution.core; import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.senacor.elasticsearch.evolution.core.api.MigrationException; import com.senacor.elasticsearch.evolution.core.api.config.ElasticsearchEvolutionConfig; @@ -44,6 +45,8 @@ class ElasticsearchEvolutionIT { private static final Logger logger = LoggerFactory.getLogger(ElasticsearchEvolutionIT.class); private HistoryRepository historyRepository; + private final ObjectMapper objectMapper = new ObjectMapper() + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); @ParameterizedTest(name = "esVersion: {0}") @ArgumentsSource(ElasticsearchArgumentsProvider.class) @@ -52,9 +55,9 @@ void migrate_OK(String esVersion, EsUtils esUtils, RestHighLevelClient restHighL ElasticsearchEvolutionConfig elasticsearchEvolutionConfig = ElasticsearchEvolution.configure() .setLocations(singletonList("classpath:es/ElasticsearchEvolutionTest/migrate_OK")); String historyIndex = elasticsearchEvolutionConfig.getHistoryIndex(); - historyRepository = new HistoryRepositoryImpl(restHighLevelClient, historyIndex, new MigrationScriptProtocolMapper(), 1000); + historyRepository = new HistoryRepositoryImpl(restHighLevelClient.getLowLevelClient(), historyIndex, new MigrationScriptProtocolMapper(), 1000, objectMapper); ElasticsearchEvolution underTest = elasticsearchEvolutionConfig - .load(restHighLevelClient); + .load(restHighLevelClient.getLowLevelClient()); assertSoftly(softly -> { softly.assertThat(underTest.migrate()) @@ -123,10 +126,10 @@ void migrate_failed_then_fixed_script_and_re_execute(String esVersion, EsUtils e ElasticsearchEvolutionConfig elasticsearchEvolutionConfig = ElasticsearchEvolution.configure() .setLocations(singletonList("classpath:es/ElasticsearchEvolutionTest/migrate_failed_step1")); - historyRepository = new HistoryRepositoryImpl(restHighLevelClient, elasticsearchEvolutionConfig.getHistoryIndex(), new MigrationScriptProtocolMapper(), 1000); + historyRepository = new HistoryRepositoryImpl(restHighLevelClient.getLowLevelClient(), elasticsearchEvolutionConfig.getHistoryIndex(), new MigrationScriptProtocolMapper(), 1000, objectMapper); assertSoftly(softly -> { - softly.assertThatThrownBy(() -> elasticsearchEvolutionConfig.load(restHighLevelClient).migrate()) + softly.assertThatThrownBy(() -> elasticsearchEvolutionConfig.load(restHighLevelClient.getLowLevelClient()).migrate()) .isInstanceOf(MigrationException.class) .hasMessage("execution of script 'FileNameInfoImpl{version=1.1, description='addDocument', scriptName='V001.01__addDocument.http'}' failed"); NavigableSet protocols = historyRepository.findAll(); @@ -144,7 +147,7 @@ void migrate_failed_then_fixed_script_and_re_execute(String esVersion, EsUtils e elasticsearchEvolutionConfig.setLocations(singletonList("classpath:es/ElasticsearchEvolutionTest/migrate_failed_step2_fixed")); assertSoftly(softly -> { - softly.assertThat(elasticsearchEvolutionConfig.load(restHighLevelClient).migrate()) + softly.assertThat(elasticsearchEvolutionConfig.load(restHighLevelClient.getLowLevelClient()).migrate()) .as("# of successful executed scripts") .isEqualTo(1); NavigableSet protocols = historyRepository.findAll(); diff --git a/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/ElasticsearchEvolutionTest.java b/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/ElasticsearchEvolutionTest.java index f76f33e2..b0634621 100644 --- a/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/ElasticsearchEvolutionTest.java +++ b/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/ElasticsearchEvolutionTest.java @@ -45,14 +45,14 @@ void migrate_historyMaxQuerySizeToLow() throws IOException { .setLocations(singletonList("classpath:es/ElasticsearchEvolutionTest/migrate_OK")) .setHistoryIndex(indexName) .setHistoryMaxQuerySize(historyMaxQuerySize) - .load(restHighLevelClient); + .load(restHighLevelClient.getLowLevelClient()); assertThatThrownBy(underTest::migrate) .isInstanceOf(MigrationException.class) .hasMessage("configured historyMaxQuerySize of '%s' is to low for the number of migration scripts of '%s'", historyMaxQuerySize, 7); InOrder order = inOrder(restHighLevelClient, restClient); - order.verify(restHighLevelClient, times(3)).getLowLevelClient(); + order.verify(restHighLevelClient, times(2)).getLowLevelClient(); order.verify(restClient).getNodes(); order.verifyNoMoreInteractions(); } @@ -61,7 +61,7 @@ void migrate_historyMaxQuerySizeToLow() throws IOException { void migrate_empty_location() { ElasticsearchEvolution underTest = ElasticsearchEvolution.configure() .setLocations(singletonList("classpath:es/ElasticsearchEvolutionTest/empty_location")) - .load(restHighLevelClient); + .load(restHighLevelClient.getLowLevelClient()); assertThatCode(underTest::migrate) .doesNotThrowAnyException(); @@ -71,7 +71,7 @@ void migrate_empty_location() { void migrate_non_existing_location() { ElasticsearchEvolution underTest = ElasticsearchEvolution.configure() .setLocations(singletonList("classpath:es/ElasticsearchEvolutionTest/does_not_exist")) - .load(restHighLevelClient); + .load(restHighLevelClient.getLowLevelClient()); assertThatCode(underTest::migrate) .doesNotThrowAnyException(); @@ -81,13 +81,13 @@ void migrate_non_existing_location() { void elasticsearchEvolutionIsNotEnabled() { int migrations = ElasticsearchEvolution.configure() .setEnabled(false) - .load(restHighLevelClient) + .load(restHighLevelClient.getLowLevelClient()) .migrate(); - assertThat(migrations).isEqualTo(0); + assertThat(migrations).isZero(); InOrder order = inOrder(restHighLevelClient, restClient); - order.verify(restHighLevelClient, times(3)).getLowLevelClient(); + order.verify(restHighLevelClient, times(2)).getLowLevelClient(); order.verify(restClient).getNodes(); order.verifyNoMoreInteractions(); } diff --git a/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/HistoryRepositoryImplIT.java b/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/HistoryRepositoryImplIT.java index e28e76e8..604ac1d4 100644 --- a/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/HistoryRepositoryImplIT.java +++ b/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/HistoryRepositoryImplIT.java @@ -1,5 +1,7 @@ package com.senacor.elasticsearch.evolution.core.internal.migration.execution; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; import com.senacor.elasticsearch.evolution.core.internal.model.MigrationVersion; import com.senacor.elasticsearch.evolution.core.internal.model.dbhistory.MigrationScriptProtocol; import com.senacor.elasticsearch.evolution.core.test.EmbeddedElasticsearchExtension; @@ -25,7 +27,6 @@ import java.util.List; import java.util.NavigableSet; -import static com.senacor.elasticsearch.evolution.core.internal.migration.execution.HistoryRepositoryImpl.INDEX_TYPE_DOC; import static com.senacor.elasticsearch.evolution.core.internal.migration.execution.MigrationScriptProtocolMapper.*; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.SoftAssertions.assertSoftly; @@ -42,6 +43,27 @@ class HistoryRepositoryImplIT { @Nested class findAll { + @ParameterizedTest(name = "esVersion: {0}") + @ArgumentsSource(ElasticsearchArgumentsProvider.class) + void can_handle_empty_search_result(String esVersion, EsUtils esUtils, RestHighLevelClient restHighLevelClient) { + HistoryRepositoryImpl underTest = createHistoryRepositoryImpl(restHighLevelClient); + underTest.refresh(INDEX); + + NavigableSet all = underTest.findAll(); + + assertThat(all).isEmpty(); + } + + @ParameterizedTest(name = "esVersion: {0}") + @ArgumentsSource(ElasticsearchArgumentsProvider.class) + void can_handle_when_index_does_not_exist(String esVersion, EsUtils esUtils, RestHighLevelClient restHighLevelClient) { + HistoryRepositoryImpl underTest = createHistoryRepositoryImpl(restHighLevelClient); + + NavigableSet all = underTest.findAll(); + + assertThat(all).isEmpty(); + } + @ParameterizedTest(name = "esVersion: {0}") @ArgumentsSource(ElasticsearchArgumentsProvider.class) void doesNotReturnProtocolsWithMajorVersion0(String esVersion, EsUtils esUtils, RestHighLevelClient restHighLevelClient) { @@ -418,6 +440,8 @@ private void indexDocumentWithLock(boolean locked, EsUtils esUtils, RestHighLeve } private HistoryRepositoryImpl createHistoryRepositoryImpl(RestHighLevelClient restHighLevelClient) { - return new HistoryRepositoryImpl(restHighLevelClient, INDEX, new MigrationScriptProtocolMapper(), 1000); + final ObjectMapper objectMapper = new ObjectMapper() + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + return new HistoryRepositoryImpl(restHighLevelClient.getLowLevelClient(), INDEX, new MigrationScriptProtocolMapper(), 1000, objectMapper); } } \ No newline at end of file diff --git a/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/HistoryRepositoryImplTest.java b/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/HistoryRepositoryImplTest.java index 7252143f..b5eaa19b 100644 --- a/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/HistoryRepositoryImplTest.java +++ b/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/HistoryRepositoryImplTest.java @@ -1,5 +1,7 @@ package com.senacor.elasticsearch.evolution.core.internal.migration.execution; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; import com.senacor.elasticsearch.evolution.core.api.MigrationException; import com.senacor.elasticsearch.evolution.core.internal.model.dbhistory.MigrationScriptProtocol; import com.senacor.elasticsearch.evolution.core.test.ArgumentProviders; @@ -22,8 +24,7 @@ import java.io.IOException; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.assertj.core.api.Assertions.*; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; @@ -43,14 +44,16 @@ class HistoryRepositoryImplTest { @BeforeEach void setUp() { - underTest = new HistoryRepositoryImpl(restHighLevelClient, INDEX, new MigrationScriptProtocolMapper(), 1000); + final ObjectMapper objectMapper = new ObjectMapper() + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + underTest = new HistoryRepositoryImpl(restHighLevelClient.getLowLevelClient(), INDEX, new MigrationScriptProtocolMapper(), 1000, objectMapper); } @Nested class findAll { @Test void failed() throws IOException { - when(restHighLevelClient.search(any(), eq(RequestOptions.DEFAULT))) + when(restHighLevelClient.getLowLevelClient().performRequest(any())) .thenThrow(new IOException("test error")); assertThatThrownBy(() -> underTest.findAll()) @@ -137,17 +140,18 @@ void failedCheckingIndex() throws IOException { class validateStatus2xxOK { @ParameterizedTest @ArgumentsSource(ArgumentProviders.SuccessHttpCodesProvider.class) - void isOK(RestStatus status) { - underTest.validateHttpStatusIs2xx(status, "isOK"); + void isOK(int httpStatusCode) { + assertThatCode(() -> underTest.validateHttpStatusIs2xx(httpStatusCode, "isOK")) + .doesNotThrowAnyException(); } @ParameterizedTest @ArgumentsSource(ArgumentProviders.FailingHttpCodesProvider.class) - void failed(RestStatus status) { + void failed(int httpStatusCode) { String description = "failed"; - assertThatThrownBy(() -> underTest.validateHttpStatusIs2xx(status, description)) + assertThatThrownBy(() -> underTest.validateHttpStatusIs2xx(httpStatusCode, description)) .isInstanceOf(MigrationException.class) - .hasMessage("%s - response status is not OK: %s", description, status.getStatus()); + .hasMessage("%s - response status is not OK: %s", description, httpStatusCode); } } diff --git a/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/MigrationServiceImplTest.java b/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/MigrationServiceImplTest.java index baa9448b..9091c344 100644 --- a/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/MigrationServiceImplTest.java +++ b/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/MigrationServiceImplTest.java @@ -14,7 +14,6 @@ import org.apache.http.entity.ContentType; import org.elasticsearch.client.Response; import org.elasticsearch.client.RestClient; -import org.elasticsearch.rest.RestStatus; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -130,7 +129,7 @@ void scriptsAndHistoryInSync_noScriptsWillBeReturned() { List res = underTest.getPendingScriptsToBeExecuted(parsedMigrationScripts); - assertThat(res).hasSize(0); + assertThat(res).isEmpty(); InOrder order = inOrder(historyRepository); order.verify(historyRepository).findAll(); order.verifyNoMoreInteractions(); @@ -178,7 +177,7 @@ void moreHistoryVersionsThanScripts_warningIsShownAnNoScriptsWillBeReturned() { List res = underTest.getPendingScriptsToBeExecuted(parsedMigrationScripts); - assertThat(res).hasSize(0); + assertThat(res).isEmpty(); InOrder order = inOrder(historyRepository); order.verify(historyRepository).findAll(); order.verifyNoMoreInteractions(); @@ -440,9 +439,9 @@ void executeScript_failed_status(Exception handledError) throws IOException { @ParameterizedTest @ArgumentsSource(FailingHttpCodesProvider.class) - void executeScript_failed_status(RestStatus status) throws IOException { + void executeScript_failed_status(int httpStatusCode) throws IOException { ParsedMigrationScript script = createParsedMigrationScript("1.1"); - Response responseMock = createResponseMock(status.getStatus()); + Response responseMock = createResponseMock(httpStatusCode); doReturn(responseMock).when(restClient).performRequest(any()); MigrationServiceImpl underTest = new MigrationServiceImpl(historyRepository, @@ -456,15 +455,15 @@ void executeScript_failed_status(RestStatus status) throws IOException { .isInstanceOf(MigrationException.class) .hasMessage("execution of script '%s' failed with HTTP status %s: Response(%s)", script.getFileNameInfo(), - status.getStatus(), - status.getStatus()); + httpStatusCode, + httpStatusCode); } @ParameterizedTest @ArgumentsSource(ArgumentProviders.SuccessHttpCodesProvider.class) - void executeScript_OK_status(RestStatus status) throws IOException { + void executeScript_OK_status(int httpStatusCode) throws IOException { ParsedMigrationScript script = createParsedMigrationScript("1.1"); - Response responseMock = createResponseMock(status.getStatus()); + Response responseMock = createResponseMock(httpStatusCode); doReturn(responseMock).when(restClient).performRequest(any()); MigrationServiceImpl underTest = new MigrationServiceImpl(historyRepository, @@ -608,7 +607,7 @@ void emptyScriptsCollection() { List res = underTest.executePendingScripts(emptyList()); - assertThat(res).hasSize(0); + assertThat(res).isEmpty(); InOrder order = inOrder(historyRepository, restClient); order.verifyNoMoreInteractions(); } diff --git a/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/test/ArgumentProviders.java b/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/test/ArgumentProviders.java index 70f4ff32..5d79476e 100644 --- a/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/test/ArgumentProviders.java +++ b/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/test/ArgumentProviders.java @@ -1,11 +1,9 @@ package com.senacor.elasticsearch.evolution.core.test; -import org.elasticsearch.rest.RestStatus; import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.ArgumentsProvider; -import java.util.Objects; import java.util.stream.IntStream; import java.util.stream.Stream; @@ -17,10 +15,8 @@ public class ArgumentProviders { public static class SuccessHttpCodesProvider implements ArgumentsProvider { @Override public Stream provideArguments(ExtensionContext context) throws Exception { - return IntStream.range(200, 300).boxed() - .map(RestStatus::fromCode) - .filter(Objects::nonNull) - .map(Arguments::of); + return IntStream.range(200, 300) + .mapToObj(Arguments::of); } } @@ -29,8 +25,6 @@ public static class FailingHttpCodesProvider implements ArgumentsProvider { public Stream provideArguments(ExtensionContext context) throws Exception { Stream status1xx = IntStream.range(100, 200).boxed(); return Stream.concat(status1xx, IntStream.range(300, 600).boxed()) - .map(RestStatus::fromCode) - .filter(Objects::nonNull) .map(Arguments::of); } } diff --git a/pom.xml b/pom.xml index 34aa5938..f29fdbd8 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.senacor.elasticsearch.evolution elasticsearch-evolution-parent - 0.3.3-SNAPSHOT + 0.4.0-SNAPSHOT pom org.springframework.boot @@ -106,6 +106,7 @@ 7.5.2 0.9.12 1.16.3 + 1.18.22
@@ -139,6 +140,17 @@ + + org.elasticsearch.client + elasticsearch-rest-client + ${elasticsearch.version} + + + commons-logging + commons-logging + + + org.testcontainers elasticsearch @@ -151,6 +163,13 @@ commons-io ${commons-io.version} + + + org.projectlombok + lombok + ${lombok.version} + provided +
diff --git a/spring-boot-starter-elasticsearch-evolution/pom.xml b/spring-boot-starter-elasticsearch-evolution/pom.xml index 2efd3989..2758e2a5 100644 --- a/spring-boot-starter-elasticsearch-evolution/pom.xml +++ b/spring-boot-starter-elasticsearch-evolution/pom.xml @@ -6,7 +6,7 @@ com.senacor.elasticsearch.evolution elasticsearch-evolution-parent - 0.3.3-SNAPSHOT + 0.4.0-SNAPSHOT ../ spring-boot-starter-elasticsearch-evolution @@ -78,6 +78,11 @@ elasticsearch test + + org.elasticsearch.client + elasticsearch-rest-high-level-client + test + org.testcontainers elasticsearch diff --git a/spring-boot-starter-elasticsearch-evolution/src/main/java/com/senacor/elasticsearch/evolution/spring/boot/starter/autoconfigure/ElasticsearchEvolutionAutoConfiguration.java b/spring-boot-starter-elasticsearch-evolution/src/main/java/com/senacor/elasticsearch/evolution/spring/boot/starter/autoconfigure/ElasticsearchEvolutionAutoConfiguration.java index 34ce6928..c092de2a 100644 --- a/spring-boot-starter-elasticsearch-evolution/src/main/java/com/senacor/elasticsearch/evolution/spring/boot/starter/autoconfigure/ElasticsearchEvolutionAutoConfiguration.java +++ b/spring-boot-starter-elasticsearch-evolution/src/main/java/com/senacor/elasticsearch/evolution/spring/boot/starter/autoconfigure/ElasticsearchEvolutionAutoConfiguration.java @@ -5,7 +5,6 @@ import org.apache.http.HttpHost; import org.elasticsearch.client.RestClient; import org.elasticsearch.client.RestClientBuilder; -import org.elasticsearch.client.RestHighLevelClient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; @@ -45,10 +44,10 @@ public class ElasticsearchEvolutionAutoConfiguration { @Bean @ConditionalOnMissingBean - @ConditionalOnBean(RestHighLevelClient.class) + @ConditionalOnBean(RestClient.class) public ElasticsearchEvolution elasticsearchEvolution(ElasticsearchEvolutionConfig elasticsearchEvolutionConfig, - RestHighLevelClient restHighLevelClient) { - return new ElasticsearchEvolution(elasticsearchEvolutionConfig, restHighLevelClient); + RestClient restClient) { + return new ElasticsearchEvolution(elasticsearchEvolutionConfig, restClient); } @Bean @@ -58,16 +57,16 @@ public ElasticsearchEvolutionInitializer elasticsearchEvolutionInitializer(Elast } @Configuration - @ConditionalOnClass(RestHighLevelClient.class) - public static class RestHighLevelClientConfiguration { + @ConditionalOnClass(RestClient.class) + public static class RestClientConfiguration { /** - * @return default RestHighLevelClient if {@link org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchRestClientAutoConfiguration} is not used - * and no RestHighLevelClient is available. + * @return default RestClientBuilder if {@link org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchRestClientAutoConfiguration} is not used + * and no RestClient is available. */ @Bean - @ConditionalOnMissingBean - public RestHighLevelClient restHighLevelClient( + @ConditionalOnMissingBean({RestClientBuilder.class, RestClient.class}) + public RestClientBuilder restClientBuilder( @Value("${spring.elasticsearch.rest.uris:http://localhost:9200}") String[] urisDeprecated, @Value("${spring.elasticsearch.uris:}") String[] uris) { final List urisList; @@ -81,14 +80,23 @@ public RestHighLevelClient restHighLevelClient( throw new IllegalStateException("spring configuration 'spring.elasticsearch.uris' does not exist"); } - logger.info("creating RestHighLevelClient with uris {}", urisList); + logger.info("creating RestClientBuilder with uris {}", urisList); HttpHost[] httpHosts = urisList.stream() .map(HttpHost::create) .toArray(HttpHost[]::new); - RestClientBuilder builder = RestClient.builder(httpHosts); - return new RestHighLevelClient(builder); + return RestClient.builder(httpHosts); } + /** + * @return default RestClient no RestClient is available. + */ + @Bean + @ConditionalOnBean(RestClientBuilder.class) + @ConditionalOnMissingBean + public RestClient restClient(RestClientBuilder restClientBuilder) { + logger.info("creating RestClient from {}", restClientBuilder); + return restClientBuilder.build(); + } } } diff --git a/spring-boot-starter-elasticsearch-evolution/src/test/java/com/senacor/elasticsearch/evolution/spring/boot/starter/autoconfigure/ElasticsearchEvolutionAutoConfigurationIT.java b/spring-boot-starter-elasticsearch-evolution/src/test/java/com/senacor/elasticsearch/evolution/spring/boot/starter/autoconfigure/ElasticsearchEvolutionAutoConfigurationIT.java index fb218522..c02ee581 100644 --- a/spring-boot-starter-elasticsearch-evolution/src/test/java/com/senacor/elasticsearch/evolution/spring/boot/starter/autoconfigure/ElasticsearchEvolutionAutoConfigurationIT.java +++ b/spring-boot-starter-elasticsearch-evolution/src/test/java/com/senacor/elasticsearch/evolution/spring/boot/starter/autoconfigure/ElasticsearchEvolutionAutoConfigurationIT.java @@ -2,9 +2,12 @@ import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.search.SearchResponse; +import org.elasticsearch.client.Node; +import org.elasticsearch.client.RestClient; import org.elasticsearch.client.RestHighLevelClient; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.search.builder.SearchSourceBuilder; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; @@ -29,11 +32,17 @@ class ElasticsearchEvolutionAutoConfigurationIT { @Autowired + private RestClient restClient; private RestHighLevelClient restHighLevelClient; @Autowired private EsUtils esUtils; + @BeforeEach + void setUp() { + restHighLevelClient = new RestHighLevelClient(RestClient.builder(restClient.getNodes().toArray(new Node[0]))); + } + @Test void migrateOnApplicationStartViaInitializer() throws IOException { esUtils.refreshIndices(); diff --git a/spring-boot-starter-elasticsearch-evolution/src/test/java/com/senacor/elasticsearch/evolution/spring/boot/starter/autoconfigure/ElasticsearchEvolutionAutoConfigurationTest.java b/spring-boot-starter-elasticsearch-evolution/src/test/java/com/senacor/elasticsearch/evolution/spring/boot/starter/autoconfigure/ElasticsearchEvolutionAutoConfigurationTest.java index fa6292ce..8870bb6a 100644 --- a/spring-boot-starter-elasticsearch-evolution/src/test/java/com/senacor/elasticsearch/evolution/spring/boot/starter/autoconfigure/ElasticsearchEvolutionAutoConfigurationTest.java +++ b/spring-boot-starter-elasticsearch-evolution/src/test/java/com/senacor/elasticsearch/evolution/spring/boot/starter/autoconfigure/ElasticsearchEvolutionAutoConfigurationTest.java @@ -1,7 +1,7 @@ package com.senacor.elasticsearch.evolution.spring.boot.starter.autoconfigure; import com.senacor.elasticsearch.evolution.core.ElasticsearchEvolution; -import org.elasticsearch.client.RestHighLevelClient; +import org.elasticsearch.client.RestClient; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.slf4j.Logger; @@ -33,8 +33,8 @@ void whenSpringContextIsBootstrapped_thenNoExceptions() { } @Test - void restHighLevelClient_existsAsBean() { - assertThat(applicationContext.getBean(RestHighLevelClient.class)).isNotNull(); + void restClient_existsAsBean() { + assertThat(applicationContext.getBean(RestClient.class)).isNotNull(); } @Test diff --git a/spring-boot-starter-elasticsearch-evolution/src/test/java/com/senacor/elasticsearch/evolution/spring/boot/starter/autoconfigure/EmbeddedElasticsearchConfiguration.java b/spring-boot-starter-elasticsearch-evolution/src/test/java/com/senacor/elasticsearch/evolution/spring/boot/starter/autoconfigure/EmbeddedElasticsearchConfiguration.java index d33068ba..fb2621d8 100644 --- a/spring-boot-starter-elasticsearch-evolution/src/test/java/com/senacor/elasticsearch/evolution/spring/boot/starter/autoconfigure/EmbeddedElasticsearchConfiguration.java +++ b/spring-boot-starter-elasticsearch-evolution/src/test/java/com/senacor/elasticsearch/evolution/spring/boot/starter/autoconfigure/EmbeddedElasticsearchConfiguration.java @@ -3,7 +3,6 @@ import org.apache.http.HttpHost; import org.elasticsearch.client.RestClient; import org.elasticsearch.client.RestClientBuilder; -import org.elasticsearch.client.RestHighLevelClient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Bean; @@ -25,13 +24,13 @@ public ElasticsearchContainer elasticsearchContainer() { } @Bean - public RestHighLevelClient restHighLevelClient(ElasticsearchContainer elasticsearchContainer) { + public RestClient restClient(ElasticsearchContainer elasticsearchContainer) { RestClientBuilder builder = RestClient.builder(HttpHost.create(elasticsearchContainer.getHttpHostAddress())); - return new RestHighLevelClient(builder); + return builder.build(); } @Bean - public EsUtils esUtils(RestHighLevelClient restHighLevelClient) { - return new EsUtils(restHighLevelClient.getLowLevelClient()); + public EsUtils esUtils(RestClient restClient) { + return new EsUtils(restClient); } } diff --git a/tests/migration-scripts/pom.xml b/tests/migration-scripts/pom.xml index a565b064..e4971c67 100644 --- a/tests/migration-scripts/pom.xml +++ b/tests/migration-scripts/pom.xml @@ -4,7 +4,7 @@ 4.0.0 com.senacor.elasticsearch.evolution migration-scripts - 0.3.3-SNAPSHOT + 0.4.0-SNAPSHOT jar containing migration files jar diff --git a/tests/pom.xml b/tests/pom.xml index b436bbaf..10cb47fd 100644 --- a/tests/pom.xml +++ b/tests/pom.xml @@ -6,7 +6,7 @@ com.senacor.elasticsearch.evolution elasticsearch-evolution-parent - 0.3.3-SNAPSHOT + 0.4.0-SNAPSHOT ../ tests diff --git a/tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml b/tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml index 7fa95956..3798846b 100644 --- a/tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml +++ b/tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml @@ -10,7 +10,7 @@ com.senacor.elasticsearch.evolution test-spring-boot-2.1-scriptsInJarFile - 0.3.3-SNAPSHOT + 0.4.0-SNAPSHOT Demo project for Spring Boot diff --git a/tests/test-spring-boot-2.2/pom.xml b/tests/test-spring-boot-2.2/pom.xml index 48069ef2..240c4a2c 100644 --- a/tests/test-spring-boot-2.2/pom.xml +++ b/tests/test-spring-boot-2.2/pom.xml @@ -10,7 +10,7 @@ com.senacor.elasticsearch.evolution test-spring-boot-2.2 - 0.3.3-SNAPSHOT + 0.4.0-SNAPSHOT Demo project for Spring Boot diff --git a/tests/test-spring-boot-2.3/pom.xml b/tests/test-spring-boot-2.3/pom.xml index ab169293..ac2b9a20 100644 --- a/tests/test-spring-boot-2.3/pom.xml +++ b/tests/test-spring-boot-2.3/pom.xml @@ -10,7 +10,7 @@ com.senacor.elasticsearch.evolution test-spring-boot-2.3 - 0.3.3-SNAPSHOT + 0.4.0-SNAPSHOT Demo project for Spring Boot diff --git a/tests/test-spring-boot-2.4/pom.xml b/tests/test-spring-boot-2.4/pom.xml index b38d54f3..c8049f44 100644 --- a/tests/test-spring-boot-2.4/pom.xml +++ b/tests/test-spring-boot-2.4/pom.xml @@ -10,7 +10,7 @@ com.senacor.elasticsearch.evolution test-spring-boot-2.4 - 0.3.3-SNAPSHOT + 0.4.0-SNAPSHOT Demo project for Spring Boot diff --git a/tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml b/tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml index 1fbceea5..7508e723 100644 --- a/tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml +++ b/tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml @@ -10,7 +10,7 @@ com.senacor.elasticsearch.evolution test-spring-boot-2.5-scriptsInJarFile - 0.3.3-SNAPSHOT + 0.4.0-SNAPSHOT Demo project for Spring Boot diff --git a/tests/test-spring-boot-2.6/pom.xml b/tests/test-spring-boot-2.6/pom.xml index 1d0e4c62..d441cb6f 100644 --- a/tests/test-spring-boot-2.6/pom.xml +++ b/tests/test-spring-boot-2.6/pom.xml @@ -10,7 +10,7 @@ com.senacor.elasticsearch.evolution test-spring-boot-2.6 - 0.3.3-SNAPSHOT + 0.4.0-SNAPSHOT Demo project for Spring Boot From 94aa338e200425421eaf9bcc690c1368fce37e96 Mon Sep 17 00:00:00 2001 From: Andreas Keefer Date: Sat, 2 Apr 2022 23:28:52 +0200 Subject: [PATCH 50/54] elasticsearch 8.x compatibility tests --- .github/workflows/maven-matrix.yml | 4 ++-- README.md | 5 +++-- .../execution/HistoryRepositoryImplIT.java | 8 +------- .../test/EmbeddedElasticsearchExtension.java | 19 ++++++++++++++++--- .../evolution/core/test/EsUtils.java | 16 ++++++++++++++++ 5 files changed, 38 insertions(+), 14 deletions(-) diff --git a/.github/workflows/maven-matrix.yml b/.github/workflows/maven-matrix.yml index 346d5ce1..6b93bff8 100644 --- a/.github/workflows/maven-matrix.yml +++ b/.github/workflows/maven-matrix.yml @@ -43,8 +43,8 @@ jobs: build-and-test-with-es-version: strategy: matrix: - elasticsearchVersion: [ "7.17.2", "7.16.3", "7.15.2", "7.14.2", "7.13.4", "7.12.1", "7.11.2", "7.10.2", - "7.9.3", "7.8.1", "7.7.1", "7.6.2", "7.5.2" ] + elasticsearchVersion: [ "8.1.2", "8.0.1", "7.17.2", "7.16.3", "7.15.2", "7.14.2", + "7.13.4", "7.12.1", "7.11.2", "7.10.2", "7.9.3", "7.8.1", "7.7.1", "7.6.2", "7.5.2" ] fail-fast: false runs-on: ubuntu-18.04 steps: diff --git a/README.md b/README.md index de3efddb..6d900fa6 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Successful executed migration scripts will not be executed again! - tested on Java 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 and 18 - runs on Spring-Boot 2.1, 2.2, 2.3, 2.4, 2.5 and 2.6 (and of course without Spring-Boot) -- runs on Elasticsearch version 7.5.x - 7.17.x +- runs on Elasticsearch version 7.5.x - 8.1.x - highly configurable (e.g. location(s) of your migration files, migration files format pattern) - placeholder substitution in migration scripts - easily extendable to your needs @@ -32,6 +32,7 @@ Successful executed migration scripts will not be executed again! | Compatibility | Spring Boot | Elasticsearch | |----------------------------------|------------------------------|----------------------| +| elasticsearch-evolution >= 0.4.0 | 2.1, 2.2, 2.3, 2.4, 2.5, 2.6 | 7.5.x - 8.1.x | | elasticsearch-evolution >= 0.3.0 | 2.1, 2.2, 2.3, 2.4, 2.5, 2.6 | 7.5.x - 7.17.x | | elasticsearch-evolution 0.2.x | 1.5, 2.0, 2.1, 2.2, 2.3, 2.4 | 7.0.x - 7.4.x, 6.8.x | @@ -290,7 +291,7 @@ ElasticsearchEvolution.configure() - version updates (spring-boot 2.6.6) - added spring boot 2.5 and 2.6 compatibility tests - added java 17 and 18 compatibility tests -- added Elasticsearch 7.17, 7.16, 7.15, 7.14 and 7.13 compatibility tests +- added Elasticsearch 8.1, 8.0, 7.17, 7.16, 7.15, 7.14 and 7.13 compatibility tests ### v0.3.2 diff --git a/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/HistoryRepositoryImplIT.java b/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/HistoryRepositoryImplIT.java index 604ac1d4..5c69a255 100644 --- a/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/HistoryRepositoryImplIT.java +++ b/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/HistoryRepositoryImplIT.java @@ -10,7 +10,6 @@ import org.apache.commons.lang3.RandomStringUtils; import org.elasticsearch.action.get.GetRequest; import org.elasticsearch.action.get.GetResponse; -import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.client.RestHighLevelClient; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.extension.ExtendWith; @@ -428,12 +427,7 @@ void indexDoesNotExistsYet_indexWillBeCreated(String esVersion, EsUtils esUtils, private void indexDocumentWithLock(boolean locked, EsUtils esUtils, RestHighLevelClient restHighLevelClient) throws Exception { HashMap source = new HashMap<>(); source.put(LOCKED_FIELD_NAME, locked); - - restHighLevelClient.index( - new IndexRequest(INDEX) - .id(RandomStringUtils.randomNumeric(5)) - .source(source), - DEFAULT); + esUtils.indexDocument(INDEX, RandomStringUtils.randomNumeric(5), source); esUtils.refreshIndices(); logger.debug("all documents in index '{}': {}", INDEX, esUtils.fetchAllDocuments(INDEX)); diff --git a/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/test/EmbeddedElasticsearchExtension.java b/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/test/EmbeddedElasticsearchExtension.java index 73a9b20e..f3ab7fae 100644 --- a/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/test/EmbeddedElasticsearchExtension.java +++ b/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/test/EmbeddedElasticsearchExtension.java @@ -2,7 +2,10 @@ import org.apache.http.HttpHost; import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest; +import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.client.*; +import org.elasticsearch.client.indices.GetIndexRequest; +import org.elasticsearch.client.indices.GetIndexResponse; import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.api.extension.ExtensionContext.Namespace; import org.junit.jupiter.api.extension.TestInstancePostProcessor; @@ -35,6 +38,8 @@ public class EmbeddedElasticsearchExtension implements TestInstancePostProcessor private static final Logger logger = LoggerFactory.getLogger(EmbeddedElasticsearchExtension.class); private static final Namespace NAMESPACE = Namespace.create(ExtensionContext.class); private static final SortedSet SUPPORTED_ES_VERSIONS = Collections.unmodifiableSortedSet(new TreeSet<>(Arrays.asList( + "8.1.2", + "8.0.1", "7.17.2", "7.16.3", "7.15.2", @@ -59,7 +64,7 @@ public void postProcessTestInstance(Object testInstance, ExtensionContext contex private static RestHighLevelClient createRestHighLevelClient(String esVersion, ElasticsearchContainer elasticsearchContainer) { HttpHost host = HttpHost.create(elasticsearchContainer.getHttpHostAddress()); - logger.debug("create RestHighLevelClient for ES {} at {}", esVersion, host); + logger.debug("create RestClient for ES {} at {}", esVersion, host); RestClientBuilder builder = RestClient.builder(host); return new RestHighLevelClient(builder); } @@ -68,7 +73,9 @@ private static ElasticsearchContainer createElasticsearchContainer(String esVers logger.info("creating ElasticsearchContainer {} ...", esVersion); ElasticsearchContainer container = new ElasticsearchContainer(DockerImageName.parse("docker.elastic.co/elasticsearch/elasticsearch") .withTag(esVersion)) - .withEnv("ES_JAVA_OPTS", "-Xms128m -Xmx128m"); + .withEnv("ES_JAVA_OPTS", "-Xms128m -Xmx128m") + // since elasticsearch 8 security / https is enabled per default - but for testing it should be disabled + .withEnv("xpack.security.enabled", "false"); int esHttpPort = SocketUtils.findAvailableTcpPort(5000, 30000); int esTransportPort = SocketUtils.findAvailableTcpPort(30001, 65535); container.setPortBindings(Arrays.asList(esHttpPort + ":9200", esTransportPort + ":9300")); @@ -88,7 +95,13 @@ private static void start(ElasticsearchContainer elasticsearchContainer, String private static void cleanup(EsUtils esUtils, String esVersion, RestHighLevelClient restHighLevelClient) { logger.debug("cleanup ElasticsearchContainer {}", esVersion); try { - restHighLevelClient.indices().delete(new DeleteIndexRequest("*"), DEFAULT); + // get all indices + final GetIndexResponse allIndices = restHighLevelClient.indices().get(new GetIndexRequest("_all") + .indicesOptions(IndicesOptions.lenientExpandOpen()), DEFAULT); + if (allIndices.getIndices().length > 0) { + logger.debug("delete indices {}", Arrays.toString(allIndices.getIndices())); + restHighLevelClient.indices().delete(new DeleteIndexRequest(allIndices.getIndices()), DEFAULT); + } Response deleteRes = restHighLevelClient.getLowLevelClient().performRequest(new Request("DELETE", "/_template/*")); logger.debug("deleted all templates: {}", deleteRes); } catch (IOException e) { diff --git a/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/test/EsUtils.java b/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/test/EsUtils.java index ac108a17..31c20aaf 100644 --- a/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/test/EsUtils.java +++ b/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/test/EsUtils.java @@ -9,6 +9,7 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; +import java.util.HashMap; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -68,5 +69,20 @@ private Stream parseDocuments(String body) { throw new IllegalStateException("parseDocuments failed. body=" + body, e); } } + + public void indexDocument(String index, String id, HashMap source) { + try { + final Request indexRequest = new Request("PUT", "/" + index + "/_doc/" + id); + indexRequest.setJsonEntity(OBJECT_MAPPER.writeValueAsString(source)); + final Response res = restClient.performRequest(indexRequest); + if (res.getStatusLine().getStatusCode() != 201) { + throw new IllegalStateException(String.format("indexDocument failed with status code %s: %s", + res.getStatusLine().getStatusCode(), + res.getStatusLine().getReasonPhrase())); + } + } catch (IOException e) { + throw new IllegalStateException("indexDocument failed", e); + } + } } From 72337f228559b516a048ecf938620744a83599eb Mon Sep 17 00:00:00 2001 From: Andreas Keefer Date: Sat, 2 Apr 2022 23:39:00 +0200 Subject: [PATCH 51/54] run pipeline on push only on master and release branch --- .github/workflows/maven-matrix.yml | 3 +++ .github/workflows/quality.yml | 7 ++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/maven-matrix.yml b/.github/workflows/maven-matrix.yml index 6b93bff8..69ca6998 100644 --- a/.github/workflows/maven-matrix.yml +++ b/.github/workflows/maven-matrix.yml @@ -2,6 +2,9 @@ name: Maven Matrix Build on: push: + branches: + - master + - release # pull_request to run the pipeline on PRs from external, because "push" does not create this pipeline on external PRs pull_request: schedule: diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index 07b3a171..f7638d8e 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -1,6 +1,11 @@ name: Quality analysis -on: [ "push", "pull_request" ] +on: + push: + branches: + - master + - release + pull_request: env: MVN_CMD: "./mvnw --settings .cicd.settings.xml -e -B -V" From aa5bb9752dfc08f2a6a8a1763fbae852bb2ebab5 Mon Sep 17 00:00:00 2001 From: Andreas Keefer Date: Sun, 3 Apr 2022 00:00:02 +0200 Subject: [PATCH 52/54] fixed integration tests --- .../autoconfigure/EmbeddedElasticsearchConfiguration.java | 4 +++- .../evolution/springboot23/ApplicationTests.java | 6 ++++-- .../evolution/springboot22/ApplicationTests.java | 7 ++++--- .../evolution/springboot23/ApplicationTests.java | 6 ++++-- .../evolution/springboot24/ApplicationTests.java | 6 ++++-- .../evolution/springboot25/ApplicationTest.java | 6 ++++-- .../evolution/springboot24/ApplicationTests.java | 6 ++++-- 7 files changed, 27 insertions(+), 14 deletions(-) diff --git a/spring-boot-starter-elasticsearch-evolution/src/test/java/com/senacor/elasticsearch/evolution/spring/boot/starter/autoconfigure/EmbeddedElasticsearchConfiguration.java b/spring-boot-starter-elasticsearch-evolution/src/test/java/com/senacor/elasticsearch/evolution/spring/boot/starter/autoconfigure/EmbeddedElasticsearchConfiguration.java index fb2621d8..d38dd6d3 100644 --- a/spring-boot-starter-elasticsearch-evolution/src/test/java/com/senacor/elasticsearch/evolution/spring/boot/starter/autoconfigure/EmbeddedElasticsearchConfiguration.java +++ b/spring-boot-starter-elasticsearch-evolution/src/test/java/com/senacor/elasticsearch/evolution/spring/boot/starter/autoconfigure/EmbeddedElasticsearchConfiguration.java @@ -17,7 +17,9 @@ public class EmbeddedElasticsearchConfiguration { @Bean(destroyMethod = "stop") public ElasticsearchContainer elasticsearchContainer() { ElasticsearchContainer container = new ElasticsearchContainer("docker.elastic.co/elasticsearch/elasticsearch:7.5.2") - .withEnv("ES_JAVA_OPTS", "-Xms128m -Xmx128m"); + .withEnv("ES_JAVA_OPTS", "-Xms128m -Xmx128m") + // since elasticsearch 8 security / https is enabled per default - but for testing it should be disabled + .withEnv("xpack.security.enabled", "false"); logger.info("starting embedded ElasticSearch..."); container.start(); return container; diff --git a/tests/test-spring-boot-2.1-scriptsInJarFile/src/test/java/com/senacor/elasticsearch/evolution/springboot23/ApplicationTests.java b/tests/test-spring-boot-2.1-scriptsInJarFile/src/test/java/com/senacor/elasticsearch/evolution/springboot23/ApplicationTests.java index ebdf0d75..d04a22e7 100644 --- a/tests/test-spring-boot-2.1-scriptsInJarFile/src/test/java/com/senacor/elasticsearch/evolution/springboot23/ApplicationTests.java +++ b/tests/test-spring-boot-2.1-scriptsInJarFile/src/test/java/com/senacor/elasticsearch/evolution/springboot23/ApplicationTests.java @@ -42,8 +42,10 @@ static class Config { public ElasticsearchContainer elasticsearchContainer(@Value("${elasticsearch.version:7.5.2}") String esVersion) { ElasticsearchContainer container = new ElasticsearchContainer(DockerImageName .parse("docker.elastic.co/elasticsearch/elasticsearch") - .withTag(esVersion) - ).withEnv("ES_JAVA_OPTS", "-Xms128m -Xmx128m"); + .withTag(esVersion)) + .withEnv("ES_JAVA_OPTS", "-Xms128m -Xmx128m") + // since elasticsearch 8 security / https is enabled per default - but for testing it should be disabled + .withEnv("xpack.security.enabled", "false"); container.setPortBindings(Collections.singletonList(ELASTICSEARCH_PORT + ":9200")); container.start(); return container; diff --git a/tests/test-spring-boot-2.2/src/test/java/com/senacor/elasticsearch/evolution/springboot22/ApplicationTests.java b/tests/test-spring-boot-2.2/src/test/java/com/senacor/elasticsearch/evolution/springboot22/ApplicationTests.java index 035e96aa..d8ca6dec 100644 --- a/tests/test-spring-boot-2.2/src/test/java/com/senacor/elasticsearch/evolution/springboot22/ApplicationTests.java +++ b/tests/test-spring-boot-2.2/src/test/java/com/senacor/elasticsearch/evolution/springboot22/ApplicationTests.java @@ -39,9 +39,10 @@ static class Config { public ElasticsearchContainer elasticsearchContainer(@Value("${elasticsearch.version:7.5.2}") String esVersion) { ElasticsearchContainer container = new ElasticsearchContainer(DockerImageName .parse("docker.elastic.co/elasticsearch/elasticsearch") - .withTag(esVersion) - ) - .withEnv("ES_JAVA_OPTS", "-Xms128m -Xmx128m"); + .withTag(esVersion)) + .withEnv("ES_JAVA_OPTS", "-Xms128m -Xmx128m") + // since elasticsearch 8 security / https is enabled per default - but for testing it should be disabled + .withEnv("xpack.security.enabled", "false"); container.setPortBindings(Collections.singletonList(ELASTICSEARCH_PORT + ":9200")); container.start(); return container; diff --git a/tests/test-spring-boot-2.3/src/test/java/com/senacor/elasticsearch/evolution/springboot23/ApplicationTests.java b/tests/test-spring-boot-2.3/src/test/java/com/senacor/elasticsearch/evolution/springboot23/ApplicationTests.java index 7d3cadc5..b5a0a557 100644 --- a/tests/test-spring-boot-2.3/src/test/java/com/senacor/elasticsearch/evolution/springboot23/ApplicationTests.java +++ b/tests/test-spring-boot-2.3/src/test/java/com/senacor/elasticsearch/evolution/springboot23/ApplicationTests.java @@ -39,8 +39,10 @@ static class Config { public ElasticsearchContainer elasticsearchContainer(@Value("${elasticsearch.version:7.5.2}") String esVersion) { ElasticsearchContainer container = new ElasticsearchContainer(DockerImageName .parse("docker.elastic.co/elasticsearch/elasticsearch") - .withTag(esVersion) - ).withEnv("ES_JAVA_OPTS", "-Xms128m -Xmx128m"); + .withTag(esVersion)) + .withEnv("ES_JAVA_OPTS", "-Xms128m -Xmx128m") + // since elasticsearch 8 security / https is enabled per default - but for testing it should be disabled + .withEnv("xpack.security.enabled", "false"); container.setPortBindings(Collections.singletonList(ELASTICSEARCH_PORT + ":9200")); container.start(); return container; diff --git a/tests/test-spring-boot-2.4/src/test/java/com/senacor/elasticsearch/evolution/springboot24/ApplicationTests.java b/tests/test-spring-boot-2.4/src/test/java/com/senacor/elasticsearch/evolution/springboot24/ApplicationTests.java index ebad3ed4..a0e5e59c 100644 --- a/tests/test-spring-boot-2.4/src/test/java/com/senacor/elasticsearch/evolution/springboot24/ApplicationTests.java +++ b/tests/test-spring-boot-2.4/src/test/java/com/senacor/elasticsearch/evolution/springboot24/ApplicationTests.java @@ -39,8 +39,10 @@ static class Config { public ElasticsearchContainer elasticsearchContainer(@Value("${elasticsearch.version:7.5.2}") String esVersion) { ElasticsearchContainer container = new ElasticsearchContainer(DockerImageName .parse("docker.elastic.co/elasticsearch/elasticsearch") - .withTag(esVersion) - ).withEnv("ES_JAVA_OPTS", "-Xms128m -Xmx128m"); + .withTag(esVersion)) + .withEnv("ES_JAVA_OPTS", "-Xms128m -Xmx128m") + // since elasticsearch 8 security / https is enabled per default - but for testing it should be disabled + .withEnv("xpack.security.enabled", "false"); container.setPortBindings(Collections.singletonList(ELASTICSEARCH_PORT + ":9200")); container.start(); return container; diff --git a/tests/test-spring-boot-2.5-scriptsInJarFile/src/test/java/com/senacor/elasticsearch/evolution/springboot25/ApplicationTest.java b/tests/test-spring-boot-2.5-scriptsInJarFile/src/test/java/com/senacor/elasticsearch/evolution/springboot25/ApplicationTest.java index 72115d31..6352ee93 100644 --- a/tests/test-spring-boot-2.5-scriptsInJarFile/src/test/java/com/senacor/elasticsearch/evolution/springboot25/ApplicationTest.java +++ b/tests/test-spring-boot-2.5-scriptsInJarFile/src/test/java/com/senacor/elasticsearch/evolution/springboot25/ApplicationTest.java @@ -39,8 +39,10 @@ static class Config { public ElasticsearchContainer elasticsearchContainer(@Value("${elasticsearch.version:7.5.2}") String esVersion) { ElasticsearchContainer container = new ElasticsearchContainer(DockerImageName .parse("docker.elastic.co/elasticsearch/elasticsearch") - .withTag(esVersion) - ).withEnv("ES_JAVA_OPTS", "-Xms128m -Xmx128m"); + .withTag(esVersion)) + .withEnv("ES_JAVA_OPTS", "-Xms128m -Xmx128m") + // since elasticsearch 8 security / https is enabled per default - but for testing it should be disabled + .withEnv("xpack.security.enabled", "false"); container.setPortBindings(Collections.singletonList(ELASTICSEARCH_PORT + ":9200")); container.start(); return container; diff --git a/tests/test-spring-boot-2.6/src/test/java/com/senacor/elasticsearch/evolution/springboot24/ApplicationTests.java b/tests/test-spring-boot-2.6/src/test/java/com/senacor/elasticsearch/evolution/springboot24/ApplicationTests.java index a913baad..cc5f8b5a 100644 --- a/tests/test-spring-boot-2.6/src/test/java/com/senacor/elasticsearch/evolution/springboot24/ApplicationTests.java +++ b/tests/test-spring-boot-2.6/src/test/java/com/senacor/elasticsearch/evolution/springboot24/ApplicationTests.java @@ -39,8 +39,10 @@ static class Config { public ElasticsearchContainer elasticsearchContainer(@Value("${elasticsearch.version:7.5.2}") String esVersion) { ElasticsearchContainer container = new ElasticsearchContainer(DockerImageName .parse("docker.elastic.co/elasticsearch/elasticsearch") - .withTag(esVersion) - ).withEnv("ES_JAVA_OPTS", "-Xms128m -Xmx128m"); + .withTag(esVersion)) + .withEnv("ES_JAVA_OPTS", "-Xms128m -Xmx128m") + // since elasticsearch 8 security / https is enabled per default - but for testing it should be disabled + .withEnv("xpack.security.enabled", "false"); container.setPortBindings(Collections.singletonList(ELASTICSEARCH_PORT + ":9200")); container.start(); return container; From b121fa8b1c5e51edb8fca0afde831df1bc3f5678 Mon Sep 17 00:00:00 2001 From: Andreas Keefer Date: Sun, 3 Apr 2022 12:13:34 +0200 Subject: [PATCH 53/54] add compatibility tests for opensearch 1.x --- README.md | 45 +++-- .../core/ElasticsearchEvolutionIT.java | 8 +- .../execution/HistoryRepositoryImplIT.java | 84 ++++---- .../execution/MigrationServiceImplIT.java | 4 +- .../test/EmbeddedElasticsearchExtension.java | 182 +++++++++++------- 5 files changed, 187 insertions(+), 136 deletions(-) diff --git a/README.md b/README.md index 6d900fa6..1f8fceb0 100644 --- a/README.md +++ b/README.md @@ -15,26 +15,27 @@ ## 1 Evolve your Elasticsearch mapping easily and reliable across all your instances -Elasticsearch-Evolution executes versioned migration scripts reliable and persists the execution state in an internal Elasticsearch index. +Elasticsearch-Evolution executes versioned migration scripts reliable and persists the execution state in an internal Elasticsearch/Opensearch index. Successful executed migration scripts will not be executed again! ## 2 Features -- tested on Java 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 and 18 -- runs on Spring-Boot 2.1, 2.2, 2.3, 2.4, 2.5 and 2.6 (and of course without Spring-Boot) -- runs on Elasticsearch version 7.5.x - 8.1.x -- highly configurable (e.g. location(s) of your migration files, migration files format pattern) -- placeholder substitution in migration scripts -- easily extendable to your needs -- supports microservices / multiple parallel running instances via logical database locks -- ready to use default configuration -- line comments in migration files - -| Compatibility | Spring Boot | Elasticsearch | -|----------------------------------|------------------------------|----------------------| -| elasticsearch-evolution >= 0.4.0 | 2.1, 2.2, 2.3, 2.4, 2.5, 2.6 | 7.5.x - 8.1.x | -| elasticsearch-evolution >= 0.3.0 | 2.1, 2.2, 2.3, 2.4, 2.5, 2.6 | 7.5.x - 7.17.x | -| elasticsearch-evolution 0.2.x | 1.5, 2.0, 2.1, 2.2, 2.3, 2.4 | 7.0.x - 7.4.x, 6.8.x | +- tested on Java 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 and 18 +- runs on Spring-Boot 2.1, 2.2, 2.3, 2.4, 2.5 and 2.6 (and of course without Spring-Boot) +- runs on Elasticsearch version 7.5.x - 8.1.x +- runs on Opensearch version 1.x +- highly configurable (e.g. location(s) of your migration files, migration files format pattern) +- placeholder substitution in migration scripts +- easily extendable to your needs +- supports microservices / multiple parallel running instances via logical database locks +- ready to use default configuration +- line comments in migration files + +| Compatibility | Spring Boot | Elasticsearch | Opensearch | +|----------------------------------|------------------------------|----------------------|------------| +| elasticsearch-evolution >= 0.4.0 | 2.1, 2.2, 2.3, 2.4, 2.5, 2.6 | 7.5.x - 8.1.x | 1.x | +| elasticsearch-evolution >= 0.3.0 | 2.1, 2.2, 2.3, 2.4, 2.5, 2.6 | 7.5.x - 7.17.x | | +| elasticsearch-evolution 0.2.x | 1.5, 2.0, 2.1, 2.2, 2.3, 2.4 | 7.0.x - 7.4.x, 6.8.x | | NOTE: When you run on Java 11 and using spring-boot 2.2 or 2.3 and you hit [this issue](https://github.com/ronmamo/reflections/issues/279), you have 2 options: @@ -65,7 +66,7 @@ Elasticsearch-Evolution uses internally Elastics `RestClient` and requires at mi Place your migration scripts in your application classpath at `es/evolution` -That's it. Elasticsearch-Evolution runs at application startup and expects your Elasticsearch at +That's it. Elasticsearch-Evolution runs at application startup and expects your Elasticsearch/Opensearch at ### 3.2 Quickstart with core library @@ -127,7 +128,7 @@ Content-Type: application/json } ``` -The first line defines the HTTP method `PUT` and the relative path to the Elasticsearch endpoint `_template/my_template` to create a new mapping template. +The first line defines the HTTP method `PUT` and the relative path to the Elasticsearch/Opensearch endpoint `_template/my_template` to create a new mapping template. Followed by a HTTP Header `Content-Type: application/json`. After a blank line the HTTP body is defined. @@ -135,15 +136,15 @@ The pattern is strongly oriented in ordinary HTTP requests and consist of 4 part 1. **The HTTP method (required)**. Supported HTTP methods are `GET`, `HEAD`, `POST`, `PUT`, `DELETE`, `OPTIONS` and `PATCH`. The First non-comment line must always start with a HTTP method. -2. **The path to the Elasticsearch endpoint to call (required)**. The path is separated by a _blank_ from the HTTP method. +2. **The path to the Elasticsearch/Opensearch endpoint to call (required)**. The path is separated by a _blank_ from the HTTP method. You can provide any query parameters like in a ordinary browser like this `/my_index_1/_doc/1?refresh=true&op_type=create` 3. **HTTP Header(s) (optional)**. All non-comment lines after the _HTTP method_ line will be interpreted as HTTP headers. Header name and content are separated by `:`. -4. **HTTP Body (optional)**. The HTTP Body is separated by a blank line and can contain any content you want to sent to Elasticsearch. +4. **HTTP Body (optional)**. The HTTP Body is separated by a blank line and can contain any content you want to sent to Elasticsearch/Opensearch. #### 4.1.1 Comments Elasticsearch-Evolution supports line-comments in its migration scripts. Every line starting with `#` or `//` will be interpreted as a comment-line. -Comment-lines are not send to Elasticsearch, they will be filtered by Elasticsearch-Evolution. +Comment-lines are not send to Elasticsearch/Opensearch, they will be filtered by Elasticsearch-Evolution. #### 4.1.2 Placeholders @@ -292,6 +293,8 @@ ElasticsearchEvolution.configure() - added spring boot 2.5 and 2.6 compatibility tests - added java 17 and 18 compatibility tests - added Elasticsearch 8.1, 8.0, 7.17, 7.16, 7.15, 7.14 and 7.13 compatibility tests +- added Opensearch 1.0, 1.1, 1.2 and 1.3 compatibility tests +- fixed issue [#114](https://github.com/senacor/elasticsearch-evolution/issues/114) ### v0.3.2 diff --git a/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/ElasticsearchEvolutionIT.java b/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/ElasticsearchEvolutionIT.java index f1c457b2..4c6c4627 100644 --- a/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/ElasticsearchEvolutionIT.java +++ b/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/ElasticsearchEvolutionIT.java @@ -48,9 +48,9 @@ class ElasticsearchEvolutionIT { private final ObjectMapper objectMapper = new ObjectMapper() .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - @ParameterizedTest(name = "esVersion: {0}") + @ParameterizedTest(name = "{0}") @ArgumentsSource(ElasticsearchArgumentsProvider.class) - void migrate_OK(String esVersion, EsUtils esUtils, RestHighLevelClient restHighLevelClient) throws IOException { + void migrate_OK(String versionInfo, EsUtils esUtils, RestHighLevelClient restHighLevelClient) throws IOException { ElasticsearchEvolutionConfig elasticsearchEvolutionConfig = ElasticsearchEvolution.configure() .setLocations(singletonList("classpath:es/ElasticsearchEvolutionTest/migrate_OK")); @@ -120,9 +120,9 @@ void migrate_OK(String esVersion, EsUtils esUtils, RestHighLevelClient restHighL .isEqualTo(3); } - @ParameterizedTest(name = "esVersion: {0}") + @ParameterizedTest(name = "{0}") @ArgumentsSource(ElasticsearchArgumentsProvider.class) - void migrate_failed_then_fixed_script_and_re_execute(String esVersion, EsUtils esUtils, RestHighLevelClient restHighLevelClient) { + void migrate_failed_then_fixed_script_and_re_execute(String versionInfo, EsUtils esUtils, RestHighLevelClient restHighLevelClient) { ElasticsearchEvolutionConfig elasticsearchEvolutionConfig = ElasticsearchEvolution.configure() .setLocations(singletonList("classpath:es/ElasticsearchEvolutionTest/migrate_failed_step1")); diff --git a/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/HistoryRepositoryImplIT.java b/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/HistoryRepositoryImplIT.java index 5c69a255..cff6c62f 100644 --- a/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/HistoryRepositoryImplIT.java +++ b/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/HistoryRepositoryImplIT.java @@ -42,9 +42,9 @@ class HistoryRepositoryImplIT { @Nested class findAll { - @ParameterizedTest(name = "esVersion: {0}") + @ParameterizedTest(name = "{0}") @ArgumentsSource(ElasticsearchArgumentsProvider.class) - void can_handle_empty_search_result(String esVersion, EsUtils esUtils, RestHighLevelClient restHighLevelClient) { + void can_handle_empty_search_result(String versionInfo, EsUtils esUtils, RestHighLevelClient restHighLevelClient) { HistoryRepositoryImpl underTest = createHistoryRepositoryImpl(restHighLevelClient); underTest.refresh(INDEX); @@ -53,9 +53,9 @@ void can_handle_empty_search_result(String esVersion, EsUtils esUtils, RestHighL assertThat(all).isEmpty(); } - @ParameterizedTest(name = "esVersion: {0}") + @ParameterizedTest(name = "{0}") @ArgumentsSource(ElasticsearchArgumentsProvider.class) - void can_handle_when_index_does_not_exist(String esVersion, EsUtils esUtils, RestHighLevelClient restHighLevelClient) { + void can_handle_when_index_does_not_exist(String versionInfo, EsUtils esUtils, RestHighLevelClient restHighLevelClient) { HistoryRepositoryImpl underTest = createHistoryRepositoryImpl(restHighLevelClient); NavigableSet all = underTest.findAll(); @@ -63,9 +63,9 @@ void can_handle_when_index_does_not_exist(String esVersion, EsUtils esUtils, Res assertThat(all).isEmpty(); } - @ParameterizedTest(name = "esVersion: {0}") + @ParameterizedTest(name = "{0}") @ArgumentsSource(ElasticsearchArgumentsProvider.class) - void doesNotReturnProtocolsWithMajorVersion0(String esVersion, EsUtils esUtils, RestHighLevelClient restHighLevelClient) { + void doesNotReturnProtocolsWithMajorVersion0(String versionInfo, EsUtils esUtils, RestHighLevelClient restHighLevelClient) { HistoryRepositoryImpl underTest = createHistoryRepositoryImpl(restHighLevelClient); underTest.saveOrUpdate(new MigrationScriptProtocol().setVersion("0.1")); underTest.saveOrUpdate(new MigrationScriptProtocol().setVersion("1.0")); @@ -77,9 +77,9 @@ void doesNotReturnProtocolsWithMajorVersion0(String esVersion, EsUtils esUtils, assertThat(all.first().getVersion()).isEqualTo(MigrationVersion.fromVersion("1.0")); } - @ParameterizedTest(name = "esVersion: {0}") + @ParameterizedTest(name = "{0}") @ArgumentsSource(ElasticsearchArgumentsProvider.class) - void returnsProtocolsInVersionOrder(String esVersion, EsUtils esUtils, RestHighLevelClient restHighLevelClient) { + void returnsProtocolsInVersionOrder(String versionInfo, EsUtils esUtils, RestHighLevelClient restHighLevelClient) { HistoryRepositoryImpl underTest = createHistoryRepositoryImpl(restHighLevelClient); underTest.saveOrUpdate(new MigrationScriptProtocol().setVersion("1.1")); underTest.saveOrUpdate(new MigrationScriptProtocol().setVersion("2.0")); @@ -110,9 +110,9 @@ void returnsProtocolsInVersionOrder(String esVersion, EsUtils esUtils, RestHighL assertThat(all.last().getVersion()).isEqualTo(MigrationVersion.fromVersion("2.0")); } - @ParameterizedTest(name = "esVersion: {0}") + @ParameterizedTest(name = "{0}") @ArgumentsSource(ElasticsearchArgumentsProvider.class) - void returnsFullProtocol(String esVersion, EsUtils esUtils, RestHighLevelClient restHighLevelClient) { + void returnsFullProtocol(String versionInfo, EsUtils esUtils, RestHighLevelClient restHighLevelClient) { HistoryRepositoryImpl underTest = createHistoryRepositoryImpl(restHighLevelClient); OffsetDateTime executionTimestamp = OffsetDateTime.of(2019, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC); MigrationScriptProtocol protocol = new MigrationScriptProtocol() @@ -147,9 +147,9 @@ void returnsFullProtocol(String esVersion, EsUtils esUtils, RestHighLevelClient @Nested class saveOrUpdate { - @ParameterizedTest(name = "esVersion: {0}") + @ParameterizedTest(name = "{0}") @ArgumentsSource(ElasticsearchArgumentsProvider.class) - void saveFullDocument(String esVersion, EsUtils esUtils, RestHighLevelClient restHighLevelClient) throws IOException { + void saveFullDocument(String versionInfo, EsUtils esUtils, RestHighLevelClient restHighLevelClient) throws IOException { HistoryRepositoryImpl underTest = createHistoryRepositoryImpl(restHighLevelClient); OffsetDateTime now = OffsetDateTime.now(); @@ -194,9 +194,9 @@ void saveFullDocument(String esVersion, EsUtils esUtils, RestHighLevelClient res .containsEntry(SUCCESS_FIELD_NAME, true); } - @ParameterizedTest(name = "esVersion: {0}") + @ParameterizedTest(name = "{0}") @ArgumentsSource(ElasticsearchArgumentsProvider.class) - void save2DocumentsWithDifferentVersions(String esVersion, EsUtils esUtils, RestHighLevelClient restHighLevelClient) throws IOException { + void save2DocumentsWithDifferentVersions(String versionInfo, EsUtils esUtils, RestHighLevelClient restHighLevelClient) throws IOException { HistoryRepositoryImpl underTest = createHistoryRepositoryImpl(restHighLevelClient); underTest.saveOrUpdate(new MigrationScriptProtocol() @@ -209,9 +209,9 @@ void save2DocumentsWithDifferentVersions(String esVersion, EsUtils esUtils, Rest assertThat(res).hasSize(2); } - @ParameterizedTest(name = "esVersion: {0}") + @ParameterizedTest(name = "{0}") @ArgumentsSource(ElasticsearchArgumentsProvider.class) - void updateDocument(String esVersion, EsUtils esUtils, RestHighLevelClient restHighLevelClient) throws IOException { + void updateDocument(String versionInfo, EsUtils esUtils, RestHighLevelClient restHighLevelClient) throws IOException { HistoryRepositoryImpl underTest = createHistoryRepositoryImpl(restHighLevelClient); underTest.saveOrUpdate(new MigrationScriptProtocol() @@ -235,9 +235,9 @@ void updateDocument(String esVersion, EsUtils esUtils, RestHighLevelClient restH @Nested class isLocked { - @ParameterizedTest(name = "esVersion: {0}") + @ParameterizedTest(name = "{0}") @ArgumentsSource(ElasticsearchArgumentsProvider.class) - void emptyIndex_IsNotLocked(String esVersion, EsUtils esUtils, RestHighLevelClient restHighLevelClient) { + void emptyIndex_IsNotLocked(String versionInfo, EsUtils esUtils, RestHighLevelClient restHighLevelClient) { HistoryRepositoryImpl underTest = createHistoryRepositoryImpl(restHighLevelClient); underTest.createIndexIfAbsent(); @@ -247,9 +247,9 @@ void emptyIndex_IsNotLocked(String esVersion, EsUtils esUtils, RestHighLevelClie .isFalse(); } - @ParameterizedTest(name = "esVersion: {0}") + @ParameterizedTest(name = "{0}") @ArgumentsSource(ElasticsearchArgumentsProvider.class) - void noIndex_IsNotLocked(String esVersion, EsUtils esUtils, RestHighLevelClient restHighLevelClient) { + void noIndex_IsNotLocked(String versionInfo, EsUtils esUtils, RestHighLevelClient restHighLevelClient) { HistoryRepositoryImpl underTest = createHistoryRepositoryImpl(restHighLevelClient); esUtils.refreshIndices(); @@ -258,9 +258,9 @@ void noIndex_IsNotLocked(String esVersion, EsUtils esUtils, RestHighLevelClient .isFalse(); } - @ParameterizedTest(name = "esVersion: {0}") + @ParameterizedTest(name = "{0}") @ArgumentsSource(ElasticsearchArgumentsProvider.class) - void existingDocuments_IsNotLocked(String esVersion, EsUtils esUtils, RestHighLevelClient restHighLevelClient) throws Exception { + void existingDocuments_IsNotLocked(String versionInfo, EsUtils esUtils, RestHighLevelClient restHighLevelClient) throws Exception { HistoryRepositoryImpl underTest = createHistoryRepositoryImpl(restHighLevelClient); underTest.createIndexIfAbsent(); indexDocumentWithLock(false, esUtils, restHighLevelClient); @@ -271,9 +271,9 @@ void existingDocuments_IsNotLocked(String esVersion, EsUtils esUtils, RestHighLe .isFalse(); } - @ParameterizedTest(name = "esVersion: {0}") + @ParameterizedTest(name = "{0}") @ArgumentsSource(ElasticsearchArgumentsProvider.class) - void existingDocuments_IsLocked(String esVersion, EsUtils esUtils, RestHighLevelClient restHighLevelClient) throws Exception { + void existingDocuments_IsLocked(String versionInfo, EsUtils esUtils, RestHighLevelClient restHighLevelClient) throws Exception { HistoryRepositoryImpl underTest = createHistoryRepositoryImpl(restHighLevelClient); underTest.createIndexIfAbsent(); indexDocumentWithLock(true, esUtils, restHighLevelClient); @@ -288,9 +288,9 @@ void existingDocuments_IsLocked(String esVersion, EsUtils esUtils, RestHighLevel @Nested class lock { - @ParameterizedTest(name = "esVersion: {0}") + @ParameterizedTest(name = "{0}") @ArgumentsSource(ElasticsearchArgumentsProvider.class) - void noDocumentsInIndex(String esVersion, EsUtils esUtils, RestHighLevelClient restHighLevelClient) { + void noDocumentsInIndex(String versionInfo, EsUtils esUtils, RestHighLevelClient restHighLevelClient) { HistoryRepositoryImpl underTest = createHistoryRepositoryImpl(restHighLevelClient); underTest.createIndexIfAbsent(); esUtils.refreshIndices(); @@ -301,9 +301,9 @@ void noDocumentsInIndex(String esVersion, EsUtils esUtils, RestHighLevelClient r assertThat(underTest.isLocked()).isTrue(); } - @ParameterizedTest(name = "esVersion: {0}") + @ParameterizedTest(name = "{0}") @ArgumentsSource(ElasticsearchArgumentsProvider.class) - void indexDoesNotExist(String esVersion, EsUtils esUtils, RestHighLevelClient restHighLevelClient) { + void indexDoesNotExist(String versionInfo, EsUtils esUtils, RestHighLevelClient restHighLevelClient) { HistoryRepositoryImpl underTest = createHistoryRepositoryImpl(restHighLevelClient); assertThat(underTest.lock()).isTrue(); @@ -312,9 +312,9 @@ void indexDoesNotExist(String esVersion, EsUtils esUtils, RestHighLevelClient re assertThat(underTest.isLocked()).isTrue(); } - @ParameterizedTest(name = "esVersion: {0}") + @ParameterizedTest(name = "{0}") @ArgumentsSource(ElasticsearchArgumentsProvider.class) - void allExistingLockedDocumentsStayLocked(String esVersion, EsUtils esUtils, RestHighLevelClient restHighLevelClient) throws Exception { + void allExistingLockedDocumentsStayLocked(String versionInfo, EsUtils esUtils, RestHighLevelClient restHighLevelClient) throws Exception { HistoryRepositoryImpl underTest = createHistoryRepositoryImpl(restHighLevelClient); indexDocumentWithLock(true, esUtils, restHighLevelClient); @@ -329,9 +329,9 @@ void allExistingLockedDocumentsStayLocked(String esVersion, EsUtils esUtils, Res assertThat(underTest.isLocked()).isTrue(); } - @ParameterizedTest(name = "esVersion: {0}") + @ParameterizedTest(name = "{0}") @ArgumentsSource(ElasticsearchArgumentsProvider.class) - void allExistingUnlockedDocumentsGetsLocked(String esVersion, EsUtils esUtils, RestHighLevelClient restHighLevelClient) throws Exception { + void allExistingUnlockedDocumentsGetsLocked(String versionInfo, EsUtils esUtils, RestHighLevelClient restHighLevelClient) throws Exception { HistoryRepositoryImpl underTest = createHistoryRepositoryImpl(restHighLevelClient); indexDocumentWithLock(false, esUtils, restHighLevelClient); @@ -349,9 +349,9 @@ void allExistingUnlockedDocumentsGetsLocked(String esVersion, EsUtils esUtils, R @Nested class unlock { - @ParameterizedTest(name = "esVersion: {0}") + @ParameterizedTest(name = "{0}") @ArgumentsSource(ElasticsearchArgumentsProvider.class) - void noDocumentsInIndex(String esVersion, EsUtils esUtils, RestHighLevelClient restHighLevelClient) { + void noDocumentsInIndex(String versionInfo, EsUtils esUtils, RestHighLevelClient restHighLevelClient) { HistoryRepositoryImpl underTest = createHistoryRepositoryImpl(restHighLevelClient); underTest.createIndexIfAbsent(); esUtils.refreshIndices(); @@ -362,9 +362,9 @@ void noDocumentsInIndex(String esVersion, EsUtils esUtils, RestHighLevelClient r assertThat(underTest.isLocked()).isFalse(); } - @ParameterizedTest(name = "esVersion: {0}") + @ParameterizedTest(name = "{0}") @ArgumentsSource(ElasticsearchArgumentsProvider.class) - void indexDoesNotExist(String esVersion, EsUtils esUtils, RestHighLevelClient restHighLevelClient) { + void indexDoesNotExist(String versionInfo, EsUtils esUtils, RestHighLevelClient restHighLevelClient) { HistoryRepositoryImpl underTest = createHistoryRepositoryImpl(restHighLevelClient); assertThat(underTest.unlock()).isTrue(); @@ -373,9 +373,9 @@ void indexDoesNotExist(String esVersion, EsUtils esUtils, RestHighLevelClient re assertThat(underTest.isLocked()).isFalse(); } - @ParameterizedTest(name = "esVersion: {0}") + @ParameterizedTest(name = "{0}") @ArgumentsSource(ElasticsearchArgumentsProvider.class) - void allExistingLockedDocumentsGetsUnlocked(String esVersion, EsUtils esUtils, RestHighLevelClient restHighLevelClient) throws Exception { + void allExistingLockedDocumentsGetsUnlocked(String versionInfo, EsUtils esUtils, RestHighLevelClient restHighLevelClient) throws Exception { HistoryRepositoryImpl underTest = createHistoryRepositoryImpl(restHighLevelClient); underTest.lock(); indexDocumentWithLock(true, esUtils, restHighLevelClient); @@ -391,9 +391,9 @@ void allExistingLockedDocumentsGetsUnlocked(String esVersion, EsUtils esUtils, R assertThat(underTest.isLocked()).isFalse(); } - @ParameterizedTest(name = "esVersion: {0}") + @ParameterizedTest(name = "{0}") @ArgumentsSource(ElasticsearchArgumentsProvider.class) - void allExistingNonLockedDocumentsStayUnlocked(String esVersion, EsUtils esUtils, RestHighLevelClient restHighLevelClient) throws Exception { + void allExistingNonLockedDocumentsStayUnlocked(String versionInfo, EsUtils esUtils, RestHighLevelClient restHighLevelClient) throws Exception { HistoryRepositoryImpl underTest = createHistoryRepositoryImpl(restHighLevelClient); indexDocumentWithLock(false, esUtils, restHighLevelClient); esUtils.refreshIndices(); @@ -410,9 +410,9 @@ void allExistingNonLockedDocumentsStayUnlocked(String esVersion, EsUtils esUtils @Nested class createIndexIfAbsent { - @ParameterizedTest(name = "esVersion: {0}") + @ParameterizedTest(name = "{0}") @ArgumentsSource(ElasticsearchArgumentsProvider.class) - void indexDoesNotExistsYet_indexWillBeCreated(String esVersion, EsUtils esUtils, RestHighLevelClient restHighLevelClient) { + void indexDoesNotExistsYet_indexWillBeCreated(String versionInfo, EsUtils esUtils, RestHighLevelClient restHighLevelClient) { HistoryRepositoryImpl underTest = createHistoryRepositoryImpl(restHighLevelClient); assertThat(underTest.createIndexIfAbsent()).as("new index created").isTrue(); diff --git a/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/MigrationServiceImplIT.java b/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/MigrationServiceImplIT.java index 75330cc0..fa37ba49 100644 --- a/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/MigrationServiceImplIT.java +++ b/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/internal/migration/execution/MigrationServiceImplIT.java @@ -49,9 +49,9 @@ class MigrationServiceImplIT { @Nested class executeScript { - @ParameterizedTest(name = "esVersion: {0}") + @ParameterizedTest(name = "{0}") @ArgumentsSource(ElasticsearchArgumentsProvider.class) - void OK_indexDocumentIsWrittenToElasticsearch(String esVersion, EsUtils esUtils, RestHighLevelClient restHighLevelClient) throws IOException { + void OK_indexDocumentIsWrittenToElasticsearch(String versionInfo, EsUtils esUtils, RestHighLevelClient restHighLevelClient) throws IOException { String index = "myindex"; ParsedMigrationScript script = createParsedMigrationScript("1.1", index); diff --git a/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/test/EmbeddedElasticsearchExtension.java b/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/test/EmbeddedElasticsearchExtension.java index f3ab7fae..bf38a722 100644 --- a/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/test/EmbeddedElasticsearchExtension.java +++ b/elasticsearch-evolution-core/src/test/java/com/senacor/elasticsearch/evolution/core/test/EmbeddedElasticsearchExtension.java @@ -1,11 +1,15 @@ package com.senacor.elasticsearch.evolution.core.test; +import lombok.Builder; +import lombok.NonNull; +import lombok.Value; import org.apache.http.HttpHost; import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest; import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.client.*; import org.elasticsearch.client.indices.GetIndexRequest; import org.elasticsearch.client.indices.GetIndexResponse; +import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.api.extension.ExtensionContext.Namespace; import org.junit.jupiter.api.extension.TestInstancePostProcessor; @@ -15,16 +19,15 @@ import org.slf4j.LoggerFactory; import org.springframework.util.SocketUtils; import org.testcontainers.elasticsearch.ElasticsearchContainer; +import org.testcontainers.shaded.com.google.common.collect.ImmutableMap; import org.testcontainers.utility.DockerImageName; import java.io.IOException; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; import java.util.*; import java.util.stream.Stream; +import static com.senacor.elasticsearch.evolution.core.test.EmbeddedElasticsearchExtension.SearchContainer.ofElasticsearch; +import static com.senacor.elasticsearch.evolution.core.test.EmbeddedElasticsearchExtension.SearchContainer.ofOpensearch; import static org.elasticsearch.client.RequestOptions.DEFAULT; /** @@ -37,63 +40,67 @@ public class EmbeddedElasticsearchExtension implements TestInstancePostProcessor private static final Logger logger = LoggerFactory.getLogger(EmbeddedElasticsearchExtension.class); private static final Namespace NAMESPACE = Namespace.create(ExtensionContext.class); - private static final SortedSet SUPPORTED_ES_VERSIONS = Collections.unmodifiableSortedSet(new TreeSet<>(Arrays.asList( - "8.1.2", - "8.0.1", - "7.17.2", - "7.16.3", - "7.15.2", - "7.14.2", - "7.13.4", - "7.12.1", - "7.11.2", - "7.10.2", - "7.9.3", - "7.8.1", - "7.7.1", - "7.6.2", - "7.5.2" + private static final SortedSet SUPPORTED_SEARCH_VERSIONS = Collections.unmodifiableSortedSet(new TreeSet<>(Arrays.asList( + ofOpensearch("1.3.1"), + ofOpensearch("1.2.4"), + ofOpensearch("1.1.0"), + ofOpensearch("1.0.1"), + + ofElasticsearch("8.1.2"), + ofElasticsearch("8.0.1"), + ofElasticsearch("7.17.2"), + ofElasticsearch("7.16.3"), + ofElasticsearch("7.15.2"), + ofElasticsearch("7.14.2"), + ofElasticsearch("7.13.4"), + ofElasticsearch("7.12.1"), + ofElasticsearch("7.11.2"), + ofElasticsearch("7.10.2"), + ofElasticsearch("7.9.3"), + ofElasticsearch("7.8.1"), + ofElasticsearch("7.7.1"), + ofElasticsearch("7.6.2"), + ofElasticsearch("7.5.2") ))); @Override public void postProcessTestInstance(Object testInstance, ExtensionContext context) { - SUPPORTED_ES_VERSIONS.parallelStream() - .forEach(esVersion -> getStore(context) - .getOrComputeIfAbsent(esVersion, EmbeddedElasticsearchExtension::createElasticsearchContainer, ElasticsearchContainer.class)); + SUPPORTED_SEARCH_VERSIONS.parallelStream() + .forEach(searchContainer -> getStore(context) + .getOrComputeIfAbsent(searchContainer, EmbeddedElasticsearchExtension::createElasticsearchContainer, ElasticsearchContainer.class)); } - private static RestHighLevelClient createRestHighLevelClient(String esVersion, ElasticsearchContainer elasticsearchContainer) { + private static RestHighLevelClient createRestHighLevelClient(String versionInfo, ElasticsearchContainer elasticsearchContainer) { HttpHost host = HttpHost.create(elasticsearchContainer.getHttpHostAddress()); - logger.debug("create RestClient for ES {} at {}", esVersion, host); + logger.debug("create RestClient for {} at {}", versionInfo, host); RestClientBuilder builder = RestClient.builder(host); return new RestHighLevelClient(builder); } - private static ElasticsearchContainer createElasticsearchContainer(String esVersion) { - logger.info("creating ElasticsearchContainer {} ...", esVersion); - ElasticsearchContainer container = new ElasticsearchContainer(DockerImageName.parse("docker.elastic.co/elasticsearch/elasticsearch") - .withTag(esVersion)) - .withEnv("ES_JAVA_OPTS", "-Xms128m -Xmx128m") - // since elasticsearch 8 security / https is enabled per default - but for testing it should be disabled - .withEnv("xpack.security.enabled", "false"); - int esHttpPort = SocketUtils.findAvailableTcpPort(5000, 30000); - int esTransportPort = SocketUtils.findAvailableTcpPort(30001, 65535); - container.setPortBindings(Arrays.asList(esHttpPort + ":9200", esTransportPort + ":9300")); - start(container, esVersion); + private static ElasticsearchContainer createElasticsearchContainer(SearchContainer searchContainer) { + logger.info("creating ElasticsearchContainer for {} ...", searchContainer.getInfo()); + ElasticsearchContainer container = new ElasticsearchContainer(DockerImageName.parse(searchContainer.getContainerImage()) + .asCompatibleSubstituteFor("docker.elastic.co/elasticsearch/elasticsearch") + .withTag(searchContainer.getVersion())) + .withEnv(searchContainer.getEnv()); + int httpPort = SocketUtils.findAvailableTcpPort(5000, 30000); + int transportPort = SocketUtils.findAvailableTcpPort(30001, 65535); + container.setPortBindings(Arrays.asList(httpPort + ":9200", transportPort + ":" + searchContainer.transportPort)); + start(container, searchContainer.getInfo()); logger.info("ElasticsearchContainer {} started with HttpPort={} and TransportTcpPort={}!", - esVersion, - esHttpPort, - esTransportPort); + searchContainer.getInfo(), + httpPort, + transportPort); return container; } - private static void start(ElasticsearchContainer elasticsearchContainer, String esVersion) { - logger.debug("starting ElasticsearchContainer {}", esVersion); + private static void start(ElasticsearchContainer elasticsearchContainer, String versionInfo) { + logger.debug("starting ElasticsearchContainer {}", versionInfo); elasticsearchContainer.start(); } - private static void cleanup(EsUtils esUtils, String esVersion, RestHighLevelClient restHighLevelClient) { - logger.debug("cleanup ElasticsearchContainer {}", esVersion); + private static void cleanup(EsUtils esUtils, String versionInfo, RestHighLevelClient restHighLevelClient) { + logger.debug("cleanup ElasticsearchContainer {}", versionInfo); try { // get all indices final GetIndexResponse allIndices = restHighLevelClient.indices().get(new GetIndexRequest("_all") @@ -116,42 +123,83 @@ private static ExtensionContext.Store getStore(ExtensionContext context) { /** * provides in this order: - * - Elasticsearch Version + * - Short Version Info * - EsUtils * - RestHighLevelClient */ public static class ElasticsearchArgumentsProvider implements ArgumentsProvider { @Override public Stream provideArguments(ExtensionContext context) { - Optional versionFilterPattern = context.getTestMethod() - .map(method -> method.getDeclaredAnnotation(IgnoreEsVersion.class)) - .map(IgnoreEsVersion::value); - - - return SUPPORTED_ES_VERSIONS.stream() - .filter(version -> versionFilterPattern - .map(filterPattern -> !version.matches(filterPattern)) - .orElse(true) - ) - .map(esVersion -> { + return SUPPORTED_SEARCH_VERSIONS.stream() + .map(searchContainer -> { ElasticsearchContainer elasticsearchContainer = getStore(context) - .getOrComputeIfAbsent(esVersion, EmbeddedElasticsearchExtension::createElasticsearchContainer, ElasticsearchContainer.class); - start(elasticsearchContainer, esVersion); - RestHighLevelClient restHighLevelClient = createRestHighLevelClient(esVersion, elasticsearchContainer); + .getOrComputeIfAbsent(searchContainer, EmbeddedElasticsearchExtension::createElasticsearchContainer, ElasticsearchContainer.class); + start(elasticsearchContainer, searchContainer.getInfo()); + RestHighLevelClient restHighLevelClient = createRestHighLevelClient(searchContainer.getInfo(), elasticsearchContainer); EsUtils esUtils = new EsUtils(restHighLevelClient.getLowLevelClient()); - cleanup(esUtils, esVersion, restHighLevelClient); - return Arguments.of(esVersion, esUtils, restHighLevelClient); + cleanup(esUtils, searchContainer.getInfo(), restHighLevelClient); + return Arguments.of(searchContainer.getShortInfo(), esUtils, restHighLevelClient); }); } } - @Target(ElementType.METHOD) - @Retention(RetentionPolicy.RUNTIME) - public @interface IgnoreEsVersion { + @Value + @Builder + public static class SearchContainer implements Comparable { + @NonNull + String vendor; + @NonNull + String vendorShort; + @NonNull + String version; + @NonNull + String containerImage; + @NonNull + Map env; + int transportPort; + + public static SearchContainer ofElasticsearch(String version) { + return SearchContainer.builder() + .vendor("Elasticsearch") + .vendorShort("ES") + .containerImage("docker.elastic.co/elasticsearch/elasticsearch") + .version(version) + .env(ImmutableMap.of( + "ES_JAVA_OPTS", "-Xms128m -Xmx128m", + // since elasticsearch 8 security / https is enabled per default - but for testing it should be disabled + "xpack.security.enabled", "false" + )) + .transportPort(9300) + .build(); + } + + public static SearchContainer ofOpensearch(String version) { + return SearchContainer.builder() + .vendor("Opensearch") + .vendorShort("OS") + .containerImage("opensearchproject/opensearch") + .version(version) + .env(ImmutableMap.of( + "OPENSEARCH_JAVA_OPTS", "-Xms128m -Xmx128m", + // disable security / https for testing + "plugins.security.disabled", "true", + "DISABLE_INSTALL_DEMO_CONFIG", "true" + )) + .transportPort(9600) + .build(); + } - /** - * The version pattern (regex) to ignore - */ - String value(); + public String getInfo() { + return vendor + " " + version; + } + + public String getShortInfo() { + return vendorShort + " " + version; + } + + @Override + public int compareTo(@NotNull SearchContainer other) { + return version.compareTo(other.version); + } } } From 4c869d07fab618f0e5ac11ae104645a6a84e2d15 Mon Sep 17 00:00:00 2001 From: Andreas Keefer Date: Sun, 3 Apr 2022 13:54:25 +0200 Subject: [PATCH 54/54] prepare 0.4.0 release --- elasticsearch-evolution-core/pom.xml | 2 +- pom.xml | 2 +- spring-boot-starter-elasticsearch-evolution/pom.xml | 2 +- tests/migration-scripts/pom.xml | 2 +- tests/pom.xml | 2 +- tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml | 2 +- tests/test-spring-boot-2.2/pom.xml | 2 +- tests/test-spring-boot-2.3/pom.xml | 2 +- tests/test-spring-boot-2.4/pom.xml | 2 +- tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml | 2 +- tests/test-spring-boot-2.6/pom.xml | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/elasticsearch-evolution-core/pom.xml b/elasticsearch-evolution-core/pom.xml index 9fdeb6e4..3d32865b 100644 --- a/elasticsearch-evolution-core/pom.xml +++ b/elasticsearch-evolution-core/pom.xml @@ -6,7 +6,7 @@ com.senacor.elasticsearch.evolution elasticsearch-evolution-parent - 0.4.0-SNAPSHOT + 0.4.0 ../ elasticsearch-evolution-core diff --git a/pom.xml b/pom.xml index f29fdbd8..96f6886a 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.senacor.elasticsearch.evolution elasticsearch-evolution-parent - 0.4.0-SNAPSHOT + 0.4.0 pom org.springframework.boot diff --git a/spring-boot-starter-elasticsearch-evolution/pom.xml b/spring-boot-starter-elasticsearch-evolution/pom.xml index 2758e2a5..cd4fd13b 100644 --- a/spring-boot-starter-elasticsearch-evolution/pom.xml +++ b/spring-boot-starter-elasticsearch-evolution/pom.xml @@ -6,7 +6,7 @@ com.senacor.elasticsearch.evolution elasticsearch-evolution-parent - 0.4.0-SNAPSHOT + 0.4.0 ../ spring-boot-starter-elasticsearch-evolution diff --git a/tests/migration-scripts/pom.xml b/tests/migration-scripts/pom.xml index e4971c67..f85402b3 100644 --- a/tests/migration-scripts/pom.xml +++ b/tests/migration-scripts/pom.xml @@ -4,7 +4,7 @@ 4.0.0 com.senacor.elasticsearch.evolution migration-scripts - 0.4.0-SNAPSHOT + 0.4.0 jar containing migration files jar diff --git a/tests/pom.xml b/tests/pom.xml index 10cb47fd..2e7fd876 100644 --- a/tests/pom.xml +++ b/tests/pom.xml @@ -6,7 +6,7 @@ com.senacor.elasticsearch.evolution elasticsearch-evolution-parent - 0.4.0-SNAPSHOT + 0.4.0 ../ tests diff --git a/tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml b/tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml index 3798846b..568c3057 100644 --- a/tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml +++ b/tests/test-spring-boot-2.1-scriptsInJarFile/pom.xml @@ -10,7 +10,7 @@ com.senacor.elasticsearch.evolution test-spring-boot-2.1-scriptsInJarFile - 0.4.0-SNAPSHOT + 0.4.0 Demo project for Spring Boot diff --git a/tests/test-spring-boot-2.2/pom.xml b/tests/test-spring-boot-2.2/pom.xml index 240c4a2c..39a2ff19 100644 --- a/tests/test-spring-boot-2.2/pom.xml +++ b/tests/test-spring-boot-2.2/pom.xml @@ -10,7 +10,7 @@ com.senacor.elasticsearch.evolution test-spring-boot-2.2 - 0.4.0-SNAPSHOT + 0.4.0 Demo project for Spring Boot diff --git a/tests/test-spring-boot-2.3/pom.xml b/tests/test-spring-boot-2.3/pom.xml index ac2b9a20..dd16fe6c 100644 --- a/tests/test-spring-boot-2.3/pom.xml +++ b/tests/test-spring-boot-2.3/pom.xml @@ -10,7 +10,7 @@ com.senacor.elasticsearch.evolution test-spring-boot-2.3 - 0.4.0-SNAPSHOT + 0.4.0 Demo project for Spring Boot diff --git a/tests/test-spring-boot-2.4/pom.xml b/tests/test-spring-boot-2.4/pom.xml index c8049f44..15a336a5 100644 --- a/tests/test-spring-boot-2.4/pom.xml +++ b/tests/test-spring-boot-2.4/pom.xml @@ -10,7 +10,7 @@ com.senacor.elasticsearch.evolution test-spring-boot-2.4 - 0.4.0-SNAPSHOT + 0.4.0 Demo project for Spring Boot diff --git a/tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml b/tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml index 7508e723..15e0f2c0 100644 --- a/tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml +++ b/tests/test-spring-boot-2.5-scriptsInJarFile/pom.xml @@ -10,7 +10,7 @@ com.senacor.elasticsearch.evolution test-spring-boot-2.5-scriptsInJarFile - 0.4.0-SNAPSHOT + 0.4.0 Demo project for Spring Boot diff --git a/tests/test-spring-boot-2.6/pom.xml b/tests/test-spring-boot-2.6/pom.xml index d441cb6f..0225149e 100644 --- a/tests/test-spring-boot-2.6/pom.xml +++ b/tests/test-spring-boot-2.6/pom.xml @@ -10,7 +10,7 @@ com.senacor.elasticsearch.evolution test-spring-boot-2.6 - 0.4.0-SNAPSHOT + 0.4.0 Demo project for Spring Boot