Skip to content

Commit f73f1f7

Browse files
radcortezgsmet
authored andcommitted
Warn about unknown application config files
1 parent 68c2af1 commit f73f1f7

File tree

4 files changed

+162
-10
lines changed

4 files changed

+162
-10
lines changed

core/deployment/src/main/java/io/quarkus/deployment/steps/ConfigGenerationBuildStep.java

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import io.quarkus.deployment.annotations.BuildStep;
4141
import io.quarkus.deployment.annotations.ExecutionTime;
4242
import io.quarkus.deployment.annotations.Record;
43+
import io.quarkus.deployment.builditem.ApplicationArchivesBuildItem;
4344
import io.quarkus.deployment.builditem.CombinedIndexBuildItem;
4445
import io.quarkus.deployment.builditem.ConfigClassBuildItem;
4546
import io.quarkus.deployment.builditem.ConfigMappingBuildItem;
@@ -69,6 +70,7 @@
6970
import io.quarkus.gizmo.MethodCreator;
7071
import io.quarkus.gizmo.MethodDescriptor;
7172
import io.quarkus.gizmo.ResultHandle;
73+
import io.quarkus.paths.PathCollection;
7274
import io.quarkus.runtime.LaunchMode;
7375
import io.quarkus.runtime.annotations.StaticInitSafe;
7476
import io.quarkus.runtime.configuration.ConfigBuilder;
@@ -267,8 +269,11 @@ void generateConfigClass(
267269
List<RunTimeConfigBuilderBuildItem> runTimeConfigBuilders)
268270
throws IOException {
269271

270-
reportUnknownBuildProperties(launchModeBuildItem.getLaunchMode(),
271-
configItem.getReadResult().getUnknownBuildProperties());
272+
// So it only reports during the build, because it is very likely that the property is available in runtime
273+
// and, it will be caught by the RuntimeConfig and log double warnings
274+
if (!launchModeBuildItem.getLaunchMode().isDevOrTest()) {
275+
ConfigDiagnostic.unknownProperties(configItem.getReadResult().getUnknownBuildProperties());
276+
}
272277

273278
if (liveReloadBuildItem.isLiveReload()) {
274279
return;
@@ -320,14 +325,6 @@ void generateConfigClass(
320325
.run();
321326
}
322327

323-
private static void reportUnknownBuildProperties(LaunchMode launchMode, Set<String> unknownBuildProperties) {
324-
// So it only reports during the build, because it is very likely that the property is available in runtime
325-
// and, it will be caught by the RuntimeConfig and log double warnings
326-
if (!launchMode.isDevOrTest()) {
327-
ConfigDiagnostic.unknownProperties(unknownBuildProperties);
328-
}
329-
}
330-
331328
@BuildStep
332329
public void suppressNonRuntimeConfigChanged(
333330
BuildProducer<SuppressNonRuntimeConfigChangedWarningBuildItem> suppressNonRuntimeConfigChanged) {
@@ -441,6 +438,31 @@ public void watchConfigFiles(BuildProducer<HotDeploymentWatchedFileBuildItem> wa
441438
}
442439
}
443440

441+
@BuildStep
442+
@Record(ExecutionTime.RUNTIME_INIT)
443+
void unknownConfigFiles(
444+
ApplicationArchivesBuildItem applicationArchives,
445+
LaunchModeBuildItem launchModeBuildItem,
446+
ConfigRecorder configRecorder) throws Exception {
447+
448+
PathCollection rootDirectories = applicationArchives.getRootArchive().getRootDirectories();
449+
if (!rootDirectories.isSinglePath()) {
450+
return;
451+
}
452+
453+
Set<String> buildTimeFiles = new HashSet<>();
454+
buildTimeFiles.addAll(ConfigDiagnostic.configFiles(rootDirectories.getSinglePath()));
455+
buildTimeFiles.addAll(ConfigDiagnostic.configFilesFromLocations());
456+
457+
// Report always at build time since config folder and locations may differ from build to runtime
458+
ConfigDiagnostic.unknownConfigFiles(buildTimeFiles);
459+
460+
// No need to include the application files, because they don't change
461+
if (!launchModeBuildItem.getLaunchMode().isDevOrTest()) {
462+
configRecorder.unknownConfigFiles();
463+
}
464+
}
465+
444466
@BuildStep(onlyIf = NativeOrNativeSourcesBuild.class)
445467
@Record(ExecutionTime.RUNTIME_INIT)
446468
void warnDifferentProfileUsedBetweenBuildAndRunTime(ConfigRecorder configRecorder) {

core/runtime/src/main/java/io/quarkus/runtime/configuration/ConfigDiagnostic.java

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,30 @@
11
package io.quarkus.runtime.configuration;
22

3+
import static io.smallrye.config.SmallRyeConfig.SMALLRYE_CONFIG_LOCATIONS;
4+
5+
import java.io.IOException;
6+
import java.net.URI;
7+
import java.nio.file.DirectoryStream;
8+
import java.nio.file.Files;
9+
import java.nio.file.Path;
10+
import java.nio.file.Paths;
11+
import java.util.Collections;
312
import java.util.HashSet;
413
import java.util.List;
514
import java.util.NoSuchElementException;
15+
import java.util.Optional;
616
import java.util.Set;
717
import java.util.concurrent.CopyOnWriteArrayList;
818
import java.util.concurrent.CopyOnWriteArraySet;
19+
import java.util.function.Consumer;
920

1021
import org.eclipse.microprofile.config.Config;
1122
import org.eclipse.microprofile.config.ConfigProvider;
23+
import org.eclipse.microprofile.config.spi.ConfigSource;
1224
import org.jboss.logging.Logger;
1325

1426
import io.quarkus.runtime.ImageMode;
27+
import io.smallrye.config.SmallRyeConfig;
1528
import io.smallrye.config.common.utils.StringUtil;
1629

1730
/**
@@ -152,4 +165,79 @@ public static String getNiceErrorMessage() {
152165
public static Set<String> getErrorKeys() {
153166
return new HashSet<>(errorKeys);
154167
}
168+
169+
private static final DirectoryStream.Filter<Path> CONFIG_FILES_FILTER = new DirectoryStream.Filter<>() {
170+
@Override
171+
public boolean accept(final Path entry) {
172+
// Ignore .properties, because we know these are have a default loader in core
173+
// Ignore profile files. The loading rules require the main file to be present, so we only need the type
174+
String filename = entry.getFileName().toString();
175+
return Files.isRegularFile(entry) && filename.startsWith("application.") && !filename.endsWith(".properties");
176+
}
177+
};
178+
179+
public static Set<String> configFiles(Path configFilesLocation) throws IOException {
180+
if (!Files.exists(configFilesLocation)) {
181+
return Collections.emptySet();
182+
}
183+
184+
Set<String> configFiles = new HashSet<>();
185+
try (DirectoryStream<Path> candidates = Files.newDirectoryStream(configFilesLocation, CONFIG_FILES_FILTER)) {
186+
for (Path candidate : candidates) {
187+
configFiles.add(candidate.toString());
188+
}
189+
}
190+
return configFiles;
191+
}
192+
193+
public static Set<String> configFilesFromLocations() throws Exception {
194+
SmallRyeConfig config = ConfigProvider.getConfig().unwrap(SmallRyeConfig.class);
195+
196+
Set<String> configFiles = new HashSet<>();
197+
configFiles.addAll(configFiles(Paths.get(System.getProperty("user.dir"), "config")));
198+
Optional<List<URI>> optionalLocations = config.getOptionalValues(SMALLRYE_CONFIG_LOCATIONS, URI.class);
199+
optionalLocations.ifPresent(new Consumer<List<URI>>() {
200+
@Override
201+
public void accept(final List<URI> locations) {
202+
for (URI location : locations) {
203+
Path path = location.getScheme() != null && location.getScheme().equals("file") ? Paths.get(location)
204+
: Paths.get(location.getPath());
205+
if (Files.isDirectory(path)) {
206+
try {
207+
configFiles.addAll(configFiles(path));
208+
} catch (IOException e) {
209+
// Ignore
210+
}
211+
}
212+
}
213+
}
214+
});
215+
216+
return configFiles;
217+
}
218+
219+
public static void unknownConfigFiles(final Set<String> configFiles) {
220+
SmallRyeConfig config = ConfigProvider.getConfig().unwrap(SmallRyeConfig.class);
221+
Set<String> configNames = new HashSet<>();
222+
for (ConfigSource configSource : config.getConfigSources()) {
223+
if (configSource.getName() != null && configSource.getName().contains("application")) {
224+
configNames.add(configSource.getName());
225+
}
226+
}
227+
228+
for (String configFile : configFiles) {
229+
boolean found = false;
230+
for (String configName : configNames) {
231+
if (configName.contains(configFile)) {
232+
found = true;
233+
break;
234+
}
235+
}
236+
if (!found) {
237+
log.warnf(
238+
"Unrecognized configuration file %s found; Please, check if your are providing the proper extension to load the file",
239+
configFile);
240+
}
241+
}
242+
}
155243
}

core/runtime/src/main/java/io/quarkus/runtime/configuration/ConfigRecorder.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,4 +97,8 @@ public void handleNativeProfileChange(List<String> buildProfiles) {
9797
}
9898
}
9999
}
100+
101+
public void unknownConfigFiles() throws Exception {
102+
ConfigDiagnostic.unknownConfigFiles(ConfigDiagnostic.configFilesFromLocations());
103+
}
100104
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package io.quarkus.config;
2+
3+
import static io.smallrye.common.constraint.Assert.assertTrue;
4+
import static org.junit.jupiter.api.Assertions.assertEquals;
5+
6+
import java.util.List;
7+
import java.util.logging.Level;
8+
import java.util.logging.LogRecord;
9+
import java.util.stream.Collectors;
10+
11+
import org.jboss.shrinkwrap.api.asset.EmptyAsset;
12+
import org.junit.jupiter.api.Test;
13+
import org.junit.jupiter.api.extension.RegisterExtension;
14+
15+
import io.quarkus.test.QuarkusUnitTest;
16+
17+
class UnknownConfigFilesTest {
18+
@RegisterExtension
19+
static final QuarkusUnitTest TEST = new QuarkusUnitTest()
20+
.withApplicationRoot((jar) -> jar
21+
.addAsResource(EmptyAsset.INSTANCE, "application.properties")
22+
.addAsResource(EmptyAsset.INSTANCE, "application-prod.properties")
23+
.addAsResource(EmptyAsset.INSTANCE, "application.yaml"))
24+
.setLogRecordPredicate(record -> record.getLevel().intValue() >= Level.WARNING.intValue())
25+
.assertLogRecords(logRecords -> {
26+
List<LogRecord> unknownConfigFiles = logRecords.stream()
27+
.filter(l -> l.getMessage().startsWith("Unrecognized configuration file"))
28+
.collect(Collectors.toList());
29+
30+
assertEquals(1, unknownConfigFiles.size());
31+
assertTrue(unknownConfigFiles.get(0).getParameters()[0].toString().contains("application.yaml"));
32+
});
33+
34+
@Test
35+
void unknownConfigFiles() {
36+
37+
}
38+
}

0 commit comments

Comments
 (0)