Upgrade to Flyway 9.20.1

Closes gh-36364

Co-authored-by: Andy Wilkinson <wilkinsona@vmware.com>
This commit is contained in:
Stephane Nicoll
2023-07-13 18:40:02 +02:00
parent d205d10519
commit fb640c04e7
6 changed files with 131 additions and 27 deletions

View File

@@ -104,6 +104,7 @@ dependencies {
exclude group: "commons-logging", module: "commons-logging"
}
optional("org.flywaydb:flyway-core")
optional("org.flywaydb:flyway-database-oracle")
optional("org.flywaydb:flyway-sqlserver")
optional("org.freemarker:freemarker")
optional("org.glassfish.jersey.containers:jersey-container-servlet-core")

View File

@@ -24,6 +24,9 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Supplier;
import javax.sql.DataSource;
@@ -32,6 +35,8 @@ import org.flywaydb.core.api.MigrationVersion;
import org.flywaydb.core.api.callback.Callback;
import org.flywaydb.core.api.configuration.FluentConfiguration;
import org.flywaydb.core.api.migration.JavaMigration;
import org.flywaydb.core.extensibility.ConfigurationExtension;
import org.flywaydb.database.oracle.OracleConfigurationExtension;
import org.flywaydb.database.sqlserver.SQLServerConfigurationExtension;
import org.springframework.aot.hint.RuntimeHints;
@@ -61,6 +66,8 @@ import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportRuntimeHints;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.GenericConverter;
import org.springframework.core.io.ResourceLoader;
@@ -71,6 +78,7 @@ import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.util.function.SingletonSupplier;
/**
* {@link EnableAutoConfiguration Auto-configuration} for Flyway database migrations.
@@ -116,6 +124,12 @@ public class FlywayAutoConfiguration {
@EnableConfigurationProperties(FlywayProperties.class)
public static class FlywayConfiguration {
private final FlywayProperties properties;
FlywayConfiguration(FlywayProperties properties) {
this.properties = properties;
}
@Bean
ResourceProviderCustomizer resourceProviderCustomizer() {
return new ResourceProviderCustomizer();
@@ -123,21 +137,26 @@ public class FlywayAutoConfiguration {
@Bean
@ConditionalOnMissingBean(FlywayConnectionDetails.class)
PropertiesFlywayConnectionDetails flywayConnectionDetails(FlywayProperties properties) {
return new PropertiesFlywayConnectionDetails(properties);
PropertiesFlywayConnectionDetails flywayConnectionDetails() {
return new PropertiesFlywayConnectionDetails(this.properties);
}
@Bean
Flyway flyway(FlywayProperties properties, FlywayConnectionDetails connectionDetails,
ResourceLoader resourceLoader, ObjectProvider<DataSource> dataSource,
@FlywayDataSource ObjectProvider<DataSource> flywayDataSource,
@ConditionalOnClass(name = "org.flywaydb.database.oracle.OracleConfigurationExtension")
OracleFlywayConfigurationCustomizer oracleFlywayConfigurationCustomizer() {
return new OracleFlywayConfigurationCustomizer(this.properties);
}
@Bean
Flyway flyway(FlywayConnectionDetails connectionDetails, ResourceLoader resourceLoader,
ObjectProvider<DataSource> dataSource, @FlywayDataSource ObjectProvider<DataSource> flywayDataSource,
ObjectProvider<FlywayConfigurationCustomizer> fluentConfigurationCustomizers,
ObjectProvider<JavaMigration> javaMigrations, ObjectProvider<Callback> callbacks,
ResourceProviderCustomizer resourceProviderCustomizer) {
FluentConfiguration configuration = new FluentConfiguration(resourceLoader.getClassLoader());
configureDataSource(configuration, flywayDataSource.getIfAvailable(), dataSource.getIfUnique(),
connectionDetails);
configureProperties(configuration, properties);
configureProperties(configuration, this.properties);
configureCallbacks(configuration, callbacks.orderedStream().toList());
configureJavaMigrations(configuration, javaMigrations.orderedStream().toList());
fluentConfigurationCustomizers.orderedStream().forEach((customizer) -> customizer.customize(configuration));
@@ -242,12 +261,6 @@ public class FlywayAutoConfiguration {
map.from(properties.getDryRunOutput()).to(configuration::dryRunOutput);
map.from(properties.getErrorOverrides()).to(configuration::errorOverrides);
map.from(properties.getLicenseKey()).to(configuration::licenseKey);
// No method references for Oracle props for compatibility with Flyway 9.20+
map.from(properties.getOracleSqlplus()).to((oracleSqlplus) -> configuration.oracleSqlplus(oracleSqlplus));
map.from(properties.getOracleSqlplusWarn())
.to((oracleSqlplusWarn) -> configuration.oracleSqlplusWarn(oracleSqlplusWarn));
map.from(properties.getOracleKerberosCacheFile())
.to((oracleKerberosCacheFile) -> configuration.oracleKerberosCacheFile(oracleKerberosCacheFile));
map.from(properties.getStream()).to(configuration::stream);
map.from(properties.getUndoSqlMigrationPrefix()).to(configuration::undoSqlMigrationPrefix);
map.from(properties.getCherryPick()).to(configuration::cherryPick);
@@ -445,4 +458,54 @@ public class FlywayAutoConfiguration {
}
@Order(Ordered.HIGHEST_PRECEDENCE)
static final class OracleFlywayConfigurationCustomizer implements FlywayConfigurationCustomizer {
private final FlywayProperties properties;
OracleFlywayConfigurationCustomizer(FlywayProperties properties) {
this.properties = properties;
}
@Override
public void customize(FluentConfiguration configuration) {
ConfigurationExtensionMapper<OracleConfigurationExtension> map = new ConfigurationExtensionMapper<>(
PropertyMapper.get().alwaysApplyingWhenNonNull(), () -> {
OracleConfigurationExtension extension = configuration.getPluginRegister()
.getPlugin(OracleConfigurationExtension.class);
Assert.notNull(extension, "Flyway Oracle extension missing");
return extension;
});
map.apply(this.properties.getOracleSqlplus(), OracleConfigurationExtension::setSqlplus);
map.apply(this.properties.getOracleSqlplusWarn(), OracleConfigurationExtension::setSqlplusWarn);
map.apply(this.properties.getOracleWalletLocation(), OracleConfigurationExtension::setWalletLocation);
map.apply(this.properties.getOracleKerberosCacheFile(), OracleConfigurationExtension::setKerberosCacheFile);
}
}
static class ConfigurationExtensionMapper<T extends ConfigurationExtension> {
private final PropertyMapper map;
private final Supplier<T> extensionProvider;
ConfigurationExtensionMapper(PropertyMapper map, Supplier<T> extensionProvider) {
this.map = map;
this.extensionProvider = SingletonSupplier.of(extensionProvider);
}
<V> void apply(V value, BiConsumer<T, V> mapper) {
this.map.from(value).to(withExtension(mapper));
}
private <V> Consumer<V> withExtension(BiConsumer<T, V> mapper) {
return (value) -> {
T extension = this.extensionProvider.get();
mapper.accept(extension, value);
};
}
}
}

View File

@@ -23,18 +23,19 @@ import org.junit.jupiter.api.Test;
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.ClassPathExclusions;
import org.springframework.boot.testsupport.classpath.ClassPathOverrides;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link FlywayAutoConfiguration} with Flyway 9.20.
* Tests for {@link FlywayAutoConfiguration} with Flyway 9.19.
*
* @author Andy Wilkinson
*/
@ClassPathOverrides({ "org.flywaydb:flyway-core:9.20.0", "org.flywaydb:flyway-sqlserver:9.20.0",
"com.h2database:h2:2.1.210" })
class Flyway920AutoConfigurationTests {
@ClassPathExclusions("flyway-*.jar")
@ClassPathOverrides("org.flywaydb:flyway-core:9.19.4")
class Flyway91AutoConfigurationTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(FlywayAutoConfiguration.class))

