mirror of
https://github.com/spring-projects/spring-boot.git
synced 2026-09-30 14:19:30 +00:00
Allow properties migrator to fail application startup
Add a new configuration property to prevent the application from starting when configuration keys that need to be migrated are found. With `on-error`, the application fails when keys that are no longer supported are found. With `on-warning`, it also fails when keys that have been renamed are found. The default, `never`, keeps the current behavior of only logging the report. See gh-51855 Signed-off-by: Hyunwoo Jung <hyunwoojung@kakao.com>
This commit is contained in:
committed by
Phillip Webb
parent
789e06631b
commit
a86aa43303
+1
@@ -106,6 +106,7 @@ public abstract class DocumentConfigurationProperties extends DefaultTask {
|
||||
config.accept("spring.ssl");
|
||||
config.accept("spring.task");
|
||||
config.accept("spring.threads");
|
||||
config.accept("spring.tools.properties-migrator");
|
||||
config.accept("spring.validation");
|
||||
config.accept("spring.mandatory-file-encoding");
|
||||
config.accept("info");
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
plugins {
|
||||
id "java"
|
||||
id "org.springframework.boot.configuration-metadata"
|
||||
id "org.springframework.boot.deployed"
|
||||
}
|
||||
|
||||
|
||||
+49
-5
@@ -18,6 +18,7 @@ package org.springframework.boot.context.properties.migrator;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
@@ -29,6 +30,7 @@ import org.springframework.boot.context.event.ApplicationFailedEvent;
|
||||
import org.springframework.boot.context.event.ApplicationPreparedEvent;
|
||||
import org.springframework.boot.context.event.ApplicationReadyEvent;
|
||||
import org.springframework.boot.context.event.SpringApplicationEvent;
|
||||
import org.springframework.boot.context.properties.bind.Binder;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.io.Resource;
|
||||
@@ -37,14 +39,18 @@ import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
||||
/**
|
||||
* An {@link ApplicationListener} that inspects the {@link ConfigurableEnvironment
|
||||
* environment} for configuration keys that need to be migrated. Automatically renames the
|
||||
* keys that have a matching replacement and logs a report of what was discovered.
|
||||
* keys that have a matching replacement and logs a report of what was discovered. Can
|
||||
* optionally fail application startup when such keys are found.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Hyunwoo Jung
|
||||
*/
|
||||
class PropertiesMigrationListener implements ApplicationListener<SpringApplicationEvent> {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(PropertiesMigrationListener.class);
|
||||
|
||||
private static final String FAIL_PROPERTY = "spring.tools.properties-migrator.fail";
|
||||
|
||||
private @Nullable PropertiesMigrationReport report;
|
||||
|
||||
private boolean reported;
|
||||
@@ -60,10 +66,11 @@ class PropertiesMigrationListener implements ApplicationListener<SpringApplicati
|
||||
}
|
||||
|
||||
private void onApplicationPreparedEvent(ApplicationPreparedEvent event) {
|
||||
ConfigurationMetadataRepository repository = loadRepository();
|
||||
PropertiesMigrationReporter reporter = new PropertiesMigrationReporter(repository,
|
||||
event.getApplicationContext().getEnvironment());
|
||||
this.report = reporter.getReport();
|
||||
ConfigurableEnvironment environment = event.getApplicationContext().getEnvironment();
|
||||
PropertiesMigrationReport report = new PropertiesMigrationReporter(loadRepository(), environment).getReport();
|
||||
this.report = report;
|
||||
Fail fail = Binder.get(environment).bind(FAIL_PROPERTY, Fail.class).orElse(Fail.NEVER);
|
||||
failIfNecessary(report, fail);
|
||||
}
|
||||
|
||||
private ConfigurationMetadataRepository loadRepository() {
|
||||
@@ -102,4 +109,41 @@ class PropertiesMigrationListener implements ApplicationListener<SpringApplicati
|
||||
this.reported = true;
|
||||
}
|
||||
|
||||
private void failIfNecessary(PropertiesMigrationReport report, Fail fail) {
|
||||
if (shouldFail(report, fail)) {
|
||||
throw new IllegalStateException("Found configuration keys that need to be migrated (%s=%s)"
|
||||
.formatted(FAIL_PROPERTY, fail.name().toLowerCase(Locale.ROOT).replace('_', '-')));
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean shouldFail(PropertiesMigrationReport report, Fail fail) {
|
||||
return switch (fail) {
|
||||
case NEVER -> false;
|
||||
case ON_ERROR -> report.getErrorReport() != null;
|
||||
case ON_WARNING -> report.getErrorReport() != null || report.getWarningReport() != null;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Conditions under which property migration should cause application startup to fail.
|
||||
*/
|
||||
enum Fail {
|
||||
|
||||
/**
|
||||
* Do not fail application startup due to migration warnings or errors.
|
||||
*/
|
||||
NEVER,
|
||||
|
||||
/**
|
||||
* Fail application startup if the migration report contains errors.
|
||||
*/
|
||||
ON_ERROR,
|
||||
|
||||
/**
|
||||
* Fail application startup if the migration report contains warnings or errors.
|
||||
*/
|
||||
ON_WARNING
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"properties": [
|
||||
{
|
||||
"name": "spring.tools.properties-migrator.fail",
|
||||
"type": "org.springframework.boot.context.properties.migrator.PropertiesMigrationListener$Fail",
|
||||
"description": "When the application should fail to start because of configuration keys that need to be migrated.",
|
||||
"sourceType": "org.springframework.boot.context.properties.migrator.PropertiesMigrationListener",
|
||||
"defaultValue": "never"
|
||||
}
|
||||
]
|
||||
}
|
||||
+39
@@ -28,11 +28,14 @@ import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
import static org.assertj.core.api.Assertions.assertThatNoException;
|
||||
|
||||
/**
|
||||
* Tests for {@link PropertiesMigrationListener}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Hyunwoo Jung
|
||||
*/
|
||||
@ExtendWith(OutputCaptureExtension.class)
|
||||
class PropertiesMigrationListenerTests {
|
||||
@@ -55,6 +58,42 @@ class PropertiesMigrationListenerTests {
|
||||
.doesNotContain("Please refer to the release notes");
|
||||
}
|
||||
|
||||
@Test
|
||||
void failNeverWithRenamedKeyStarts() {
|
||||
assertThatNoException().isThrownBy(() -> this.context = createSampleApplication()
|
||||
.run("--spring.tools.properties-migrator.fail=never", "--logging.file=test.log"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void failNeverWithUnsupportedKeyStarts() {
|
||||
assertThatNoException().isThrownBy(() -> this.context = createSampleApplication()
|
||||
.run("--spring.tools.properties-migrator.fail=never", "--spring.banner.image.width=10"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void failOnErrorWithRenamedKeyStarts() {
|
||||
assertThatNoException().isThrownBy(() -> this.context = createSampleApplication()
|
||||
.run("--spring.tools.properties-migrator.fail=on-error", "--logging.file=test.log"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void failOnErrorWithUnsupportedKeyFails() {
|
||||
assertThatIllegalStateException().isThrownBy(() -> createSampleApplication()
|
||||
.run("--spring.tools.properties-migrator.fail=on-error", "--spring.banner.image.width=10"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void failOnWarningWithRenamedKeyFails() {
|
||||
assertThatIllegalStateException().isThrownBy(() -> createSampleApplication()
|
||||
.run("--spring.tools.properties-migrator.fail=on-warning", "--logging.file=test.log"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void failOnWarningWithUnsupportedKeyFails() {
|
||||
assertThatIllegalStateException().isThrownBy(() -> createSampleApplication()
|
||||
.run("--spring.tools.properties-migrator.fail=on-warning", "--spring.banner.image.width=10"));
|
||||
}
|
||||
|
||||
private SpringApplication createSampleApplication() {
|
||||
return new SpringApplication(TestApplication.class);
|
||||
}
|
||||
|
||||
@@ -49,6 +49,14 @@ To enable that feature, add the following dependency to your project:
|
||||
</dependency>
|
||||
----
|
||||
|
||||
The configprop:spring.tools.properties-migrator.fail[] property controls whether migration warnings or errors cause the application to fail to start:
|
||||
|
||||
* `never`: do not fail application startup because of migration warnings or errors (default).
|
||||
* `on-error`: fail when properties that are no longer supported are found.
|
||||
* `on-warning`: fail when properties that have been renamed or are no longer supported are found.
|
||||
|
||||
Migration diagnostics are logged regardless of this setting.
|
||||
|
||||
WARNING: Properties that are added late to the environment, such as when using javadoc:org.springframework.context.annotation.PropertySource[format=annotation], will not be taken into account.
|
||||
|
||||
NOTE: Once you finish the migration, please make sure to remove this module from your project's dependencies.
|
||||
|
||||
Reference in New Issue
Block a user