Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@
* @author Dan Zheng
* @author András Deák
* @author Semyon Danilov
* @author Chris Bono
* @since 1.1.0
*/
@Configuration(proxyBeanMethods = false)
Expand Down Expand Up @@ -245,6 +246,15 @@ private void configureProperties(FluentConfiguration configuration, FlywayProper
// No method reference for compatibility with Flyway 6.x
map.from(properties.getSkipExecutingMigrations()).whenNonNull()
.to((skipExecutingMigrations) -> configuration.skipExecutingMigrations(skipExecutingMigrations));
// Teams secrets management properties (all non-method reference for
// compatibility with Flyway 6.x)
map.from(properties.getConjurUrl()).whenNonNull().to((conjurUrl) -> configuration.conjurUrl(conjurUrl));
map.from(properties.getConjurToken()).whenNonNull()
.to((conjurToken) -> configuration.conjurToken(conjurToken));
map.from(properties.getVaultUrl()).whenNonNull().to((vaultUrl) -> configuration.vaultUrl(vaultUrl));
map.from(properties.getVaultToken()).whenNonNull().to((vaultToken) -> configuration.vaultToken(vaultToken));
map.from(properties.getVaultSecrets()).whenNonNull()
.to((vaultSecrets) -> configuration.vaultSecrets(vaultSecrets.toArray(new String[0])));
}