View File

@@ -31,8 +31,10 @@ import org.flywaydb.core.api.MigrationVersion;
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.configuration.FluentConfiguration;
import org.flywaydb.core.api.migration.JavaMigration;
import org.flywaydb.core.internal.license.FlywayTeamsUpgradeRequiredException;
import org.flywaydb.database.oracle.OracleConfigurationExtension;
import org.flywaydb.database.sqlserver.SQLServerConfigurationExtension;
import org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform;
import org.jooq.DSLContext;
@@ -49,6 +51,7 @@ import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration.FlywayAutoConfigurationRuntimeHints;
import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration.OracleFlywayConfigurationCustomizer;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration;
import org.springframework.boot.autoconfigure.jdbc.JdbcConnectionDetails;
@@ -84,6 +87,7 @@ import org.springframework.stereotype.Component;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.then;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
@@ -602,18 +606,56 @@ class FlywayAutoConfigurationTests {
+ "Enterprise features, download Flyway Teams Edition & Flyway Enterprise Edition"));
}
@Test
void oracleExtensionIsNotLoadedByDefault() {
FluentConfiguration configuration = mock(FluentConfiguration.class);
new OracleFlywayConfigurationCustomizer(new FlywayProperties()).customize(configuration);
then(configuration).shouldHaveNoInteractions();
}
@Test
void oracleSqlplusIsCorrectlyMapped() {
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
.withPropertyValues("spring.flyway.oracle-sqlplus=true")
.run(validateFlywayTeamsPropertyOnly("oracle.sqlplus"));
.run((context) -> assertThat(context.getBean(Flyway.class)
.getConfiguration()
.getPluginRegister()
.getPlugin(OracleConfigurationExtension.class)
.getSqlplus()).isTrue());
}
@Test
void oracleSqlplusWarnIsCorrectlyMapped() {
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
.withPropertyValues("spring.flyway.oracle-sqlplus-warn=true")
.run(validateFlywayTeamsPropertyOnly("oracle.sqlplusWarn"));
.run((context) -> assertThat(context.getBean(Flyway.class)
.getConfiguration()
.getPluginRegister()
.getPlugin(OracleConfigurationExtension.class)
.getSqlplusWarn()).isTrue());
}
@Test
void oracleWallerLocationIsCorrectlyMapped() {
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
.withPropertyValues("spring.flyway.oracle-wallet-location=/tmp/my.wallet")
.run((context) -> assertThat(context.getBean(Flyway.class)
.getConfiguration()
.getPluginRegister()
.getPlugin(OracleConfigurationExtension.class)
.getWalletLocation()).isEqualTo("/tmp/my.wallet"));
}
@Test
void oracleKerberosCacheFileIsCorrectlyMapped() {
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
.withPropertyValues("spring.flyway.oracle-kerberos-cache-file=/tmp/cache")
.run((context) -> assertThat(context.getBean(Flyway.class)
.getConfiguration()
.getPluginRegister()
.getPlugin(OracleConfigurationExtension.class)
.getKerberosCacheFile()).isEqualTo("/tmp/cache"));
}
@Test
@@ -683,13 +725,6 @@ class FlywayAutoConfigurationTests {
.run(validateFlywayTeamsPropertyOnly("kerberosConfigFile"));
}
@Test
void oracleKerberosCacheFileIsCorrectlyMapped() {
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
.withPropertyValues("spring.flyway.oracle-kerberos-cache-file=/tmp/cache")
.run(validateFlywayTeamsPropertyOnly("oracle.kerberosCacheFile"));
}
@Test
void outputQueryResultsIsCorrectlyMapped() {
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)

