[KEYCLOAK-14255] - More improvements to CLI

This commit is contained in:
Pedro Igor 2020-09-24 19:53:14 -03:00 committed by Marek Posolda
parent 6596811d5d
commit 04415d34ea
9 changed files with 132 additions and 61 deletions

View file

@ -46,7 +46,7 @@ do
break
;;
*)
if [[ $1 = --* || ! $1 =~ ^-.* ]]; then
if [[ $1 = -* || ! $1 =~ ^-.* ]]; then
CONFIG_ARGS="$CONFIG_ARGS $1"
else
SERVER_OPTS="$SERVER_OPTS $1"

View file

@ -151,7 +151,7 @@ class KeycloakProcessor {
}
}
recorder.setBuildTimeProperties(properties, Environment.isRebuild(), KeycloakRecorder.getConfig().getRawValue("kc.config.args"));
recorder.validateAndSetBuildTimeProperties(properties, Environment.isRebuild(), KeycloakRecorder.getConfig().getRawValue("kc.config.args"));
recorder.showConfig();
}

View file

@ -38,6 +38,17 @@ public class KeycloakMain {
if (args.length != 0) {
CommandLine cmd = new CommandLine(createCommandSpec());
cmd.setExecutionExceptionHandler(new CommandLine.IExecutionExceptionHandler() {
@Override
public int handleExecutionException(Exception ex, CommandLine commandLine,
CommandLine.ParseResult parseResult) {
commandLine.getErr().println(ex.getMessage());
commandLine.usage(commandLine.getErr());
return commandLine.getCommandSpec().exitCodeOnExecutionException();
}
});
List<String> argsList = new LinkedList<>(Arrays.asList(args));
try {
@ -48,12 +59,15 @@ public class KeycloakMain {
argsList.add(0, "start");
}
} catch (CommandLine.UnmatchedArgumentException e) {
if (!cmd.getParseResult().hasSubcommand()) {
if (!cmd.getParseResult().hasSubcommand() && argsList.get(0).startsWith("--")) {
argsList.add(0, "start");
} else {
cmd.getErr().println(e.getMessage());
System.exit(CommandLine.ExitCode.SOFTWARE);
}
} catch (Exception e) {
cmd.getErr().println(e.getMessage());
System.exit(CommandLine.ExitCode.SOFTWARE);
}
System.exit(cmd.execute(argsList.toArray(new String[argsList.size()])));

View file

@ -44,15 +44,16 @@ public class MainCommand {
@Spec
CommandSpec spec;
@Option(names = { "--help" }, usageHelp = true, hidden = true)
@Option(names = { "--help" }, description = "This help message.", usageHelp = true)
boolean help;
@Option(names = { "--version" }, versionHelp = true, hidden = true)
@Option(names = { "--version" }, description = "Show version information", versionHelp = true)
boolean version;
@Option(names = "--profile", arity = "1", description = "Set the profile. Use 'dev' profile to enable development mode.", scope = CommandLine.ScopeType.INHERIT)
public void setProfile(String profile) {
System.setProperty("kc.profile", profile);
System.setProperty("kc.profile", "dev");
System.setProperty("quarkus.profile", "dev");
}
@Option(names = "--config-file", arity = "1", description = "Set the path to a configuration file.", paramLabel = "<path>", scope = CommandLine.ScopeType.INHERIT)
@ -85,6 +86,7 @@ public class MainCommand {
parameterListHeading = "Available Commands%n")
public void startDev() {
System.setProperty("kc.profile", "dev");
System.setProperty("quarkus.profile", "dev");
start();
}
@ -162,9 +164,11 @@ public class MainCommand {
public void start(
@CommandLine.Parameters(paramLabel = "show-config", arity = "0..1",
description = "Print out the configuration options when starting the server.") String showConfig) {
if (showConfig != null) {
if ("show-config".equals(showConfig)) {
System.setProperty("kc.show.config.runtime", Boolean.TRUE.toString());
System.setProperty("kc.show.config", "all");
} else if (showConfig != null) {
throw new CommandLine.UnmatchedArgumentException(spec.commandLine(), "Invalid argument: " + showConfig);
}
start();
}

View file

@ -50,19 +50,23 @@ public final class ShowConfigCommand {
.forEachOrdered(ShowConfigCommand::printProperty);
if (configArgs.equalsIgnoreCase("all")) {
properties.get("%").stream()
.sorted()
.collect(Collectors.groupingBy(s -> s.substring(1, s.indexOf('.'))))
.forEach((p, properties1) -> {
if (p.equals(profile)) {
System.out.printf("Profile \"%s\" Configuration (%s):%n", p,
p.equals(profile) ? "current" : "");
} else {
System.out.printf("Profile \"%s\" Configuration:%n", p);
}
Set<String> profiles = properties.get("%");
if (profiles != null) {
profiles.stream()
.sorted()
.collect(Collectors.groupingBy(s -> s.substring(1, s.indexOf('.'))))
.forEach((p, properties1) -> {
if (p.equals(profile)) {
System.out.printf("Profile \"%s\" Configuration (%s):%n", p,
p.equals(profile) ? "current" : "");
} else {
System.out.printf("Profile \"%s\" Configuration:%n", p);
}
properties1.stream().sorted().forEachOrdered(ShowConfigCommand::printProperty);
});
properties1.stream().sorted().forEachOrdered(ShowConfigCommand::printProperty);
});
}
System.out.println("Quarkus Configuration:");
properties.get(MicroProfileConfigProvider.NS_QUARKUS).stream().sorted()

View file

@ -98,15 +98,11 @@ public class ConfigArgsConfigSource extends PropertiesConfigSource {
String value;
if (keyValue.length == 1) {
// the argument does not have a value because the key by itself already has a meaning
value = Boolean.TRUE.toString();
} else if (keyValue.length == 2) {
if (keyValue.length == 2) {
// the argument has a simple value. Eg.: key=pair
value = keyValue[1];
} else {
// the argument is multivalued. eg.: key=kv1=kv2
value = arg.substring(key.length() + 1);
continue;
}
key = NS_KEYCLOAK_PREFIX + key.substring(2);

View file

@ -26,6 +26,7 @@ import java.util.function.BiFunction;
import io.smallrye.config.ConfigSourceInterceptorContext;
import io.smallrye.config.ConfigValue;
import org.keycloak.quarkus.KeycloakRecorder;
public class PropertyMapper {
@ -60,10 +61,9 @@ public class PropertyMapper {
public ConfigValue getOrDefault(String name, ConfigSourceInterceptorContext context, ConfigValue current) {
if (current == null) {
ConfigValue.builder().withName(name)
.withValue(getBuiltTimeProperty(
NS_KEYCLOAK_PREFIX + name.substring(NS_KEYCLOAK_PREFIX.length()).replaceAll("\\.", "-"))
.orElseGet(() -> getBuiltTimeProperty(name).orElse(null)))
.build();
.withValue(getBuiltTimeProperty(PropertyMappers.toCLIFormat(name))
.orElseGet(() -> getBuiltTimeProperty(name)
.orElse(null))).build();
}
return current;
@ -161,8 +161,8 @@ public class PropertyMapper {
}
public Optional<ConfigValue> getBuiltTimeConfig(String name, ConfigSourceInterceptorContext context) {
ConfigValue value = transformValue(getBuiltTimeProperty(name).orElseGet(() -> getBuiltTimeProperty(
NS_KEYCLOAK_PREFIX + name.substring(NS_KEYCLOAK_PREFIX.length()).replaceAll("\\.", "-")).orElse(null)), context);
ConfigValue value = transformValue(getBuiltTimeProperty(name)
.orElseGet(() -> getBuiltTimeProperty(PropertyMappers.toCLIFormat(name)).orElse(null)), context);
if (value == null) {
return Optional.empty();

View file

@ -25,6 +25,7 @@ import java.util.Map;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import com.google.common.base.Ascii;
import io.quarkus.runtime.configuration.ProfileManager;
import io.smallrye.config.ConfigSourceInterceptorContext;
import io.smallrye.config.ConfigValue;
@ -165,4 +166,8 @@ public final class PropertyMappers {
return PropertyMapper.MAPPERS.values().stream()
.filter(entry -> entry.isBuildTime()).collect(Collectors.toList());
}
public static String canonicalFormat(String name) {
return name.replaceAll("-", "\\.");
}
}

View file

@ -20,7 +20,9 @@ package org.keycloak.quarkus;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.StreamSupport;
import io.smallrye.config.ConfigValue;
import org.jboss.logging.Logger;
@ -28,6 +30,7 @@ import org.keycloak.QuarkusKeycloakSessionFactory;
import org.keycloak.cli.ShowConfigCommand;
import org.keycloak.common.Profile;
import org.keycloak.configuration.MicroProfileConfigProvider;
import org.keycloak.configuration.PropertyMapper;
import org.keycloak.configuration.PropertyMappers;
import org.keycloak.connections.liquibase.FastServiceLocator;
import org.keycloak.connections.liquibase.KeycloakLogger;
@ -106,57 +109,102 @@ public class KeycloakRecorder {
QuarkusKeycloakSessionFactory.setInstance(new QuarkusKeycloakSessionFactory(factories, defaultProviders, reaugmented));
}
public void setBuildTimeProperties(Map<String, String> buildTimeProperties, Boolean rebuild, String configArgs) {
/**
* <p>Validate the build time properties with any property passed during runtime in order to advertise any difference with the
* server image state.
*
* <p>This method also keep the build time properties available at runtime.
*
*
* @param buildTimeProperties the build time properties set when running the last re-augmentation
* @param rebuild indicates whether or not the server was re-augmented
* @param configArgs the configuration args if provided when the server was re-augmented
*/
public void validateAndSetBuildTimeProperties(Map<String, String> buildTimeProperties, Boolean rebuild, String configArgs) {
BUILD_TIME_PROPERTIES = buildTimeProperties;
String configHelpText = configArgs;
for (String propertyName : getConfig().getPropertyNames()) {
if (!propertyName.startsWith(MicroProfileConfigProvider.NS_KEYCLOAK_PREFIX)) {
// we should only validate if there is a server image and if the property is a runtime property
if (!shouldValidate(propertyName, rebuild)) {
continue;
}
String buildValue = Environment.getBuiltTimeProperty(propertyName).orElseGet(new Supplier<String>() {
@Override
public String get() {
return Environment.getBuiltTimeProperty(PropertyMappers.toCLIFormat(propertyName)).orElse(null);
}
// try to resolve any property set using profiles
if (propertyName.startsWith("%")) {
propertyName = propertyName.substring(propertyName.indexOf('.') + 1);
}
String finalPropertyName = propertyName;
String buildValue = Environment.getBuiltTimeProperty(PropertyMappers.toCLIFormat(finalPropertyName))
.orElseGet(new Supplier<String>() {
@Override
public String get() {
return Environment.getBuiltTimeProperty(finalPropertyName).orElse(null);
}
});
ConfigValue value = getConfig().getConfigValue(propertyName);
// if no value found we try to resolve using the CLI format
if (value == null || value.getValue() == null) {
value = getConfig().getConfigValue(PropertyMappers.toCLIFormat(propertyName));
}
if (buildValue != null && isRuntimeValue(value) && !buildValue.equalsIgnoreCase(value.getValue())) {
if (value.getValue() != null && !value.getValue().equalsIgnoreCase(buildValue)) {
if (configHelpText != null) {
String currentProp = "--" + PropertyMappers.toCLIFormat(propertyName).substring(3) + "=" + buildValue;
String newProp = "--" + PropertyMappers.toCLIFormat(propertyName).substring(3) + "=" + value.getValue();
if (configHelpText.contains(currentProp)) {
LOGGER.warnf("The new value [%s] of the property [%s] in [%s] differs from the value [%s] set into the server image. The new value will override the value set into the server image.", value.getValue(), propertyName, value.getConfigSourceName(), buildValue);
configHelpText = configHelpText.replaceAll(currentProp, newProp);
} else if (!configHelpText.contains("--" + PropertyMappers.toCLIFormat(propertyName).substring(3))) {
configHelpText += newProp;
}
}
} else if (configHelpText != null && rebuild && isRuntimeValue(value)) {
String prop = "--" + PropertyMappers.toCLIFormat(propertyName).substring(3) + "=" + value.getValue();
if (buildValue != null) {
String currentProp =
"--" + PropertyMappers.toCLIFormat(propertyName).substring(3) + "=" + buildValue;
String newProp =
"--" + PropertyMappers.toCLIFormat(propertyName).substring(3) + "=" + value.getValue();
if (!configHelpText.contains(prop)) {
LOGGER.infof("New property [%s] set with value [%s] in [%s]. This property is not persisted into the server image.",
propertyName, value.getValue(), value.getConfigSourceName(), buildValue);
configHelpText += " " + prop;
if (configHelpText.contains(currentProp)) {
LOGGER.warnf("The new value [%s] of the property [%s] in [%s] differs from the value [%s] set into the server image. The new value will override the value set into the server image.",
value.getValue(), propertyName, value.getConfigSourceName(), buildValue);
configHelpText = configHelpText.replaceAll(currentProp, newProp);
} else if (!configHelpText
.contains("--" + PropertyMappers.toCLIFormat(propertyName).substring(3))) {
LOGGER.warnf("The new value [%s] of the property [%s] in [%s] differs from the value [%s] set into the server image. The new value will override the value set into the server image.",
value.getValue(), propertyName, value.getConfigSourceName(), buildValue);
configHelpText += " " + newProp;
}
} else if (!BUILD_TIME_PROPERTIES.keySet().stream()
.anyMatch(new Predicate<String>() {
@Override
public boolean test(String s) {
return PropertyMappers.canonicalFormat(finalPropertyName)
.equalsIgnoreCase(PropertyMappers.canonicalFormat(s));
}
})) {
String prop = "--" + PropertyMappers.toCLIFormat(propertyName).substring(3) + "=" + value.getValue();
if (!configHelpText.contains(prop)) {
LOGGER.warnf("New property [%s] set with value [%s] in [%s]. This property is not persisted into the server image.",
propertyName, value.getValue(), value.getConfigSourceName(), buildValue);
configHelpText += " " + prop;
}
}
}
}
}
if (configArgs != null && !configArgs.equals(configHelpText)) {
LOGGER.infof("Please, run the 'config' command if you want to configure the server image with the new property values:\n\t%s config %s", Environment.getCommand(), String.join(" ", configHelpText.split(",")));
LOGGER.warnf("Please, run the 'config' command if you want to persist the new configuration into the server image:\n\n\t%s config %s\n", Environment.getCommand(), String.join(" ", configHelpText.split(",")));
}
}
private boolean isRuntimeValue(ConfigValue value) {
String name = value.getName();
return value.getValue() != null && !PropertyMappers.isBuildTimeProperty(name)
&& !"kc.version".equals(name) && !"kc.config.args".equals(
name) && !"kc.home.dir".equals(name);
private boolean shouldValidate(String name, boolean rebuild) {
return rebuild && name.contains(MicroProfileConfigProvider.NS_KEYCLOAK_PREFIX)
&& (!PropertyMappers.isBuildTimeProperty(name)
&& !"kc.version".equals(name)
&& !"kc.config.args".equals(name)
&& !"kc.home.dir".equals(name)
&& !"kc.config.file".equals(name)
&& !"kc.profile".equals(name)
&& !"kc.show.config".equals(name)
&& !"kc.show.config.runtime".equals(name)
&& !PropertyMappers.toCLIFormat("kc.config.file").equals(name));
}
/**