Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rework how to enable/activate Flyway #35732

Merged
merged 5 commits into from
Sep 19, 2023
Merged
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
@@ -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