Skip to content

Commit

Permalink
Merge pull request quarkusio#35732 from gsmet/flyway-enabled
Browse files Browse the repository at this point in the history
Rework how to enable/activate Flyway
  • Loading branch information
gsmet authored Sep 19, 2023
2 parents af76e49 + 88e50c2 commit 3bf8a5a
Show file tree
Hide file tree
Showing 14 changed files with 301 additions and 83 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package io.quarkus.flyway.deployment;

import org.flywaydb.core.extensibility.Plugin;

import io.quarkus.deployment.Feature;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.FeatureBuildItem;
import io.quarkus.deployment.builditem.IndexDependencyBuildItem;
import io.quarkus.deployment.builditem.nativeimage.NativeImageResourceBuildItem;
import io.quarkus.deployment.builditem.nativeimage.RuntimeReinitializedClassBuildItem;
import io.quarkus.deployment.builditem.nativeimage.ServiceProviderBuildItem;

public class FlywayAlwaysEnabledProcessor {

@BuildStep
void build(BuildProducer<FeatureBuildItem> featureProducer) {
featureProducer.produce(new FeatureBuildItem(Feature.FLYWAY));
}

/**
* Reinitialize {@code InsertRowLock} to avoid using a cached seed when invoking {@code getNextRandomString}
*/
@BuildStep
public RuntimeReinitializedClassBuildItem reinitInsertRowLock() {
return new RuntimeReinitializedClassBuildItem(
"org.flywaydb.core.internal.database.InsertRowLock");
}

@BuildStep
public NativeImageResourceBuildItem resources() {
return new NativeImageResourceBuildItem("org/flywaydb/database/version.txt");
}

@BuildStep
IndexDependencyBuildItem indexFlyway() {
return new IndexDependencyBuildItem("org.flywaydb", "flyway-core");
}

@BuildStep
public ServiceProviderBuildItem flywayPlugins() {
return ServiceProviderBuildItem.allProvidersFromClassPath(Plugin.class.getName());
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package io.quarkus.flyway;
package io.quarkus.flyway.deployment;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package io.quarkus.flyway.deployment;

import java.util.function.BooleanSupplier;

import io.quarkus.flyway.runtime.FlywayBuildTimeConfig;

/**
* Supplier that can be used to only run build steps
* if the Flyway extension is enabled.
*/
public class FlywayEnabled implements BooleanSupplier {

private final FlywayBuildTimeConfig config;

FlywayEnabled(FlywayBuildTimeConfig config) {
this.config = config;
}

@Override
public boolean getAsBoolean() {
return config.enabled;
}

}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package io.quarkus.flyway;
package io.quarkus.flyway.deployment;

import static io.quarkus.deployment.annotations.ExecutionTime.STATIC_INIT;

Expand All @@ -25,7 +25,6 @@
import org.flywaydb.core.api.Location;
import org.flywaydb.core.api.callback.Callback;
import org.flywaydb.core.api.migration.JavaMigration;
import org.flywaydb.core.extensibility.Plugin;
import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.ClassType;
Expand All @@ -43,34 +42,33 @@
import io.quarkus.arc.processor.DotNames;
import io.quarkus.builder.item.SimpleBuildItem;
import io.quarkus.datasource.common.runtime.DataSourceUtil;
import io.quarkus.deployment.Feature;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.annotations.BuildSteps;
import io.quarkus.deployment.annotations.Consume;
import io.quarkus.deployment.annotations.ExecutionTime;
import io.quarkus.deployment.annotations.Produce;
import io.quarkus.deployment.annotations.Record;
import io.quarkus.deployment.builditem.ApplicationInfoBuildItem;
import io.quarkus.deployment.builditem.CombinedIndexBuildItem;
import io.quarkus.deployment.builditem.FeatureBuildItem;
import io.quarkus.deployment.builditem.HotDeploymentWatchedFileBuildItem;
import io.quarkus.deployment.builditem.IndexDependencyBuildItem;
import io.quarkus.deployment.builditem.InitTaskBuildItem;
import io.quarkus.deployment.builditem.InitTaskCompletedBuildItem;
import io.quarkus.deployment.builditem.ServiceStartBuildItem;
import io.quarkus.deployment.builditem.nativeimage.NativeImageResourceBuildItem;
import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem;
import io.quarkus.deployment.builditem.nativeimage.RuntimeReinitializedClassBuildItem;
import io.quarkus.deployment.builditem.nativeimage.ServiceProviderBuildItem;
import io.quarkus.deployment.logging.LoggingSetupBuildItem;
import io.quarkus.deployment.recording.RecorderContext;
import io.quarkus.flyway.FlywayDataSource;
import io.quarkus.flyway.runtime.FlywayBuildTimeConfig;
import io.quarkus.flyway.runtime.FlywayContainer;
import io.quarkus.flyway.runtime.FlywayContainerProducer;
import io.quarkus.flyway.runtime.FlywayDataSourceBuildTimeConfig;
import io.quarkus.flyway.runtime.FlywayRecorder;
import io.quarkus.flyway.runtime.FlywayRuntimeConfig;
import io.quarkus.runtime.util.ClassPathUtils;

@BuildSteps(onlyIf = FlywayEnabled.class)
class FlywayProcessor {

private static final String CLASSPATH_APPLICATION_MIGRATIONS_PROTOCOL = "classpath";
Expand All @@ -82,32 +80,26 @@ class FlywayProcessor {

private static final Logger LOGGER = Logger.getLogger(FlywayProcessor.class);

FlywayBuildTimeConfig flywayBuildConfig;

@BuildStep
IndexDependencyBuildItem indexFlyway() {
return new IndexDependencyBuildItem("org.flywaydb", "flyway-core");
}

@Record(STATIC_INIT)
@BuildStep
MigrationStateBuildItem build(BuildProducer<FeatureBuildItem> featureProducer,
BuildProducer<NativeImageResourceBuildItem> resourceProducer,
MigrationStateBuildItem build(BuildProducer<NativeImageResourceBuildItem> resourceProducer,
BuildProducer<ReflectiveClassBuildItem> reflectiveClassProducer,
BuildProducer<HotDeploymentWatchedFileBuildItem> hotDeploymentProducer,
FlywayRecorder recorder,
RecorderContext context,
CombinedIndexBuildItem combinedIndexBuildItem,
List<JdbcDataSourceBuildItem> jdbcDataSourceBuildItems) throws Exception {

featureProducer.produce(new FeatureBuildItem(Feature.FLYWAY));
List<JdbcDataSourceBuildItem> jdbcDataSourceBuildItems,
FlywayBuildTimeConfig flywayBuildTimeConfig) throws Exception {

Collection<String> dataSourceNames = getDataSourceNames(jdbcDataSourceBuildItems);
Map<String, Collection<String>> applicationMigrationsToDs = new HashMap<>();
for (var i : dataSourceNames) {
for (var dataSourceName : dataSourceNames) {
FlywayDataSourceBuildTimeConfig flywayDataSourceBuildTimeConfig = flywayBuildTimeConfig
.getConfigForDataSourceName(dataSourceName);

Collection<String> migrationLocations = discoverApplicationMigrations(
flywayBuildConfig.getConfigForDataSourceName(i).locations);
applicationMigrationsToDs.put(i, migrationLocations);
flywayDataSourceBuildTimeConfig.locations);
applicationMigrationsToDs.put(dataSourceName, migrationLocations);
}
Set<String> datasourcesWithMigrations = new HashSet<>();
Set<String> datasourcesWithoutMigrations = new HashSet<>();
Expand Down Expand Up @@ -138,7 +130,7 @@ MigrationStateBuildItem build(BuildProducer<FeatureBuildItem> featureProducer,

final Map<String, Collection<Callback>> callbacks = FlywayCallbacksLocator.with(
dataSourceNames,
flywayBuildConfig,
flywayBuildTimeConfig,
combinedIndexBuildItem,
reflectiveClassProducer).getCallbacks();
recorder.setApplicationCallbackClasses(callbacks);
Expand Down Expand Up @@ -170,7 +162,8 @@ void createBeans(FlywayRecorder recorder,
List<JdbcInitialSQLGeneratorBuildItem> sqlGeneratorBuildItems,
BuildProducer<AdditionalBeanBuildItem> additionalBeans,
BuildProducer<SyntheticBeanBuildItem> syntheticBeanBuildItemBuildProducer,
MigrationStateBuildItem migrationsBuildItem) {
MigrationStateBuildItem migrationsBuildItem,
FlywayBuildTimeConfig flywayBuildTimeConfig) {
// make a FlywayContainerProducer bean
additionalBeans.produce(AdditionalBeanBuildItem.builder().addBeanClasses(FlywayContainerProducer.class).setUnremovable()
.setDefaultScope(DotNames.SINGLETON).build());
Expand Down Expand Up @@ -251,9 +244,14 @@ public ServiceStartBuildItem startActions(FlywayRecorder recorder,
FlywayRuntimeConfig config,
BuildProducer<JdbcDataSourceSchemaReadyBuildItem> schemaReadyBuildItem,
BuildProducer<InitTaskCompletedBuildItem> initializationCompleteBuildItem,
List<JdbcDataSourceBuildItem> jdbcDataSourceBuildItems,
MigrationStateBuildItem migrationsBuildItem) {

recorder.doStartActions();
Collection<String> dataSourceNames = getDataSourceNames(jdbcDataSourceBuildItems);

for (String dataSourceName : dataSourceNames) {
recorder.doStartActions(dataSourceName);
}

// once we are done running the migrations, we produce a build item indicating that the
// schema is "ready"
Expand Down Expand Up @@ -341,25 +339,6 @@ private Set<String> getApplicationMigrationsFromPath(final String location, fina
}
}

/**
* Reinitialize {@code InsertRowLock} to avoid using a cached seed when invoking {@code getNextRandomString}
*/
@BuildStep
public RuntimeReinitializedClassBuildItem reinitInsertRowLock() {
return new RuntimeReinitializedClassBuildItem(
"org.flywaydb.core.internal.database.InsertRowLock");
}

@BuildStep
public NativeImageResourceBuildItem resources() {
return new NativeImageResourceBuildItem("org/flywaydb/database/version.txt");
}

@BuildStep
public ServiceProviderBuildItem flywayPlugins() {
return ServiceProviderBuildItem.allProvidersFromClassPath(Plugin.class.getName());
}

public static final class MigrationStateBuildItem extends SimpleBuildItem {

final Set<String> hasMigrations;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package io.quarkus.flyway.devui;
package io.quarkus.flyway.deployment.devui;

import static io.quarkus.deployment.annotations.ExecutionTime.RUNTIME_INIT;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package io.quarkus.flyway.test;

import static org.junit.jupiter.api.Assertions.assertEquals;

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.flyway.FlywayDataSource;
import io.quarkus.test.QuarkusUnitTest;

public class FlywayExtensionBaselineOnMigrateNamedDataSourcesInactiveTest {

@Inject
@FlywayDataSource("users")
Flyway flywayUsers;

@Inject
@FlywayDataSource("laptops")
Flyway flywayLaptops;

static final FlywayH2TestCustomizer customizerUsers = FlywayH2TestCustomizer
.withDbName("quarkus-flyway-baseline-on-named-ds-users")
.withPort(11302)
.withInitSqlFile("src/test/resources/h2-init-data.sql");

static final FlywayH2TestCustomizer customizerLaptops = FlywayH2TestCustomizer
.withDbName("quarkus-flyway-baseline-on-named-ds-laptops")
.withPort(11303)
.withInitSqlFile("src/test/resources/h2-init-data.sql");

@RegisterExtension
static final QuarkusUnitTest config = new QuarkusUnitTest()
.setBeforeAllCustomizer(new Runnable() {
@Override
public void run() {
customizerUsers.startH2();
customizerLaptops.startH2();
}
})
.setAfterAllCustomizer(new Runnable() {
@Override
public void run() {
customizerUsers.stopH2();
customizerLaptops.stopH2();
}
})
.withApplicationRoot((jar) -> jar
.addClass(FlywayH2TestCustomizer.class)
.addAsResource("baseline-on-migrate-named-datasources-inactive.properties", "application.properties"));

@Test
@DisplayName("Create history table correctly")
public void testFlywayInitialBaselineInfo() {
MigrationInfo baselineInfo = flywayUsers.info().applied()[0];

assertEquals("0.0.1", baselineInfo.getVersion().getVersion());
assertEquals("Initial description for test", baselineInfo.getDescription());
}

@Test
@DisplayName("History table not created if inactive")
public void testFlywayInitialBaselineInfoInactive() {
assertEquals(0, flywayLaptops.info().applied().length);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package io.quarkus.flyway.test;

import static org.junit.jupiter.api.Assertions.assertTrue;

import jakarta.enterprise.inject.Instance;
import jakarta.inject.Inject;

import org.flywaydb.core.Flyway;
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 FlywayExtensionDisabledTest {

@Inject
Instance<Flyway> flyway;

@RegisterExtension
static final QuarkusUnitTest config = new QuarkusUnitTest()
.withApplicationRoot((jar) -> jar
.addAsResource("db/migration/V1.0.0__Quarkus.sql")
.addAsResource("disabled-config.properties", "application.properties"));

@Test
@DisplayName("No Flyway instance available if disabled")
public void testFlywayConfigInjection() {
assertTrue(flyway.isUnsatisfied());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
quarkus.datasource.users.db-kind=h2
quarkus.datasource.users.username=sa
quarkus.datasource.users.password=sa
quarkus.datasource.users.jdbc.url=jdbc:h2:tcp://localhost:11302/mem:quarkus-flyway-baseline-on-named-ds-users

# Flyway config properties
quarkus.flyway.users.migrate-at-start=true
quarkus.flyway.users.table=test_flyway_history
quarkus.flyway.users.baseline-on-migrate=true
quarkus.flyway.users.baseline-version=0.0.1
quarkus.flyway.users.baseline-description=Initial description for test

quarkus.datasource.laptops.db-kind=h2
quarkus.datasource.laptops.username=sa
quarkus.datasource.laptops.password=sa
quarkus.datasource.laptops.jdbc.url=jdbc:h2:tcp://localhost:11302/mem:quarkus-flyway-baseline-on-named-ds-laptops

# Flyway config properties
quarkus.flyway.laptops.active=false
quarkus.flyway.laptops.migrate-at-start=true
quarkus.flyway.laptops.table=test_flyway_history
quarkus.flyway.laptops.baseline-on-migrate=true
quarkus.flyway.laptops.baseline-version=0.0.1
quarkus.flyway.laptops.baseline-description=Initial description for test
Original file line number Diff line number Diff line change
@@ -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:test-quarkus-migrate-at-start;DB_CLOSE_DELAY=-1

# Flyway config properties
quarkus.flyway.enabled=false
quarkus.flyway.migrate-at-start=true
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,16 @@ public FlywayDataSourceBuildTimeConfig getConfigForDataSourceName(String dataSou
return namedDataSources.getOrDefault(dataSourceName, FlywayDataSourceBuildTimeConfig.defaultConfig());
}

/**
* Whether Flyway is enabled *during the build*.
*
* If Flyway is disabled, the Flyway beans won't be created and Flyway won't be usable.
*
* @asciidoclet
*/
@ConfigItem(defaultValue = "true")
public boolean enabled;

/**
* Flyway configuration for the default datasource.
*/
Expand Down
Loading

0 comments on commit 3bf8a5a

Please sign in to comment.