Skip to content

Commit

Permalink
Merge pull request #35298 from radcortez/fix-13227
Browse files Browse the repository at this point in the history
Warn about unknown application config files
  • Loading branch information
gsmet committed Oct 5, 2023
2 parents e131148 + f73f1f7 commit b71b1e0
Show file tree
Hide file tree
Showing 4 changed files with 162 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.annotations.ExecutionTime;
import io.quarkus.deployment.annotations.Record;
import io.quarkus.deployment.builditem.ApplicationArchivesBuildItem;
import io.quarkus.deployment.builditem.CombinedIndexBuildItem;
import io.quarkus.deployment.builditem.ConfigClassBuildItem;
import io.quarkus.deployment.builditem.ConfigMappingBuildItem;
Expand Down Expand Up @@ -69,6 +70,7 @@
import io.quarkus.gizmo.MethodCreator;
import io.quarkus.gizmo.MethodDescriptor;
import io.quarkus.gizmo.ResultHandle;
import io.quarkus.paths.PathCollection;
import io.quarkus.runtime.LaunchMode;
import io.quarkus.runtime.annotations.StaticInitSafe;
import io.quarkus.runtime.configuration.ConfigBuilder;
Expand Down Expand Up @@ -267,8 +269,11 @@ void generateConfigClass(
List<RunTimeConfigBuilderBuildItem> runTimeConfigBuilders)
throws IOException {

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

if (liveReloadBuildItem.isLiveReload()) {
return;
Expand Down Expand Up @@ -320,14 +325,6 @@ void generateConfigClass(
.run();
}

private static void reportUnknownBuildProperties(LaunchMode launchMode, Set<String> unknownBuildProperties) {
// So it only reports during the build, because it is very likely that the property is available in runtime
// and, it will be caught by the RuntimeConfig and log double warnings
if (!launchMode.isDevOrTest()) {
ConfigDiagnostic.unknownProperties(unknownBuildProperties);
}
}

@BuildStep
public void suppressNonRuntimeConfigChanged(
BuildProducer<SuppressNonRuntimeConfigChangedWarningBuildItem> suppressNonRuntimeConfigChanged) {
Expand Down Expand Up @@ -441,6 +438,31 @@ public void watchConfigFiles(BuildProducer<HotDeploymentWatchedFileBuildItem> wa
}
}

@BuildStep
@Record(ExecutionTime.RUNTIME_INIT)
void unknownConfigFiles(
ApplicationArchivesBuildItem applicationArchives,
LaunchModeBuildItem launchModeBuildItem,
ConfigRecorder configRecorder) throws Exception {

PathCollection rootDirectories = applicationArchives.getRootArchive().getRootDirectories();
if (!rootDirectories.isSinglePath()) {
return;
}

Set<String> buildTimeFiles = new HashSet<>();
buildTimeFiles.addAll(ConfigDiagnostic.configFiles(rootDirectories.getSinglePath()));
buildTimeFiles.addAll(ConfigDiagnostic.configFilesFromLocations());

// Report always at build time since config folder and locations may differ from build to runtime
ConfigDiagnostic.unknownConfigFiles(buildTimeFiles);

// No need to include the application files, because they don't change
if (!launchModeBuildItem.getLaunchMode().isDevOrTest()) {
configRecorder.unknownConfigFiles();
}
}

@BuildStep(onlyIf = NativeOrNativeSourcesBuild.class)
@Record(ExecutionTime.RUNTIME_INIT)
void warnDifferentProfileUsedBetweenBuildAndRunTime(ConfigRecorder configRecorder) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,30 @@
package io.quarkus.runtime.configuration;

import static io.smallrye.config.SmallRyeConfig.SMALLRYE_CONFIG_LOCATIONS;

import java.io.IOException;
import java.net.URI;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.function.Consumer;

import org.eclipse.microprofile.config.Config;
import org.eclipse.microprofile.config.ConfigProvider;
import org.eclipse.microprofile.config.spi.ConfigSource;
import org.jboss.logging.Logger;

import io.quarkus.runtime.ImageMode;
import io.smallrye.config.SmallRyeConfig;
import io.smallrye.config.common.utils.StringUtil;

/**
Expand Down Expand Up @@ -152,4 +165,79 @@ public static String getNiceErrorMessage() {
public static Set<String> getErrorKeys() {
return new HashSet<>(errorKeys);
}

private static final DirectoryStream.Filter<Path> CONFIG_FILES_FILTER = new DirectoryStream.Filter<>() {
@Override
public boolean accept(final Path entry) {
// Ignore .properties, because we know these are have a default loader in core
// Ignore profile files. The loading rules require the main file to be present, so we only need the type
String filename = entry.getFileName().toString();
return Files.isRegularFile(entry) && filename.startsWith("application.") && !filename.endsWith(".properties");
}
};

public static Set<String> configFiles(Path configFilesLocation) throws IOException {
if (!Files.exists(configFilesLocation)) {
return Collections.emptySet();
}

Set<String> configFiles = new HashSet<>();
try (DirectoryStream<Path> candidates = Files.newDirectoryStream(configFilesLocation, CONFIG_FILES_FILTER)) {
for (Path candidate : candidates) {
configFiles.add(candidate.toString());
}
}
return configFiles;
}

public static Set<String> configFilesFromLocations() throws Exception {
SmallRyeConfig config = ConfigProvider.getConfig().unwrap(SmallRyeConfig.class);

Set<String> configFiles = new HashSet<>();
configFiles.addAll(configFiles(Paths.get(System.getProperty("user.dir"), "config")));
Optional<List<URI>> optionalLocations = config.getOptionalValues(SMALLRYE_CONFIG_LOCATIONS, URI.class);
optionalLocations.ifPresent(new Consumer<List<URI>>() {
@Override
public void accept(final List<URI> locations) {
for (URI location : locations) {
Path path = location.getScheme() != null && location.getScheme().equals("file") ? Paths.get(location)
: Paths.get(location.getPath());
if (Files.isDirectory(path)) {
try {
configFiles.addAll(configFiles(path));
} catch (IOException e) {
// Ignore
}
}
}
}
});

return configFiles;
}

public static void unknownConfigFiles(final Set<String> configFiles) {
SmallRyeConfig config = ConfigProvider.getConfig().unwrap(SmallRyeConfig.class);
Set<String> configNames = new HashSet<>();
for (ConfigSource configSource : config.getConfigSources()) {
if (configSource.getName() != null && configSource.getName().contains("application")) {
configNames.add(configSource.getName());
}
}

for (String configFile : configFiles) {
boolean found = false;
for (String configName : configNames) {
if (configName.contains(configFile)) {
found = true;
break;
}
}
if (!found) {
log.warnf(
"Unrecognized configuration file %s found; Please, check if your are providing the proper extension to load the file",
configFile);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -97,4 +97,8 @@ public void handleNativeProfileChange(List<String> buildProfiles) {
}
}
}

public void unknownConfigFiles() throws Exception {
ConfigDiagnostic.unknownConfigFiles(ConfigDiagnostic.configFilesFromLocations());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package io.quarkus.config;

import static io.smallrye.common.constraint.Assert.assertTrue;
import static org.junit.jupiter.api.Assertions.assertEquals;

import java.util.List;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.stream.Collectors;

import org.jboss.shrinkwrap.api.asset.EmptyAsset;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.test.QuarkusUnitTest;

class UnknownConfigFilesTest {
@RegisterExtension
static final QuarkusUnitTest TEST = new QuarkusUnitTest()
.withApplicationRoot((jar) -> jar
.addAsResource(EmptyAsset.INSTANCE, "application.properties")
.addAsResource(EmptyAsset.INSTANCE, "application-prod.properties")
.addAsResource(EmptyAsset.INSTANCE, "application.yaml"))
.setLogRecordPredicate(record -> record.getLevel().intValue() >= Level.WARNING.intValue())
.assertLogRecords(logRecords -> {
List<LogRecord> unknownConfigFiles = logRecords.stream()
.filter(l -> l.getMessage().startsWith("Unrecognized configuration file"))
.collect(Collectors.toList());

assertEquals(1, unknownConfigFiles.size());
assertTrue(unknownConfigFiles.get(0).getParameters()[0].toString().contains("application.yaml"));
});

@Test
void unknownConfigFiles() {

}
}

0 comments on commit b71b1e0

Please sign in to comment.