From 89cb68d4e29a5440e7b8be5117d3803d907dbc6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Hermann?= Date: Tue, 6 Sep 2022 00:14:09 -0400 Subject: [PATCH] flyway: config option to baseline new environment at startup --- ...AtStartExistingSchemaHistoryTableTest.java | 40 +++++++++++++++++++ .../FlywayExtensionBaselineAtStartTest.java | 39 ++++++++++++++++++ .../baseline-at-start-config.properties | 8 ++++ ...ing-schema-history-table-config.properties | 8 ++++ .../h2-init-schema-history-table.sql | 13 ++++++ .../flyway/runtime/FlywayContainer.java | 11 ++++- .../runtime/FlywayContainerProducer.java | 3 +- .../FlywayDataSourceRuntimeConfig.java | 13 +++++- .../flyway/runtime/FlywayRecorder.java | 29 ++++++++++++++ 9 files changed, 160 insertions(+), 4 deletions(-) create mode 100644 extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionBaselineAtStartExistingSchemaHistoryTableTest.java create mode 100644 extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionBaselineAtStartTest.java create mode 100644 extensions/flyway/deployment/src/test/resources/baseline-at-start-config.properties create mode 100644 extensions/flyway/deployment/src/test/resources/baseline-at-start-existing-schema-history-table-config.properties create mode 100644 extensions/flyway/deployment/src/test/resources/h2-init-schema-history-table.sql diff --git a/extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionBaselineAtStartExistingSchemaHistoryTableTest.java b/extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionBaselineAtStartExistingSchemaHistoryTableTest.java new file mode 100644 index 0000000000000..9e604742a8a3b --- /dev/null +++ b/extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionBaselineAtStartExistingSchemaHistoryTableTest.java @@ -0,0 +1,40 @@ +package io.quarkus.flyway.test; + +import static org.junit.jupiter.api.Assertions.assertNull; + +import jakarta.inject.Inject; + +import org.flywaydb.core.Flyway; +import org.flywaydb.core.api.MigrationInfo; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; + +public class FlywayExtensionBaselineAtStartExistingSchemaHistoryTableTest { + @Inject + Flyway flyway; + + static final FlywayH2TestCustomizer customizer = FlywayH2TestCustomizer + .withDbName("quarkus-baseline-at-start-existing-schema-history") + .withPort(11309) + .withInitSqlFile("src/test/resources/h2-init-schema-history-table.sql"); + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .setBeforeAllCustomizer(customizer::startH2) + .setAfterAllCustomizer(customizer::stopH2) + .withApplicationRoot((jar) -> jar + .addClass(FlywayH2TestCustomizer.class) + .addAsResource("db/migration/V1.0.0__Quarkus.sql") + .addAsResource("baseline-at-start-existing-schema-history-table-config.properties", + "application.properties")); + + @Test + @DisplayName("Baseline at start is not executed against existing schema-history-table") + public void testFlywayConfigInjection() { + MigrationInfo migrationInfo = flyway.info().current(); + assertNull(migrationInfo, "Flyway baseline was executed on existing schema history table"); + } +} diff --git a/extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionBaselineAtStartTest.java b/extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionBaselineAtStartTest.java new file mode 100644 index 0000000000000..717b3e3777ead --- /dev/null +++ b/extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionBaselineAtStartTest.java @@ -0,0 +1,39 @@ +package io.quarkus.flyway.test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import jakarta.inject.Inject; + +import org.flywaydb.core.Flyway; +import org.flywaydb.core.api.MigrationInfo; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; + +public class FlywayExtensionBaselineAtStartTest { + @Inject + Flyway flyway; + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .withApplicationRoot((jar) -> jar + .addAsResource("db/migration/V1.0.0__Quarkus.sql") + .addAsResource("baseline-at-start-config.properties", "application.properties")); + + @Test + @DisplayName("Baseline at start is executed against empty schema") + public void testFlywayConfigInjection() { + MigrationInfo migrationInfo = flyway.info().current(); + assertNotNull(migrationInfo, "No Flyway migration was executed"); + assertTrue(migrationInfo.getType().isBaseline(), "Flyway migration is not a baseline"); + String currentVersion = migrationInfo + .getVersion() + .toString(); + + assertEquals("1.0.1", currentVersion); + } +} diff --git a/extensions/flyway/deployment/src/test/resources/baseline-at-start-config.properties b/extensions/flyway/deployment/src/test/resources/baseline-at-start-config.properties new file mode 100644 index 0000000000000..b476e073dc59f --- /dev/null +++ b/extensions/flyway/deployment/src/test/resources/baseline-at-start-config.properties @@ -0,0 +1,8 @@ +quarkus.datasource.db-kind=h2 +quarkus.datasource.username=sa +quarkus.datasource.password=sa +quarkus.datasource.jdbc.url=jdbc:h2:tcp://localhost/mem:quarkus-baseline-at-start;DB_CLOSE_DELAY=-1 + +# Flyway config properties +quarkus.flyway.baseline-at-start=true +quarkus.flyway.baseline-version=1.0.1 \ No newline at end of file diff --git a/extensions/flyway/deployment/src/test/resources/baseline-at-start-existing-schema-history-table-config.properties b/extensions/flyway/deployment/src/test/resources/baseline-at-start-existing-schema-history-table-config.properties new file mode 100644 index 0000000000000..23d6fbbc2303c --- /dev/null +++ b/extensions/flyway/deployment/src/test/resources/baseline-at-start-existing-schema-history-table-config.properties @@ -0,0 +1,8 @@ +quarkus.datasource.db-kind=h2 +quarkus.datasource.username=sa +quarkus.datasource.password=sa +quarkus.datasource.jdbc.url=jdbc:h2:tcp://localhost:11309/mem:quarkus-baseline-at-start-existing-schema-history;DB_CLOSE_DELAY=-1 + +# Flyway config properties +quarkus.flyway.baseline-at-start=true +quarkus.flyway.baseline-version=1.0.1 \ No newline at end of file diff --git a/extensions/flyway/deployment/src/test/resources/h2-init-schema-history-table.sql b/extensions/flyway/deployment/src/test/resources/h2-init-schema-history-table.sql new file mode 100644 index 0000000000000..fafb04b764139 --- /dev/null +++ b/extensions/flyway/deployment/src/test/resources/h2-init-schema-history-table.sql @@ -0,0 +1,13 @@ +CREATE TABLE "flyway_schema_history" ( + "installed_rank" integer NOT NULL, + "version" character varying(50), + "description" character varying(200) NOT NULL, + "type" character varying(20) NOT NULL, + "script" character varying(1000) NOT NULL, + "checksum" integer, + "installed_by" character varying(100) NOT NULL, + "installed_on" timestamp without time zone DEFAULT now() NOT NULL, + "execution_time" integer NOT NULL, + "success" boolean NOT NULL, + CONSTRAINT flyway_schema_history_pk PRIMARY KEY ("installed_rank") +); \ No newline at end of file diff --git a/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/FlywayContainer.java b/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/FlywayContainer.java index 0a956422d1a6b..cc952cb0f15aa 100644 --- a/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/FlywayContainer.java +++ b/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/FlywayContainer.java @@ -5,6 +5,8 @@ public class FlywayContainer { private final Flyway flyway; + + private final boolean baselineAtStart; private final boolean cleanAtStart; private final boolean migrateAtStart; private final boolean repairAtStart; @@ -15,10 +17,11 @@ public class FlywayContainer { private final boolean createPossible; private final String id; - public FlywayContainer(Flyway flyway, boolean cleanAtStart, boolean migrateAtStart, boolean repairAtStart, - boolean validateAtStart, + public FlywayContainer(Flyway flyway, boolean baselineAtStart, boolean cleanAtStart, boolean migrateAtStart, + boolean repairAtStart, boolean validateAtStart, String dataSourceName, boolean hasMigrations, boolean createPossible) { this.flyway = flyway; + this.baselineAtStart = baselineAtStart; this.cleanAtStart = cleanAtStart; this.migrateAtStart = migrateAtStart; this.repairAtStart = repairAtStart; @@ -33,6 +36,10 @@ public Flyway getFlyway() { return flyway; } + public boolean isBaselineAtStart() { + return baselineAtStart; + } + public boolean isCleanAtStart() { return cleanAtStart; } diff --git a/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/FlywayContainerProducer.java b/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/FlywayContainerProducer.java index d58bb692839ed..fe921bdf6a9a3 100644 --- a/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/FlywayContainerProducer.java +++ b/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/FlywayContainerProducer.java @@ -51,7 +51,8 @@ public FlywayContainer createFlyway(DataSource dataSource, String dataSourceName final Flyway flyway = new FlywayCreator(matchingRuntimeConfig, matchingBuildTimeConfig, matchingConfigCustomizers( configCustomizerInstances, dataSourceName)).withCallbacks(callbacks) .createFlyway(dataSource); - return new FlywayContainer(flyway, matchingRuntimeConfig.cleanAtStart, matchingRuntimeConfig.migrateAtStart, + return new FlywayContainer(flyway, matchingRuntimeConfig.baselineAtStart, matchingRuntimeConfig.cleanAtStart, + matchingRuntimeConfig.migrateAtStart, matchingRuntimeConfig.repairAtStart, matchingRuntimeConfig.validateAtStart, dataSourceName, hasMigrations, createPossible); diff --git a/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/FlywayDataSourceRuntimeConfig.java b/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/FlywayDataSourceRuntimeConfig.java index b5826b3ecc46c..79f4c961a0e5a 100644 --- a/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/FlywayDataSourceRuntimeConfig.java +++ b/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/FlywayDataSourceRuntimeConfig.java @@ -152,11 +152,22 @@ public static FlywayDataSourceRuntimeConfig defaultConfig() { public boolean validateAtStart; /** - * Enable the creation of the history table if it does not exist already. + * true to execute Flyway baseline before migrations This flag is ignored if the flyway_schema_history table exists in the + * current schema or if the current schema is empty. + * Note that this will not automatically call migrate, you must either enable baselineAtStart or programmatically call + * flyway.migrate(). */ @ConfigItem public boolean baselineOnMigrate; + /** + * true to execute Flyway baseline automatically when the application starts. + * This flag is ignored if the flyway_schema_history table exists in the current schema. + * This will work even if the current schema is empty. + */ + @ConfigItem + public boolean baselineAtStart; + /** * The initial baseline version. */ diff --git a/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/FlywayRecorder.java b/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/FlywayRecorder.java index 2b0f724a9a16f..2bb0fffb51c72 100644 --- a/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/FlywayRecorder.java +++ b/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/FlywayRecorder.java @@ -11,8 +11,16 @@ import jakarta.enterprise.inject.UnsatisfiedResolutionException; import org.flywaydb.core.Flyway; +import org.flywaydb.core.FlywayExecutor; import org.flywaydb.core.api.callback.Callback; import org.flywaydb.core.api.migration.JavaMigration; +import org.flywaydb.core.api.output.BaselineResult; +import org.flywaydb.core.internal.callback.CallbackExecutor; +import org.flywaydb.core.internal.database.base.Database; +import org.flywaydb.core.internal.database.base.Schema; +import org.flywaydb.core.internal.jdbc.StatementInterceptor; +import org.flywaydb.core.internal.resolver.CompositeMigrationResolver; +import org.flywaydb.core.internal.schemahistory.SchemaHistory; import org.jboss.logging.Logger; import io.quarkus.agroal.runtime.DataSources; @@ -101,6 +109,10 @@ public void doStartActions() { if (flywayContainer.isValidateAtStart()) { flywayContainer.getFlyway().validate(); } + if (flywayContainer.isBaselineAtStart()) { + new FlywayExecutor(flywayContainer.getFlyway().getConfiguration()) + .execute(new BaselineCommand(flywayContainer.getFlyway()), true, null); + } if (flywayContainer.isRepairAtStart()) { flywayContainer.getFlyway().repair(); } @@ -109,4 +121,21 @@ public void doStartActions() { } } } + + static class BaselineCommand implements FlywayExecutor.Command { + BaselineCommand(Flyway flyway) { + this.flyway = flyway; + } + + final Flyway flyway; + + @Override + public BaselineResult execute(CompositeMigrationResolver cmr, SchemaHistory schemaHistory, Database d, + Schema defaultSchema, Schema[] s, CallbackExecutor ce, StatementInterceptor si) { + if (!schemaHistory.exists()) { + return flyway.baseline(); + } + return null; + } + } }