Add configuration properties for Flyway's Vault and Conjur support

See gh-25456
pull/25505/head
bono007 4 years ago committed by Stephane Nicoll
parent 14c4221fde
commit 32caf760b5

@ -91,6 +91,7 @@ import org.springframework.util.StringUtils;
* @author Dan Zheng
* @author András Deák
* @author Semyon Danilov
* @author Chris Bono
* @since 1.1.0
*/
@Configuration(proxyBeanMethods = false)
@ -245,6 +246,15 @@ public class FlywayAutoConfiguration {
// 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) {

@ -33,6 +33,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
* @author Dave Syer
* @author Eddú Meléndez
* @author Stephane Nicoll
* @author Chris Bono
* @since 1.1.0
*/
@ConfigurationProperties(prefix = "spring.flyway")
@ -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;
}
@ -772,4 +800,44 @@ public class FlywayProperties {
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;
}
}

@ -46,7 +46,9 @@ import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfigurati
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;
@ -82,6 +84,7 @@ import static org.mockito.Mockito.mock;
* @author Dominic Gunn
* @author András Deák
* @author Takaaki Shimbo
* @author Chris Bono
*/
@ExtendWith(OutputCaptureExtension.class)
class FlywayAutoConfigurationTests {
@ -413,34 +416,21 @@ class FlywayAutoConfigurationTests {
@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
@ -453,45 +443,28 @@ class FlywayAutoConfigurationTests {
@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
@ -526,67 +499,78 @@ class FlywayAutoConfigurationTests {
@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)
.withPropertyValues("spring.flyway.vault-secrets=kv/test/1/config,kv/test/2/config")
.run(validateTeamsPropertyCorrectlyMapped("vaultSecrets"));
}
@Test
@ -616,6 +600,15 @@ class FlywayAutoConfigurationTests {
});
}
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 {

@ -40,6 +40,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* Tests for {@link FlywayProperties}.
*
* @author Stephane Nicoll
* @author Chris Bono
*/
class FlywayPropertiesTests {
@ -108,9 +109,6 @@ class FlywayPropertiesTests {
// 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");

Loading…
Cancel
Save