From 847aecf62170a1c7825b27506bae9076d1902e80 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Mon, 5 Oct 2020 14:51:50 +0100 Subject: [PATCH] Upgrade to Flyway 7.0.0 Closes gh-23514 --- .../flyway/FlywayMigrationInitializer.java | 10 +- .../flyway/FlywayProperties.java | 19 ++-- .../Flyway6xAutoConfigurationTests.java | 102 ++++++++++++++++++ .../flyway/FlywayAutoConfigurationTests.java | 28 +++-- .../flyway/FlywayPropertiesTests.java | 7 +- .../spring-boot-dependencies/build.gradle | 2 +- 6 files changed, 138 insertions(+), 30 deletions(-) create mode 100644 spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/Flyway6xAutoConfigurationTests.java diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayMigrationInitializer.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayMigrationInitializer.java index cca907e419..6f864abb5e 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayMigrationInitializer.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayMigrationInitializer.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -62,7 +62,13 @@ public class FlywayMigrationInitializer implements InitializingBean, Ordered { this.migrationStrategy.migrate(this.flyway); } else { - this.flyway.migrate(); + try { + this.flyway.migrate(); + } + catch (NoSuchMethodError ex) { + // Flyway < 7.0 + this.flyway.getClass().getMethod("migrate").invoke(this.flyway); + } } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayProperties.java index 81e3d15bb8..badf48a3ca 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayProperties.java @@ -248,48 +248,45 @@ public class FlywayProperties { private boolean validateOnMigrate = true; /** - * Whether to batch SQL statements when executing them. Requires Flyway Pro or Flyway - * Enterprise. + * Whether to batch SQL statements when executing them. Requires Flyway Teams. */ private Boolean batch; /** * File to which the SQL statements of a migration dry run should be output. Requires - * Flyway Pro or Flyway Enterprise. + * Flyway Teams. */ private File dryRunOutput; /** * Rules for the built-in error handling to override specific SQL states and error - * codes. Requires Flyway Pro or Flyway Enterprise. + * codes. Requires Flyway Teams. */ private String[] errorOverrides; /** - * Licence key for Flyway Pro or Flyway Enterprise. + * Licence key for Flyway Teams. */ private String licenseKey; /** - * Whether to enable support for Oracle SQL*Plus commands. Requires Flyway Pro or - * Flyway Enterprise. + * Whether to enable support for Oracle SQL*Plus commands. Requires Flyway Teams. */ private Boolean oracleSqlplus; /** * Whether to issue a warning rather than an error when a not-yet-supported Oracle - * SQL*Plus statement is encountered. Requires Flyway Pro or Flyway Enterprise. + * SQL*Plus statement is encountered. Requires Flyway Teams. */ private Boolean oracleSqlplusWarn; /** - * Whether to stream SQL migrations when executing them. Requires Flyway Pro or Flyway - * Enterprise. + * Whether to stream SQL migrations when executing them. Requires Flyway Teams. */ private Boolean stream; /** - * File name prefix for undo SQL migrations. Requires Flyway Pro or Flyway Enterprise. + * File name prefix for undo SQL migrations. Requires Flyway Teams. */ private String undoSqlMigrationPrefix; diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/Flyway6xAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/Flyway6xAutoConfigurationTests.java new file mode 100644 index 0000000000..fd169165fd --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/Flyway6xAutoConfigurationTests.java @@ -0,0 +1,102 @@ +/* + * Copyright 2012-2020 the original author or authors. + * + * Licensed 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 + * + * https://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. + */ + +package org.springframework.boot.autoconfigure.flyway; + +import org.flywaydb.core.Flyway; +import org.flywaydb.core.api.Location; +import org.flywaydb.core.api.callback.Callback; +import org.flywaydb.core.api.callback.Context; +import org.flywaydb.core.api.callback.Event; +import org.junit.jupiter.api.Test; +import org.mockito.InOrder; + +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.boot.testsupport.classpath.ClassPathOverrides; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.Order; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; + +/** + * Tests for {@link FlywayAutoConfiguration} with Flyway 6.x. + * + * @author Andy Wilkinson + */ +@ClassPathOverrides("org.flywaydb:flyway-core:6.5.6") +class Flyway6xAutoConfigurationTests { + + private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(FlywayAutoConfiguration.class)) + .withPropertyValues("spring.datasource.generate-unique-name=true"); + + @Test + void defaultFlyway() { + this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class).run((context) -> { + assertThat(context).hasSingleBean(Flyway.class); + Flyway flyway = context.getBean(Flyway.class); + assertThat(flyway.getConfiguration().getLocations()) + .containsExactly(new Location("classpath:db/migration")); + }); + } + + @Test + void callbacksAreConfiguredAndOrdered() { + this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class, CallbackConfiguration.class) + .run((context) -> { + assertThat(context).hasSingleBean(Flyway.class); + Flyway flyway = context.getBean(Flyway.class); + Callback callbackOne = context.getBean("callbackOne", Callback.class); + Callback callbackTwo = context.getBean("callbackTwo", Callback.class); + assertThat(flyway.getConfiguration().getCallbacks()).hasSize(2); + assertThat(flyway.getConfiguration().getCallbacks()).containsExactly(callbackTwo, callbackOne); + InOrder orderedCallbacks = inOrder(callbackOne, callbackTwo); + orderedCallbacks.verify(callbackTwo).handle(any(Event.class), any(Context.class)); + orderedCallbacks.verify(callbackOne).handle(any(Event.class), any(Context.class)); + }); + } + + @Configuration(proxyBeanMethods = false) + static class CallbackConfiguration { + + @Bean + @Order(1) + Callback callbackOne() { + return mockCallback(); + } + + @Bean + @Order(0) + Callback callbackTwo() { + return mockCallback(); + } + + private Callback mockCallback() { + Callback callback = mock(Callback.class); + given(callback.supports(any(Event.class), any(Context.class))).willReturn(true); + return callback; + } + + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfigurationTests.java index b234c3fc99..f260e71bed 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfigurationTests.java @@ -30,7 +30,7 @@ import org.flywaydb.core.api.callback.Callback; import org.flywaydb.core.api.callback.Context; import org.flywaydb.core.api.callback.Event; import org.flywaydb.core.api.migration.JavaMigration; -import org.flywaydb.core.internal.license.FlywayProUpgradeRequiredException; +import org.flywaydb.core.internal.license.FlywayTeamsUpgradeRequiredException; import org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -380,7 +380,7 @@ class FlywayAutoConfigurationTests { } @Test - void callbacksAreConfiguredAndOrdered() { + void callbacksAreConfiguredAndOrderedByName() { this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class, CallbackConfiguration.class) .run((context) -> { assertThat(context).hasSingleBean(Flyway.class); @@ -388,7 +388,6 @@ class FlywayAutoConfigurationTests { Callback callbackOne = context.getBean("callbackOne", Callback.class); Callback callbackTwo = context.getBean("callbackTwo", Callback.class); assertThat(flyway.getConfiguration().getCallbacks()).hasSize(2); - assertThat(flyway.getConfiguration().getCallbacks()).containsExactly(callbackTwo, callbackOne); InOrder orderedCallbacks = inOrder(callbackOne, callbackTwo); orderedCallbacks.verify(callbackTwo).handle(any(Event.class), any(Context.class)); orderedCallbacks.verify(callbackOne).handle(any(Event.class), any(Context.class)); @@ -413,7 +412,7 @@ class FlywayAutoConfigurationTests { .withPropertyValues("spring.flyway.batch=true").run((context) -> { assertThat(context).hasFailed(); Throwable failure = context.getStartupFailure(); - assertThat(failure).hasRootCauseInstanceOf(FlywayProUpgradeRequiredException.class); + assertThat(failure).hasRootCauseInstanceOf(FlywayTeamsUpgradeRequiredException.class); assertThat(failure).hasMessageContaining(" batch "); }); } @@ -424,7 +423,7 @@ class FlywayAutoConfigurationTests { .withPropertyValues("spring.flyway.dryRunOutput=dryrun.sql").run((context) -> { assertThat(context).hasFailed(); Throwable failure = context.getStartupFailure(); - assertThat(failure).hasRootCauseInstanceOf(FlywayProUpgradeRequiredException.class); + assertThat(failure).hasRootCauseInstanceOf(FlywayTeamsUpgradeRequiredException.class); assertThat(failure).hasMessageContaining(" dryRunOutput "); }); } @@ -435,7 +434,7 @@ class FlywayAutoConfigurationTests { .withPropertyValues("spring.flyway.errorOverrides=D12345").run((context) -> { assertThat(context).hasFailed(); Throwable failure = context.getStartupFailure(); - assertThat(failure).hasRootCauseInstanceOf(FlywayProUpgradeRequiredException.class); + assertThat(failure).hasRootCauseInstanceOf(FlywayTeamsUpgradeRequiredException.class); assertThat(failure).hasMessageContaining(" errorOverrides "); }); } @@ -453,7 +452,7 @@ class FlywayAutoConfigurationTests { .withPropertyValues("spring.flyway.oracle-sqlplus=true").run((context) -> { assertThat(context).hasFailed(); Throwable failure = context.getStartupFailure(); - assertThat(failure).hasRootCauseInstanceOf(FlywayProUpgradeRequiredException.class); + assertThat(failure).hasRootCauseInstanceOf(FlywayTeamsUpgradeRequiredException.class); assertThat(failure).hasMessageContaining(" oracle.sqlplus "); }); } @@ -464,7 +463,7 @@ class FlywayAutoConfigurationTests { .withPropertyValues("spring.flyway.oracle-sqlplus-warn=true").run((context) -> { assertThat(context).hasFailed(); Throwable failure = context.getStartupFailure(); - assertThat(failure).hasRootCauseInstanceOf(FlywayProUpgradeRequiredException.class); + assertThat(failure).hasRootCauseInstanceOf(FlywayTeamsUpgradeRequiredException.class); assertThat(failure).hasMessageContaining(" oracle.sqlplusWarn "); }); } @@ -475,7 +474,7 @@ class FlywayAutoConfigurationTests { .withPropertyValues("spring.flyway.stream=true").run((context) -> { assertThat(context).hasFailed(); Throwable failure = context.getStartupFailure(); - assertThat(failure).hasRootCauseInstanceOf(FlywayProUpgradeRequiredException.class); + assertThat(failure).hasRootCauseInstanceOf(FlywayTeamsUpgradeRequiredException.class); assertThat(failure).hasMessageContaining(" stream "); }); } @@ -486,7 +485,7 @@ class FlywayAutoConfigurationTests { .withPropertyValues("spring.flyway.undo-sql-migration-prefix=undo").run((context) -> { assertThat(context).hasFailed(); Throwable failure = context.getStartupFailure(); - assertThat(failure).hasRootCauseInstanceOf(FlywayProUpgradeRequiredException.class); + assertThat(failure).hasRootCauseInstanceOf(FlywayTeamsUpgradeRequiredException.class); assertThat(failure).hasMessageContaining(" undoSqlMigrationPrefix "); }); } @@ -712,20 +711,19 @@ class FlywayAutoConfigurationTests { static class CallbackConfiguration { @Bean - @Order(1) Callback callbackOne() { - return mockCallback(); + return mockCallback("b"); } @Bean - @Order(0) Callback callbackTwo() { - return mockCallback(); + return mockCallback("a"); } - private Callback mockCallback() { + private Callback mockCallback(String name) { Callback callback = mock(Callback.class); given(callback.supports(any(Event.class), any(Context.class))).willReturn(true); + given(callback.getCallbackName()).willReturn(name); return callback; } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/FlywayPropertiesTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/FlywayPropertiesTests.java index f48e107785..0ce9a32203 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/FlywayPropertiesTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/FlywayPropertiesTests.java @@ -113,11 +113,16 @@ class FlywayPropertiesTests { ignoreProperties(configuration, "dryRunOutputAsFile", "dryRunOutputAsFileName"); // Handled as createSchemas ignoreProperties(configuration, "shouldCreateSchemas"); + // Getters for the DataSource settings rather than actual properties + ignoreProperties(configuration, "password", "url", "user"); + // Flyway 7.0 properties that are not yet supported + ignoreProperties(configuration, "cherryPick", "jdbcProperties", "oracleKerberosCacheFile", + "oracleKerberosConfigFile", "skipExecutingMigrations"); List configurationKeys = new ArrayList<>(configuration.keySet()); Collections.sort(configurationKeys); List propertiesKeys = new ArrayList<>(properties.keySet()); Collections.sort(propertiesKeys); - assertThat(configurationKeys).isEqualTo(propertiesKeys); + assertThat(configurationKeys).containsExactlyElementsOf(propertiesKeys); } private void ignoreProperties(Map index, String... propertyNames) { diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index 8378391244..3575ef8c1a 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -304,7 +304,7 @@ bom { ] } } - library("Flyway", "6.5.6") { + library("Flyway", "7.0.0") { group("org.flywaydb") { modules = [ "flyway-core"