View File

@@ -109,6 +109,9 @@ class FlywayPropertiesTests {
PropertyAccessorFactory.forBeanPropertyAccess(new ClassicConfiguration()));
// Properties specific settings
ignoreProperties(properties, "url", "driverClassName", "user", "password", "enabled");
// Property that moved to a separate Oracle plugin
ignoreProperties(properties, "oracleSqlplus", "oracleSqlplusWarn", "oracleKerberosCacheFile",
"oracleWalletLocation");
// Property that moved to a separate SQL plugin
ignoreProperties(properties, "sqlServerKerberosLoginFile");
// High level object we can't set with properties
@@ -128,7 +131,7 @@ class FlywayPropertiesTests {
// Handled as createSchemas
ignoreProperties(configuration, "shouldCreateSchemas");
// Getters for the DataSource settings rather than actual properties
ignoreProperties(configuration, "password", "url", "user");
ignoreProperties(configuration, "databaseType", "password", "url", "user");
// Properties not exposed by Flyway
ignoreProperties(configuration, "failOnMissingTarget");
List<String> configurationKeys = new ArrayList<>(configuration.keySet());

View File

@@ -279,10 +279,11 @@ bom {
]
}
}
library("Flyway", "9.19.4") {
library("Flyway", "9.20.1") {
group("org.flywaydb") {
modules = [
"flyway-core",
"flyway-database-oracle",
"flyway-firebird",
"flyway-mysql",
"flyway-sqlserver"