Skip to content

Commit 32caf76

Browse files
onobcsnicoll
authored andcommitted
Add configuration properties for Flyway's Vault and Conjur support
See gh-25456
1 parent 14c4221 commit 32caf76

File tree

4 files changed

+150
-81
lines changed

4 files changed

+150
-81
lines changed

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfiguration.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@
9191
* @author Dan Zheng
9292
* @author András Deák
9393
* @author Semyon Danilov
94+
* @author Chris Bono
9495
* @since 1.1.0
9596
*/
9697
@Configuration(proxyBeanMethods = false)
@@ -245,6 +246,15 @@ private void configureProperties(FluentConfiguration configuration, FlywayProper
245246
// No method reference for compatibility with Flyway 6.x
246247
map.from(properties.getSkipExecutingMigrations()).whenNonNull()
247248
.to((skipExecutingMigrations) -> configuration.skipExecutingMigrations(skipExecutingMigrations));
249+
// Teams secrets management properties (all non-method reference for
250+
// compatibility with Flyway 6.x)
251+
map.from(properties.getConjurUrl()).whenNonNull().to((conjurUrl) -> configuration.conjurUrl(conjurUrl));
252+
map.from(properties.getConjurToken()).whenNonNull()
253+
.to((conjurToken) -> configuration.conjurToken(conjurToken));
254+
map.from(properties.getVaultUrl()).whenNonNull().to((vaultUrl) -> configuration.vaultUrl(vaultUrl));
255+
map.from(properties.getVaultToken()).whenNonNull().to((vaultToken) -> configuration.vaultToken(vaultToken));
256+
map.from(properties.getVaultSecrets()).whenNonNull()
257+
.to((vaultSecrets) -> configuration.vaultSecrets(vaultSecrets.toArray(new String[0])));
248258
}
249259

