Skip to content

Commit

Permalink
Use @ConfigMapping for configs (#87)
Browse files Browse the repository at this point in the history
  • Loading branch information
radcortez authored Jan 11, 2023
1 parent f95929f commit c12724c
Show file tree
Hide file tree
Showing 15 changed files with 317 additions and 440 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,15 @@

import org.jsoup.Connection.Response;

import io.quarkus.consul.config.runtime.ConsulConfigRecorder;
import io.quarkus.consul.config.runtime.ConsulConfigSourceFactoryBuilder;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.annotations.ExecutionTime;
import io.quarkus.deployment.annotations.Record;
import io.quarkus.deployment.builditem.ExtensionSslNativeSupportBuildItem;
import io.quarkus.deployment.builditem.FeatureBuildItem;
import io.quarkus.deployment.builditem.RunTimeConfigurationSourceValueBuildItem;
import io.quarkus.deployment.builditem.RunTimeConfigBuilderBuildItem;
import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem;

public class ConsulConfigProcessor {

private static final String FEATURE = "consul-config";

@BuildStep
Expand All @@ -32,10 +29,7 @@ public void registerForReflection(BuildProducer<ReflectiveClassBuildItem> reflec
}

@BuildStep
@Record(ExecutionTime.RUNTIME_INIT)
public RunTimeConfigurationSourceValueBuildItem configure(ConsulConfigRecorder recorder) {
return new RunTimeConfigurationSourceValueBuildItem(
recorder.configSources());
void consulConfigFactory(BuildProducer<RunTimeConfigBuilderBuildItem> runTimeConfigBuilder) {
runTimeConfigBuilder.produce(new RunTimeConfigBuilderBuildItem(ConsulConfigSourceFactoryBuilder.class.getName()));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,149 +3,132 @@
import java.net.InetSocketAddress;
import java.nio.file.Path;
import java.time.Duration;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import io.quarkus.runtime.annotations.ConfigGroup;
import io.quarkus.runtime.annotations.ConfigItem;
import io.quarkus.runtime.annotations.ConfigPhase;
import io.quarkus.runtime.annotations.ConfigRoot;
import io.quarkus.runtime.configuration.DurationConverter;
import io.quarkus.runtime.configuration.InetSocketAddressConverter;
import io.quarkus.runtime.configuration.PathConverter;
import io.smallrye.config.ConfigMapping;
import io.smallrye.config.WithConverter;
import io.smallrye.config.WithDefault;

@ConfigRoot(name = "consul-config", phase = ConfigPhase.BOOTSTRAP)
public class ConsulConfig {
@ConfigMapping(prefix = "quarkus.consul-config")
@ConfigRoot(phase = ConfigPhase.RUN_TIME)
public interface ConsulConfig {

/**
* If set to true, the application will attempt to look up the configuration from Consul
*/
@ConfigItem(defaultValue = "false")
boolean enabled;
@WithDefault("false")
boolean enabled();

/**
* Consul agent related configuration
*/
@ConfigItem
AgentConfig agent;
AgentConfig agent();

/**
* Common prefix that all keys share when looking up the keys from Consul.
* The prefix is <b>not</b> included in the keys of the user configuration
*/
@ConfigItem
Optional<String> prefix;
Optional<String> prefix();

/**
* Keys whose value is a raw string.
* When this is used, the keys that end up in the user configuration are the keys specified her with '/' replaced by '.'
*/
@ConfigItem
Optional<List<String>> rawValueKeys;
Optional<List<String>> rawValueKeys();

/**
* Keys whose value represents a properties file.
* When this is used, the keys that end up in the user configuration are the keys of the properties file,
* not these keys
*/
@ConfigItem
Optional<List<String>> propertiesValueKeys;
Optional<List<String>> propertiesValueKeys();

/**
* If set to true, the application will not start if any of the configured config sources cannot be located
*/
@ConfigItem(defaultValue = "true")
boolean failOnMissingKey;

Map<String, ValueType> keysAsMap() {
Map<String, ValueType> result = new LinkedHashMap<>();
if (rawValueKeys.isPresent()) {
for (String key : rawValueKeys.get()) {
result.put(key, ValueType.RAW);
}
}
if (propertiesValueKeys.isPresent()) {
for (String key : propertiesValueKeys.get()) {
result.put(key, ValueType.PROPERTIES);
}
}
return result;
}
@WithDefault("true")
boolean failOnMissingKey();

@ConfigGroup
public static class AgentConfig {
interface AgentConfig {

/**
* Consul agent host
*/
@ConfigItem(defaultValue = "localhost:8500")
InetSocketAddress hostPort;
@WithDefault("localhost:8500")
@WithConverter(InetSocketAddressConverter.class)
InetSocketAddress hostPort();

/**
* Whether or not to use HTTPS when communicating with the agent
*/
@ConfigItem(defaultValue = "false")
boolean useHttps;
@WithDefault("false")
boolean useHttps();

/**
* Consul token to be provided when authentication is enabled
*/
@ConfigItem
Optional<String> token;
Optional<String> token();

/**
* TrustStore to be used containing the SSL certificate used by Consul agent
* Can be either a classpath resource or a file system path
*/
@ConfigItem
public Optional<Path> trustStore;
@WithConverter(PathConverter.class)
Optional<Path> trustStore();

/**
* Password of TrustStore to be used containing the SSL certificate used by Consul agent
*/
@ConfigItem
public Optional<String> trustStorePassword;
Optional<String> trustStorePassword();

/**
* KeyStore to be used containing the SSL certificate for authentication with Consul agent
* Can be either a classpath resource or a file system path
*/
@ConfigItem
public Optional<Path> keyStore;
@WithConverter(PathConverter.class)
Optional<Path> keyStore();

/**
* Password of KeyStore to be used containing the SSL certificate for authentication with Consul agent
*/
@ConfigItem
public Optional<String> keyStorePassword;
Optional<String> keyStorePassword();

/**
* Password to recover key from KeyStore for SSL client authentication with Consul agent
* If no value is provided, the key-store-password will be used
*/
@ConfigItem
public Optional<String> keyPassword;
Optional<String> keyPassword();

/**
* When using HTTPS and no keyStore has been specified, whether or not to trust all certificates
*/
@ConfigItem(defaultValue = "false")
boolean trustCerts;
@WithDefault("false")
boolean trustCerts();

/**
* The amount of time to wait when initially establishing a connection before giving up and timing out.
* <p>
* Specify `0` to wait indefinitely.
*/
@ConfigItem(defaultValue = "10S")
public Duration connectionTimeout;
@WithDefault("10s")
@WithConverter(DurationConverter.class)
Duration connectionTimeout();

/**
* The amount of time to wait for a read on a socket before an exception is thrown.
* <p>
* Specify `0` to wait indefinitely.
*/
@ConfigItem(defaultValue = "60S")
public Duration readTimeout;
@WithDefault("60s")
@WithConverter(DurationConverter.class)
Duration readTimeout();
}

}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,46 +2,44 @@

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletionException;
import java.util.function.Consumer;

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

import io.smallrye.config.ConfigSourceContext;
import io.smallrye.config.ConfigSourceFactory;
import io.smallrye.mutiny.Uni;
import io.smallrye.mutiny.groups.UniAwait;

class ConsulConfigSourceProvider implements ConfigSourceProvider {
class ConsulConfigSourceFactory implements ConfigSourceFactory.ConfigurableConfigSourceFactory<ConsulConfig> {
private static final Logger log = Logger.getLogger(ConsulConfigSourceFactory.class);

private static final Logger log = Logger.getLogger(ConsulConfigSourceProvider.class);

private final ConsulConfig config;

private final ConsulConfigGateway consulConfigGateway;
private final ResponseConfigSourceUtil responseConfigSourceUtil;

public ConsulConfigSourceProvider(ConsulConfig config) {
this(config, new VertxConsulConfigGateway(config), new ResponseConfigSourceUtil());
}

// visible for testing
ConsulConfigSourceProvider(ConsulConfig config, ConsulConfigGateway consulConfigGateway) {
this(config, consulConfigGateway, new ResponseConfigSourceUtil());
}
@Override
public Iterable<ConfigSource> getConfigSources(final ConfigSourceContext context, final ConsulConfig config) {
if (!config.enabled()) {
return Collections.emptyList();
}

private ConsulConfigSourceProvider(ConsulConfig config, ConsulConfigGateway consulConfigGateway,
ResponseConfigSourceUtil responseConfigSourceUtil) {
this.config = config;
this.consulConfigGateway = consulConfigGateway;
this.responseConfigSourceUtil = responseConfigSourceUtil;
return getConfigSources(config, new VertxConsulConfigGateway(config));
}

@Override
public Iterable<ConfigSource> getConfigSources(ClassLoader cl) {
Map<String, ValueType> keys = config.keysAsMap();
Iterable<ConfigSource> getConfigSources(ConsulConfig config, ConsulConfigGateway consulConfigGateway) {
Map<String, ValueType> keys = new LinkedHashMap<>();
if (config.rawValueKeys().isPresent()) {
for (String key : config.rawValueKeys().get()) {
keys.put(key, ValueType.RAW);
}
}
if (config.propertiesValueKeys().isPresent()) {
for (String key : config.propertiesValueKeys().get()) {
keys.put(key, ValueType.PROPERTIES);
}
}
if (keys.isEmpty()) {
log.debug("No keys were configured for config source lookup");
return Collections.emptyList();
Expand All @@ -52,17 +50,15 @@ public Iterable<ConfigSource> getConfigSources(ClassLoader cl) {
List<Uni<?>> allUnis = new ArrayList<>();

for (Map.Entry<String, ValueType> entry : keys.entrySet()) {
String fullKey = config.prefix.isPresent() ? config.prefix.get() + "/" + entry.getKey() : entry.getKey();
String fullKey = config.prefix().isPresent() ? config.prefix().get() + "/" + entry.getKey() : entry.getKey();
allUnis.add(consulConfigGateway.getValue(fullKey).invoke(new Consumer<Response>() {
@Override
public void accept(Response response) {
if (response != null) {
result.add(
responseConfigSourceUtil.toConfigSource(response, entry.getValue(),
config.prefix));
result.add(ResponseConfigSourceUtil.toConfigSource(response, entry.getValue(), config.prefix()));
} else {
String message = "Key '" + fullKey + "' not found in Consul.";
if (config.failOnMissingKey) {
if (config.failOnMissingKey()) {
throw new RuntimeException(message);
} else {
log.info(message);
Expand All @@ -74,10 +70,10 @@ public void accept(Response response) {

try {
UniAwait<Void> await = Uni.combine().all().unis(allUnis).discardItems().await();
if (config.agent.connectionTimeout.isZero() && config.agent.readTimeout.isZero()) {
if (config.agent().connectionTimeout().isZero() && config.agent().readTimeout().isZero()) {
await.indefinitely();
} else {
await.atMost(config.agent.connectionTimeout.plus(config.agent.readTimeout.multipliedBy(2)));
await.atMost(config.agent().connectionTimeout().plus(config.agent().readTimeout().multipliedBy(2)));
}
} catch (CompletionException e) {
throw new RuntimeException("An error occurred while attempting to fetch configuration from Consul.", e);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package io.quarkus.consul.config.runtime;

import io.quarkus.runtime.configuration.ConfigBuilder;
import io.smallrye.config.SmallRyeConfigBuilder;

public class ConsulConfigSourceFactoryBuilder implements ConfigBuilder {
@Override
public SmallRyeConfigBuilder configBuilder(final SmallRyeConfigBuilder builder) {
return builder.withSources(new ConsulConfigSourceFactory());
}
}
Loading

0 comments on commit c12724c

Please sign in to comment.