private void configureCreateSchemas(FluentConfiguration configuration, boolean createSchemas) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
* @author Dave Syer
* @author Eddú Meléndez
* @author Stephane Nicoll
* @author Chris Bono
* @since 1.1.0
*/
@ConfigurationProperties(prefix = "spring.flyway")
Expand Down Expand Up @@ -328,6 +329,33 @@ public class FlywayProperties {
*/
private Boolean skipExecutingMigrations;

/**
* REST API URL of the Conjur server. Requires Flyway teams.
*/
private String conjurUrl;

/**
* Conjur token required to access secrets. Requires Flyway teams.
*/
private String conjurToken;

/**
* REST API URL of the Vault server. Requires Flyway teams.
*/
private String vaultUrl;

/**
* Vault token required to access secrets. Requires Flyway teams.
*/
private String vaultToken;

/**
* Comma-separated list of paths to secrets that contain Flyway configurations. Each
* path must start with the name of the engine and end with the name of the secret
* such 'kv/test/1/config'. Requires Flyway teams.
*/
private List<String> vaultSecrets;

public boolean isEnabled() {
return this.enabled;
}
Expand Down Expand Up @@ -772,4 +800,44 @@ public void setSkipExecutingMigrations(Boolean skipExecutingMigrations) {
this.skipExecutingMigrations = skipExecutingMigrations;
}

public String getConjurUrl() {
return this.conjurUrl;
}

public void setConjurUrl(String conjurUrl) {
this.conjurUrl = conjurUrl;
}

public String getConjurToken() {
return this.conjurToken;
}

public void setConjurToken(String conjurToken) {
this.conjurToken = conjurToken;
}

public String getVaultUrl() {
return this.vaultUrl;
}

public void setVaultUrl(String vaultUrl) {
this.vaultUrl = vaultUrl;
}

public String getVaultToken() {
return this.vaultToken;
}

public void setVaultToken(String vaultToken) {
this.vaultToken = vaultToken;
}

public List<String> getVaultSecrets() {
return this.vaultSecrets;
}

public void setVaultSecrets(List<String> vaultSecrets) {
this.vaultSecrets = vaultSecrets;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.boot.jdbc.SchemaManagement;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.boot.test.context.runner.ContextConsumer;
import org.springframework.boot.test.system.CapturedOutput;
import org.springframework.boot.test.system.OutputCaptureExtension;
import org.springframework.context.annotation.Bean;
Expand Down Expand Up @@ -82,6 +84,7 @@
* @author Dominic Gunn
* @author András Deák
* @author Takaaki Shimbo
* @author Chris Bono
*/
@ExtendWith(OutputCaptureExtension.class)
class FlywayAutoConfigurationTests {
Expand Down Expand Up @@ -413,34 +416,21 @@ void configurationCustomizersAreConfiguredAndOrdered() {
@Test
void batchIsCorrectlyMapped() {
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
.withPropertyValues("spring.flyway.batch=true").run((context) -> {
assertThat(context).hasFailed();
Throwable failure = context.getStartupFailure();
assertThat(failure).hasRootCauseInstanceOf(FlywayTeamsUpgradeRequiredException.class);
assertThat(failure).hasMessageContaining(" batch ");
});
.withPropertyValues("spring.flyway.batch=true").run(validateTeamsPropertyCorrectlyMapped("batch"));
}

@Test
void dryRunOutputIsCorrectlyMapped() {
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
.withPropertyValues("spring.flyway.dryRunOutput=dryrun.sql").run((context) -> {
assertThat(context).hasFailed();
Throwable failure = context.getStartupFailure();
assertThat(failure).hasRootCauseInstanceOf(FlywayTeamsUpgradeRequiredException.class);
assertThat(failure).hasMessageContaining(" dryRunOutput ");
});
.withPropertyValues("spring.flyway.dryRunOutput=dryrun.sql")
.run(validateTeamsPropertyCorrectlyMapped("dryRunOutput"));
}

@Test
void errorOverridesIsCorrectlyMapped() {
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
.withPropertyValues("spring.flyway.errorOverrides=D12345").run((context) -> {
assertThat(context).hasFailed();
Throwable failure = context.getStartupFailure();
assertThat(failure).hasRootCauseInstanceOf(FlywayTeamsUpgradeRequiredException.class);
assertThat(failure).hasMessageContaining(" errorOverrides ");
});
.withPropertyValues("spring.flyway.errorOverrides=D12345")
.run(validateTeamsPropertyCorrectlyMapped("errorOverrides"));
}

@Test
Expand All @@ -453,45 +443,28 @@ void licenseKeyIsCorrectlyMapped(CapturedOutput output) {
@Test
void oracleSqlplusIsCorrectlyMapped() {
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
.withPropertyValues("spring.flyway.oracle-sqlplus=true").run((context) -> {
assertThat(context).hasFailed();
Throwable failure = context.getStartupFailure();
assertThat(failure).hasRootCauseInstanceOf(FlywayTeamsUpgradeRequiredException.class);
assertThat(failure).hasMessageContaining(" oracle.sqlplus ");
});
.withPropertyValues("spring.flyway.oracle-sqlplus=true")
.run(validateTeamsPropertyCorrectlyMapped("oracle.sqlplus"));
}

@Test
void oracleSqlplusWarnIsCorrectlyMapped() {
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
.withPropertyValues("spring.flyway.oracle-sqlplus-warn=true").run((context) -> {
assertThat(context).hasFailed();
Throwable failure = context.getStartupFailure();
assertThat(failure).hasRootCauseInstanceOf(FlywayTeamsUpgradeRequiredException.class);
assertThat(failure).hasMessageContaining(" oracle.sqlplusWarn ");
});
.withPropertyValues("spring.flyway.oracle-sqlplus-warn=true")
.run(validateTeamsPropertyCorrectlyMapped("oracle.sqlplusWarn"));
}

@Test
void streamIsCorrectlyMapped() {
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
.withPropertyValues("spring.flyway.stream=true").run((context) -> {
assertThat(context).hasFailed();
Throwable failure = context.getStartupFailure();
assertThat(failure).hasRootCauseInstanceOf(FlywayTeamsUpgradeRequiredException.class);
assertThat(failure).hasMessageContaining(" stream ");
});
.withPropertyValues("spring.flyway.stream=true").run(validateTeamsPropertyCorrectlyMapped("stream"));
}

@Test
void undoSqlMigrationPrefix() {
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
.withPropertyValues("spring.flyway.undo-sql-migration-prefix=undo").run((context) -> {
assertThat(context).hasFailed();
Throwable failure = context.getStartupFailure();
assertThat(failure).hasRootCauseInstanceOf(FlywayTeamsUpgradeRequiredException.class);
assertThat(failure).hasMessageContaining(" undoSqlMigrationPrefix ");
});
.withPropertyValues("spring.flyway.undo-sql-migration-prefix=undo")
.run(validateTeamsPropertyCorrectlyMapped("undoSqlMigrationPrefix"));
}

@Test
Expand Down Expand Up @@ -526,67 +499,78 @@ void initSqlsWithFlywayUrl() {
@Test
void cherryPickIsCorrectlyMapped() {
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
.withPropertyValues("spring.flyway.cherry-pick=1.1").run((context) -> {
assertThat(context).hasFailed();
Throwable failure = context.getStartupFailure();
assertThat(failure).hasRootCauseInstanceOf(FlywayTeamsUpgradeRequiredException.class);
assertThat(failure).hasMessageContaining(" cherryPick ");
});
.withPropertyValues("spring.flyway.cherry-pick=1.1")
.run(validateTeamsPropertyCorrectlyMapped("cherryPick"));
}

@Test
void jdbcPropertiesAreCorrectlyMapped() {
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
.withPropertyValues("spring.flyway.jdbc-properties.prop=value").run((context) -> {
assertThat(context).hasFailed();
Throwable failure = context.getStartupFailure();
assertThat(failure).hasRootCauseInstanceOf(FlywayTeamsUpgradeRequiredException.class);
assertThat(failure).hasMessageContaining(" jdbcProperties ");
});
.withPropertyValues("spring.flyway.jdbc-properties.prop=value")
.run(validateTeamsPropertyCorrectlyMapped("jdbcProperties"));
}

@Test
void oracleKerberosCacheFileIsCorrectlyMapped() {
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
.withPropertyValues("spring.flyway.oracle-kerberos-cache-file=/tmp/cache").run((context) -> {
assertThat(context).hasFailed();
Throwable failure = context.getStartupFailure();
assertThat(failure).hasRootCauseInstanceOf(FlywayTeamsUpgradeRequiredException.class);
assertThat(failure).hasMessageContaining(" oracle.kerberosCacheFile ");
});
.withPropertyValues("spring.flyway.oracle-kerberos-cache-file=/tmp/cache")
.run(validateTeamsPropertyCorrectlyMapped("oracle.kerberosCacheFile"));
}

@Test
void oracleKerberosConfigFileIsCorrectlyMapped() {
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
.withPropertyValues("spring.flyway.oracle-kerberos-config-file=/tmp/config").run((context) -> {
assertThat(context).hasFailed();
Throwable failure = context.getStartupFailure();
assertThat(failure).hasRootCauseInstanceOf(FlywayTeamsUpgradeRequiredException.class);
assertThat(failure).hasMessageContaining(" oracle.kerberosConfigFile ");
});
.withPropertyValues("spring.flyway.oracle-kerberos-config-file=/tmp/config")
.run(validateTeamsPropertyCorrectlyMapped("oracle.kerberosConfigFile"));
}

@Test
void outputQueryResultsIsCorrectlyMapped() {
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
.withPropertyValues("spring.flyway.output-query-results=false").run((context) -> {
assertThat(context).hasFailed();
Throwable failure = context.getStartupFailure();
assertThat(failure).hasRootCauseInstanceOf(FlywayTeamsUpgradeRequiredException.class);
assertThat(failure).hasMessageContaining(" outputQueryResults ");
});
.withPropertyValues("spring.flyway.output-query-results=false")
.run(validateTeamsPropertyCorrectlyMapped("outputQueryResults"));
}

@Test
void skipExecutingMigrationsIsCorrectlyMapped() {
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
.withPropertyValues("spring.flyway.skip-executing-migrations=true").run((context) -> {
assertThat(context).hasFailed();
Throwable failure = context.getStartupFailure();
assertThat(failure).hasRootCauseInstanceOf(FlywayTeamsUpgradeRequiredException.class);
assertThat(failure).hasMessageContaining(" skipExecutingMigrations ");
});
.withPropertyValues("spring.flyway.skip-executing-migrations=true")
.run(validateTeamsPropertyCorrectlyMapped("skipExecutingMigrations"));
}

@Test
void conjurUrlIsCorrectlyMapped() {
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
.withPropertyValues("spring.flyway.conjur-url=http://foo.com/secrets")
.run(validateTeamsPropertyCorrectlyMapped("conjurUrl"));
}

@Test
void conjurTokenIsCorrectlyMapped() {
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
.withPropertyValues("spring.flyway.conjur-token=5150")
.run(validateTeamsPropertyCorrectlyMapped("conjurToken"));
}

@Test
void vaultUrlIsCorrectlyMapped() {
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
.withPropertyValues("spring.flyway.vault-url=http://foo.com/secrets")
.run(validateTeamsPropertyCorrectlyMapped("vaultUrl"));
}

@Test
void vaultTokenIsCorrectlyMapped() {
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
.withPropertyValues("spring.flyway.vault-token=5150")
.run(validateTeamsPropertyCorrectlyMapped("vaultToken"));
}

@Test
void vaultSecretsIsCorrectlyMapped() {
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@snicoll thx for the suggestion. Keeping the setup in each test makes it easy to see what the test is doing from first glance. Sharing only the common validation reduces the duplicated lines (and that was the main goal of the first attempt). 👍🏻

.withPropertyValues("spring.flyway.vault-secrets=kv/test/1/config,kv/test/2/config")
.run(validateTeamsPropertyCorrectlyMapped("vaultSecrets"));
}

@Test
Expand Down Expand Up @@ -616,6 +600,15 @@ void whenCustomFlywayIsDefinedThenJooqDslContextDependsOnIt() {
});
}

private ContextConsumer<AssertableApplicationContext> validateTeamsPropertyCorrectlyMapped(String propertyName) {
return (context) -> {
assertThat(context).hasFailed();
Throwable failure = context.getStartupFailure();
assertThat(failure).hasRootCauseInstanceOf(FlywayTeamsUpgradeRequiredException.class);
assertThat(failure).hasMessageContaining(String.format(" %s ", propertyName));
};
}

@Configuration(proxyBeanMethods = false)
static class FlywayDataSourceConfiguration {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
* Tests for {@link FlywayProperties}.
*
* @author Stephane Nicoll
* @author Chris Bono
*/
class FlywayPropertiesTests {

Expand Down Expand Up @@ -108,9 +109,6 @@ void expectedPropertiesAreManaged() {
// Handled by the conversion service
ignoreProperties(configuration, "baselineVersionAsString", "encodingAsString", "locationsAsStrings",
"targetAsString");
// Teams-only properties that we cannot detect as no exception is thrown and
// getters return null
ignoreProperties(configuration, "conjurToken", "conjurUrl", "vaultSecrets", "vaultToken", "vaultUrl");
// Handled as initSql array
ignoreProperties(configuration, "initSql");
ignoreProperties(properties, "initSqls");
Expand Down