250260
private void configureCreateSchemas(FluentConfiguration configuration, boolean createSchemas) {

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayProperties.java

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
* @author Dave Syer
3434
* @author Eddú Meléndez
3535
* @author Stephane Nicoll
36+
* @author Chris Bono
3637
* @since 1.1.0
3738
*/
3839
@ConfigurationProperties(prefix = "spring.flyway")
@@ -328,6 +329,33 @@ public class FlywayProperties {
328329
*/
329330
private Boolean skipExecutingMigrations;
330331

332+
/**
333+
* REST API URL of the Conjur server. Requires Flyway teams.
334+
*/
335+
private String conjurUrl;
336+
337+
/**
338+
* Conjur token required to access secrets. Requires Flyway teams.
339+
*/
340+
private String conjurToken;
341+
342+
/**
343+
* REST API URL of the Vault server. Requires Flyway teams.
344+
*/
345+
private String vaultUrl;
346+
347+
/**
348+
* Vault token required to access secrets. Requires Flyway teams.
349+
*/
350+
private String vaultToken;
351+
352+
/**
353+
* Comma-separated list of paths to secrets that contain Flyway configurations. Each
354+
* path must start with the name of the engine and end with the name of the secret
355+
* such 'kv/test/1/config'. Requires Flyway teams.
356+
*/
357+
private List<String> vaultSecrets;
358+
331359
public boolean isEnabled() {
332360
return this.enabled;
333361
}
@@ -772,4 +800,44 @@ public void setSkipExecutingMigrations(Boolean skipExecutingMigrations) {
772800
this.skipExecutingMigrations = skipExecutingMigrations;
773801
}
774802

803+
public String getConjurUrl() {
804+
return this.conjurUrl;
805+
}
806+
807+
public void setConjurUrl(String conjurUrl) {
808+
this.conjurUrl = conjurUrl;
809+
}
810+
811+
public String getConjurToken() {
812+
return this.conjurToken;
813+
}
814+
815+
public void setConjurToken(String conjurToken) {
816+
this.conjurToken = conjurToken;
817+
}
818+
819+
public String getVaultUrl() {
820+
return this.vaultUrl;
821+
}
822+
823+
public void setVaultUrl(String vaultUrl) {
824+
this.vaultUrl = vaultUrl;
825+
}
826+
827+
public String getVaultToken() {
828+
return this.vaultToken;
829+
}
830+
831+
public void setVaultToken(String vaultToken) {
832+
this.vaultToken = vaultToken;
833+
}
834+
835+
public List<String> getVaultSecrets() {
836+
return this.vaultSecrets;
837+
}
838+
839+
public void setVaultSecrets(List<String> vaultSecrets) {
840+
this.vaultSecrets = vaultSecrets;
841+
}
842+
775843
}

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfigurationTests.java

Lines changed: 71 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,9 @@
4646
import org.springframework.boot.jdbc.DataSourceBuilder;
4747
import org.springframework.boot.jdbc.SchemaManagement;
4848
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
49+
import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
4950
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
51+
import org.springframework.boot.test.context.runner.ContextConsumer;
5052
import org.springframework.boot.test.system.CapturedOutput;
5153
import org.springframework.boot.test.system.OutputCaptureExtension;
5254
import org.springframework.context.annotation.Bean;
@@ -82,6 +84,7 @@
8284
* @author Dominic Gunn
8385
* @author András Deák
8486
* @author Takaaki Shimbo
87+
* @author Chris Bono
8588
*/
8689
@ExtendWith(OutputCaptureExtension.class)
8790
class FlywayAutoConfigurationTests {
@@ -413,34 +416,21 @@ void configurationCustomizersAreConfiguredAndOrdered() {
413416
@Test
414417
void batchIsCorrectlyMapped() {
415418
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
416-
.withPropertyValues("spring.flyway.batch=true").run((context) -> {
417-
assertThat(context).hasFailed();
418-
Throwable failure = context.getStartupFailure();
419-
assertThat(failure).hasRootCauseInstanceOf(FlywayTeamsUpgradeRequiredException.class);
420-
assertThat(failure).hasMessageContaining(" batch ");
421-
});
419+
.withPropertyValues("spring.flyway.batch=true").run(validateTeamsPropertyCorrectlyMapped("batch"));
422420
}
423421

424422
@Test
425423
void dryRunOutputIsCorrectlyMapped() {
426424
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
427-
.withPropertyValues("spring.flyway.dryRunOutput=dryrun.sql").run((context) -> {
428-
assertThat(context).hasFailed();
429-
Throwable failure = context.getStartupFailure();
430-
assertThat(failure).hasRootCauseInstanceOf(FlywayTeamsUpgradeRequiredException.class);
431-
assertThat(failure).hasMessageContaining(" dryRunOutput ");
432-
});
425+
.withPropertyValues("spring.flyway.dryRunOutput=dryrun.sql")
426+
.run(validateTeamsPropertyCorrectlyMapped("dryRunOutput"));
433427
}
434428

435429
@Test
436430
void errorOverridesIsCorrectlyMapped() {
437431
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
438-
.withPropertyValues("spring.flyway.errorOverrides=D12345").run((context) -> {
439-
assertThat(context).hasFailed();
440-
Throwable failure = context.getStartupFailure();
441-
assertThat(failure).hasRootCauseInstanceOf(FlywayTeamsUpgradeRequiredException.class);
442-
assertThat(failure).hasMessageContaining(" errorOverrides ");
443-
});
432+
.withPropertyValues("spring.flyway.errorOverrides=D12345")
433+
.run(validateTeamsPropertyCorrectlyMapped("errorOverrides"));
444434
}
445435

446436
@Test
@@ -453,45 +443,28 @@ void licenseKeyIsCorrectlyMapped(CapturedOutput output) {
453443
@Test
454444
void oracleSqlplusIsCorrectlyMapped() {
455445
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
456-
.withPropertyValues("spring.flyway.oracle-sqlplus=true").run((context) -> {
457-
assertThat(context).hasFailed();
458-
Throwable failure = context.getStartupFailure();
459-
assertThat(failure).hasRootCauseInstanceOf(FlywayTeamsUpgradeRequiredException.class);
460-
assertThat(failure).hasMessageContaining(" oracle.sqlplus ");
461-
});
446+
.withPropertyValues("spring.flyway.oracle-sqlplus=true")
447+
.run(validateTeamsPropertyCorrectlyMapped("oracle.sqlplus"));
462448
}
463449

464450
@Test
465451
void oracleSqlplusWarnIsCorrectlyMapped() {
466452
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
467-
.withPropertyValues("spring.flyway.oracle-sqlplus-warn=true").run((context) -> {
468-
assertThat(context).hasFailed();
469-
Throwable failure = context.getStartupFailure();
470-
assertThat(failure).hasRootCauseInstanceOf(FlywayTeamsUpgradeRequiredException.class);
471-
assertThat(failure).hasMessageContaining(" oracle.sqlplusWarn ");
472-
});
453+
.withPropertyValues("spring.flyway.oracle-sqlplus-warn=true")
454+
.run(validateTeamsPropertyCorrectlyMapped("oracle.sqlplusWarn"));
473455
}
474456

475457
@Test
476458
void streamIsCorrectlyMapped() {
477459
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
478-
.withPropertyValues("spring.flyway.stream=true").run((context) -> {
479-
assertThat(context).hasFailed();
480-
Throwable failure = context.getStartupFailure();
481-
assertThat(failure).hasRootCauseInstanceOf(FlywayTeamsUpgradeRequiredException.class);
482-
assertThat(failure).hasMessageContaining(" stream ");
483-
});
460+
.withPropertyValues("spring.flyway.stream=true").run(validateTeamsPropertyCorrectlyMapped("stream"));
484461
}
485462

486463
@Test
487464
void undoSqlMigrationPrefix() {
488465
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
489-
.withPropertyValues("spring.flyway.undo-sql-migration-prefix=undo").run((context) -> {
490-
assertThat(context).hasFailed();
491-
Throwable failure = context.getStartupFailure();
492-
assertThat(failure).hasRootCauseInstanceOf(FlywayTeamsUpgradeRequiredException.class);
493-
assertThat(failure).hasMessageContaining(" undoSqlMigrationPrefix ");
494-
});
466+
.withPropertyValues("spring.flyway.undo-sql-migration-prefix=undo")
467+
.run(validateTeamsPropertyCorrectlyMapped("undoSqlMigrationPrefix"));
495468
}
496469

497470
@Test
@@ -526,67 +499,78 @@ void initSqlsWithFlywayUrl() {
526499
@Test
527500
void cherryPickIsCorrectlyMapped() {
528501
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
529-
.withPropertyValues("spring.flyway.cherry-pick=1.1").run((context) -> {
530-
assertThat(context).hasFailed();
531-
Throwable failure = context.getStartupFailure();
532-
assertThat(failure).hasRootCauseInstanceOf(FlywayTeamsUpgradeRequiredException.class);
533-
assertThat(failure).hasMessageContaining(" cherryPick ");
534-
});
502+
.withPropertyValues("spring.flyway.cherry-pick=1.1")
503+
.run(validateTeamsPropertyCorrectlyMapped("cherryPick"));
535504
}
536505

537506
@Test
538507
void jdbcPropertiesAreCorrectlyMapped() {
539508
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
540-
.withPropertyValues("spring.flyway.jdbc-properties.prop=value").run((context) -> {
541-
assertThat(context).hasFailed();
542-
Throwable failure = context.getStartupFailure();
543-
assertThat(failure).hasRootCauseInstanceOf(FlywayTeamsUpgradeRequiredException.class);
544-
assertThat(failure).hasMessageContaining(" jdbcProperties ");
545-
});
509+
.withPropertyValues("spring.flyway.jdbc-properties.prop=value")
510+
.run(validateTeamsPropertyCorrectlyMapped("jdbcProperties"));
546511
}
547512

548513
@Test
549514
void oracleKerberosCacheFileIsCorrectlyMapped() {
550515
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
551-
.withPropertyValues("spring.flyway.oracle-kerberos-cache-file=/tmp/cache").run((context) -> {
552-
assertThat(context).hasFailed();
553-
Throwable failure = context.getStartupFailure();
554-
assertThat(failure).hasRootCauseInstanceOf(FlywayTeamsUpgradeRequiredException.class);
555-
assertThat(failure).hasMessageContaining(" oracle.kerberosCacheFile ");
556-
});
516+
.withPropertyValues("spring.flyway.oracle-kerberos-cache-file=/tmp/cache")
517+
.run(validateTeamsPropertyCorrectlyMapped("oracle.kerberosCacheFile"));
557518
}
558519

559520
@Test
560521
void oracleKerberosConfigFileIsCorrectlyMapped() {
561522
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
562-
.withPropertyValues("spring.flyway.oracle-kerberos-config-file=/tmp/config").run((context) -> {
563-
assertThat(context).hasFailed();
564-
Throwable failure = context.getStartupFailure();
565-
assertThat(failure).hasRootCauseInstanceOf(FlywayTeamsUpgradeRequiredException.class);
566-
assertThat(failure).hasMessageContaining(" oracle.kerberosConfigFile ");
567-
});
523+
.withPropertyValues("spring.flyway.oracle-kerberos-config-file=/tmp/config")
524+
.run(validateTeamsPropertyCorrectlyMapped("oracle.kerberosConfigFile"));
568525
}
569526

570527
@Test
571528
void outputQueryResultsIsCorrectlyMapped() {
572529
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
573-
.withPropertyValues("spring.flyway.output-query-results=false").run((context) -> {
574-
assertThat(context).hasFailed();
575-
Throwable failure = context.getStartupFailure();
576-
assertThat(failure).hasRootCauseInstanceOf(FlywayTeamsUpgradeRequiredException.class);
577-
assertThat(failure).hasMessageContaining(" outputQueryResults ");
578-
});
530+
.withPropertyValues("spring.flyway.output-query-results=false")
531+
.run(validateTeamsPropertyCorrectlyMapped("outputQueryResults"));
579532
}
580533

581534
@Test
582535
void skipExecutingMigrationsIsCorrectlyMapped() {
583536
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
584-
.withPropertyValues("spring.flyway.skip-executing-migrations=true").run((context) -> {
585-
assertThat(context).hasFailed();
586-
Throwable failure = context.getStartupFailure();
587-
assertThat(failure).hasRootCauseInstanceOf(FlywayTeamsUpgradeRequiredException.class);
588-
assertThat(failure).hasMessageContaining(" skipExecutingMigrations ");
589-
});
537+
.withPropertyValues("spring.flyway.skip-executing-migrations=true")
538+
.run(validateTeamsPropertyCorrectlyMapped("skipExecutingMigrations"));
539+
}
540+
541+
@Test
542+
void conjurUrlIsCorrectlyMapped() {
543+
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
544+
.withPropertyValues("spring.flyway.conjur-url=http://foo.com/secrets")
545+
.run(validateTeamsPropertyCorrectlyMapped("conjurUrl"));
546+
}
547+
548+
@Test
549+
void conjurTokenIsCorrectlyMapped() {
550+
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
551+
.withPropertyValues("spring.flyway.conjur-token=5150")
552+
.run(validateTeamsPropertyCorrectlyMapped("conjurToken"));
553+
}
554+
555+
@Test
556+
void vaultUrlIsCorrectlyMapped() {
557+
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
558+
.withPropertyValues("spring.flyway.vault-url=http://foo.com/secrets")
559+
.run(validateTeamsPropertyCorrectlyMapped("vaultUrl"));
560+
}
561+
562+
@Test
563+
void vaultTokenIsCorrectlyMapped() {
564+
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
565+
.withPropertyValues("spring.flyway.vault-token=5150")
566+
.run(validateTeamsPropertyCorrectlyMapped("vaultToken"));
567+
}
568+
569+
@Test
570+
void vaultSecretsIsCorrectlyMapped() {
571+
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
572+
.withPropertyValues("spring.flyway.vault-secrets=kv/test/1/config,kv/test/2/config")
573+
.run(validateTeamsPropertyCorrectlyMapped("vaultSecrets"));
590574
}
591575

592576
@Test
@@ -616,6 +600,15 @@ void whenCustomFlywayIsDefinedThenJooqDslContextDependsOnIt() {
616600
});
617601
}
618602

603+
private ContextConsumer<AssertableApplicationContext> validateTeamsPropertyCorrectlyMapped(String propertyName) {
604+
return (context) -> {
605+
assertThat(context).hasFailed();
606+
Throwable failure = context.getStartupFailure();
607+
assertThat(failure).hasRootCauseInstanceOf(FlywayTeamsUpgradeRequiredException.class);
608+
assertThat(failure).hasMessageContaining(String.format(" %s ", propertyName));
609+
};
610+
}
611+
619612
@Configuration(proxyBeanMethods = false)
620613
static class FlywayDataSourceConfiguration {
621614

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/FlywayPropertiesTests.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
* Tests for {@link FlywayProperties}.
4141
*
4242
* @author Stephane Nicoll
43+
* @author Chris Bono
4344
*/
4445
class FlywayPropertiesTests {
4546

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

0 commit comments

Comments
 (0)