fix: consolidating logic dealing with persisted property handling (#34260)
closes: #34258 Signed-off-by: Steve Hawkins <shawkins@redhat.com>
This commit is contained in:
parent
bd1a5a1543
commit
927f110aef
21 changed files with 387 additions and 533 deletions
|
@ -45,12 +45,11 @@ import io.quarkus.hibernate.orm.deployment.PersistenceXmlDescriptorBuildItem;
|
||||||
import io.quarkus.hibernate.orm.deployment.integration.HibernateOrmIntegrationRuntimeConfiguredBuildItem;
|
import io.quarkus.hibernate.orm.deployment.integration.HibernateOrmIntegrationRuntimeConfiguredBuildItem;
|
||||||
import io.quarkus.hibernate.orm.deployment.spi.AdditionalJpaModelBuildItem;
|
import io.quarkus.hibernate.orm.deployment.spi.AdditionalJpaModelBuildItem;
|
||||||
import io.quarkus.resteasy.reactive.server.spi.MethodScannerBuildItem;
|
import io.quarkus.resteasy.reactive.server.spi.MethodScannerBuildItem;
|
||||||
import io.quarkus.runtime.LaunchMode;
|
|
||||||
import io.quarkus.runtime.configuration.ConfigurationException;
|
import io.quarkus.runtime.configuration.ConfigurationException;
|
||||||
import io.quarkus.vertx.http.deployment.HttpRootPathBuildItem;
|
import io.quarkus.vertx.http.deployment.HttpRootPathBuildItem;
|
||||||
import io.quarkus.vertx.http.deployment.NonApplicationRootPathBuildItem;
|
import io.quarkus.vertx.http.deployment.NonApplicationRootPathBuildItem;
|
||||||
import io.quarkus.vertx.http.deployment.RouteBuildItem;
|
import io.quarkus.vertx.http.deployment.RouteBuildItem;
|
||||||
import io.smallrye.config.ConfigValue;
|
|
||||||
import org.eclipse.microprofile.health.Readiness;
|
import org.eclipse.microprofile.health.Readiness;
|
||||||
import org.hibernate.cfg.AvailableSettings;
|
import org.hibernate.cfg.AvailableSettings;
|
||||||
import org.hibernate.jpa.boot.spi.PersistenceUnitDescriptor;
|
import org.hibernate.jpa.boot.spi.PersistenceUnitDescriptor;
|
||||||
|
@ -97,11 +96,11 @@ import org.keycloak.provider.ProviderManager;
|
||||||
import org.keycloak.provider.Spi;
|
import org.keycloak.provider.Spi;
|
||||||
import org.keycloak.quarkus.runtime.Environment;
|
import org.keycloak.quarkus.runtime.Environment;
|
||||||
import org.keycloak.quarkus.runtime.KeycloakRecorder;
|
import org.keycloak.quarkus.runtime.KeycloakRecorder;
|
||||||
|
import org.keycloak.quarkus.runtime.cli.Picocli;
|
||||||
import org.keycloak.quarkus.runtime.configuration.Configuration;
|
import org.keycloak.quarkus.runtime.configuration.Configuration;
|
||||||
import org.keycloak.quarkus.runtime.configuration.KeycloakConfigSourceProvider;
|
import org.keycloak.quarkus.runtime.configuration.KeycloakConfigSourceProvider;
|
||||||
import org.keycloak.quarkus.runtime.configuration.MicroProfileConfigProvider;
|
import org.keycloak.quarkus.runtime.configuration.MicroProfileConfigProvider;
|
||||||
import org.keycloak.quarkus.runtime.configuration.PersistedConfigSource;
|
import org.keycloak.quarkus.runtime.configuration.PersistedConfigSource;
|
||||||
import org.keycloak.quarkus.runtime.configuration.QuarkusPropertiesConfigSource;
|
|
||||||
import org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper;
|
import org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper;
|
||||||
import org.keycloak.quarkus.runtime.configuration.mappers.PropertyMappers;
|
import org.keycloak.quarkus.runtime.configuration.mappers.PropertyMappers;
|
||||||
import org.keycloak.quarkus.runtime.integration.resteasy.KeycloakHandlerChainCustomizer;
|
import org.keycloak.quarkus.runtime.integration.resteasy.KeycloakHandlerChainCustomizer;
|
||||||
|
@ -130,7 +129,6 @@ import org.keycloak.vault.FilesPlainTextVaultProviderFactory;
|
||||||
import jakarta.persistence.Entity;
|
import jakarta.persistence.Entity;
|
||||||
import jakarta.persistence.spi.PersistenceUnitTransactionType;
|
import jakarta.persistence.spi.PersistenceUnitTransactionType;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
@ -153,14 +151,10 @@ import java.util.logging.Handler;
|
||||||
|
|
||||||
import static org.keycloak.connections.jpa.util.JpaUtils.loadSpecificNamedQueries;
|
import static org.keycloak.connections.jpa.util.JpaUtils.loadSpecificNamedQueries;
|
||||||
import static org.keycloak.quarkus.runtime.Environment.getCurrentOrCreateFeatureProfile;
|
import static org.keycloak.quarkus.runtime.Environment.getCurrentOrCreateFeatureProfile;
|
||||||
import static org.keycloak.quarkus.runtime.Environment.getProviderFiles;
|
|
||||||
import static org.keycloak.quarkus.runtime.Providers.getProviderManager;
|
import static org.keycloak.quarkus.runtime.Providers.getProviderManager;
|
||||||
import static org.keycloak.quarkus.runtime.configuration.Configuration.getOptionalKcValue;
|
import static org.keycloak.quarkus.runtime.configuration.Configuration.getOptionalKcValue;
|
||||||
import static org.keycloak.quarkus.runtime.configuration.Configuration.getOptionalValue;
|
import static org.keycloak.quarkus.runtime.configuration.Configuration.getOptionalValue;
|
||||||
import static org.keycloak.quarkus.runtime.configuration.Configuration.getPropertyNames;
|
|
||||||
import static org.keycloak.quarkus.runtime.configuration.MicroProfileConfigProvider.NS_KEYCLOAK_PREFIX;
|
import static org.keycloak.quarkus.runtime.configuration.MicroProfileConfigProvider.NS_KEYCLOAK_PREFIX;
|
||||||
import static org.keycloak.quarkus.runtime.configuration.MicroProfileConfigProvider.NS_QUARKUS;
|
|
||||||
import static org.keycloak.quarkus.runtime.configuration.QuarkusPropertiesConfigSource.QUARKUS_PROPERTY_ENABLED;
|
|
||||||
import static org.keycloak.quarkus.runtime.storage.database.jpa.QuarkusJpaConnectionProviderFactory.QUERY_PROPERTY_PREFIX;
|
import static org.keycloak.quarkus.runtime.storage.database.jpa.QuarkusJpaConnectionProviderFactory.QUERY_PROPERTY_PREFIX;
|
||||||
import static org.keycloak.representations.provider.ScriptProviderDescriptor.AUTHENTICATORS;
|
import static org.keycloak.representations.provider.ScriptProviderDescriptor.AUTHENTICATORS;
|
||||||
import static org.keycloak.representations.provider.ScriptProviderDescriptor.MAPPERS;
|
import static org.keycloak.representations.provider.ScriptProviderDescriptor.MAPPERS;
|
||||||
|
@ -578,31 +572,7 @@ class KeycloakProcessor {
|
||||||
*/
|
*/
|
||||||
@BuildStep(onlyIf = IsReAugmentation.class)
|
@BuildStep(onlyIf = IsReAugmentation.class)
|
||||||
void persistBuildTimeProperties(BuildProducer<GeneratedResourceBuildItem> resources) {
|
void persistBuildTimeProperties(BuildProducer<GeneratedResourceBuildItem> resources) {
|
||||||
Properties properties = new Properties();
|
Properties properties = Picocli.getNonPersistedBuildTimeOptions();
|
||||||
|
|
||||||
putPersistedProperty(properties, "kc.db");
|
|
||||||
|
|
||||||
for (String name : getPropertyNames()) {
|
|
||||||
putPersistedProperty(properties, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (File jar : getProviderFiles().values()) {
|
|
||||||
properties.put(String.format("kc.provider.file.%s.last-modified", jar.getName()), String.valueOf(jar.lastModified()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Environment.isRebuildCheck()) {
|
|
||||||
// not auto-build (e.g.: start without optimized option) but a regular build to create an optimized server image
|
|
||||||
Configuration.markAsOptimized(properties);
|
|
||||||
}
|
|
||||||
|
|
||||||
String profile = org.keycloak.common.util.Environment.getProfile();
|
|
||||||
|
|
||||||
if (profile != null) {
|
|
||||||
properties.put(org.keycloak.common.util.Environment.PROFILE, profile);
|
|
||||||
properties.put(LaunchMode.current().getProfileKey(), profile);
|
|
||||||
}
|
|
||||||
|
|
||||||
properties.put(QUARKUS_PROPERTY_ENABLED, String.valueOf(QuarkusPropertiesConfigSource.getConfigurationFile() != null));
|
|
||||||
|
|
||||||
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
|
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
|
||||||
properties.store(outputStream, " Auto-generated, DO NOT change this file");
|
properties.store(outputStream, " Auto-generated, DO NOT change this file");
|
||||||
|
@ -612,40 +582,6 @@ class KeycloakProcessor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void putPersistedProperty(Properties properties, String name) {
|
|
||||||
PropertyMapper<?> mapper = PropertyMappers.getMapper(name);
|
|
||||||
ConfigValue value = null;
|
|
||||||
|
|
||||||
if (mapper == null) {
|
|
||||||
if (name.startsWith(NS_QUARKUS)) {
|
|
||||||
value = Configuration.getConfigValue(name);
|
|
||||||
|
|
||||||
if (!QuarkusPropertiesConfigSource.isSameSource(value)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else if (PropertyMappers.isSpiBuildTimeProperty(name)) {
|
|
||||||
value = Configuration.getConfigValue(name);
|
|
||||||
}
|
|
||||||
} else if (mapper.isBuildTime()) {
|
|
||||||
name = mapper.getFrom();
|
|
||||||
value = Configuration.getConfigValue(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value != null && value.getValue() != null) {
|
|
||||||
if (value.getConfigSourceName() == null) {
|
|
||||||
// only persist build options resolved from config sources and not default values
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
String rawValue = value.getRawValue();
|
|
||||||
|
|
||||||
if (rawValue == null) {
|
|
||||||
rawValue = value.getValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
properties.put(name, rawValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This will cause quarkus to include specified modules in the jandex index. For example keycloak-services is needed as it includes
|
* This will cause quarkus to include specified modules in the jandex index. For example keycloak-services is needed as it includes
|
||||||
* most of the JAX-RS resources, which are required to register Resteasy builtin providers.
|
* most of the JAX-RS resources, which are required to register Resteasy builtin providers.
|
||||||
|
|
|
@ -17,8 +17,6 @@
|
||||||
|
|
||||||
package org.keycloak.quarkus.runtime;
|
package org.keycloak.quarkus.runtime;
|
||||||
|
|
||||||
import static org.keycloak.quarkus.runtime.configuration.Configuration.getBuildTimeProperty;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FilenameFilter;
|
import java.io.FilenameFilter;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
@ -36,6 +34,7 @@ import io.smallrye.config.SmallRyeConfig;
|
||||||
import org.apache.commons.lang3.SystemUtils;
|
import org.apache.commons.lang3.SystemUtils;
|
||||||
import org.keycloak.common.Profile;
|
import org.keycloak.common.Profile;
|
||||||
import org.keycloak.quarkus.runtime.cli.command.AbstractCommand;
|
import org.keycloak.quarkus.runtime.cli.command.AbstractCommand;
|
||||||
|
import org.keycloak.quarkus.runtime.configuration.Configuration;
|
||||||
import org.keycloak.quarkus.runtime.configuration.PersistedConfigSource;
|
import org.keycloak.quarkus.runtime.configuration.PersistedConfigSource;
|
||||||
|
|
||||||
public final class Environment {
|
public final class Environment {
|
||||||
|
@ -109,21 +108,18 @@ public final class Environment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getCurrentOrPersistedProfile() {
|
/**
|
||||||
|
* Update the profile settings based upon what was set in the system, environment, or optionally persistent values
|
||||||
|
*/
|
||||||
|
public static String updateProfile(boolean usePersistent) {
|
||||||
String profile = org.keycloak.common.util.Environment.getProfile();
|
String profile = org.keycloak.common.util.Environment.getProfile();
|
||||||
if(profile == null) {
|
if(profile == null && usePersistent) {
|
||||||
profile = PersistedConfigSource.getInstance().getValue(org.keycloak.common.util.Environment.PROFILE);
|
profile = PersistedConfigSource.getInstance().getValue(org.keycloak.common.util.Environment.PROFILE);
|
||||||
}
|
}
|
||||||
return profile;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getProfileOrDefault(String defaultProfile) {
|
|
||||||
String profile = org.keycloak.common.util.Environment.getProfile();
|
|
||||||
|
|
||||||
if (profile == null) {
|
if (profile == null) {
|
||||||
profile = defaultProfile;
|
profile = Environment.PROD_PROFILE_VALUE;
|
||||||
}
|
}
|
||||||
|
setProfile(profile);
|
||||||
return profile;
|
return profile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,7 +128,7 @@ public final class Environment {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return org.keycloak.common.util.Environment.DEV_PROFILE_VALUE.equals(getBuildTimeProperty(org.keycloak.common.util.Environment.PROFILE).orElse(null));
|
return org.keycloak.common.util.Environment.DEV_PROFILE_VALUE.equals(Configuration.getNonPersistedConfigValue(org.keycloak.common.util.Environment.PROFILE).getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isDevProfile(){
|
public static boolean isDevProfile(){
|
||||||
|
@ -221,6 +217,10 @@ public final class Environment {
|
||||||
return Boolean.getBoolean("kc.config.build-and-exit");
|
return Boolean.getBoolean("kc.config.build-and-exit");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void setRebuildCheck() {
|
||||||
|
System.setProperty("kc.config.build-and-exit", "true");
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean isRebuilt() {
|
public static boolean isRebuilt() {
|
||||||
return Boolean.getBoolean("kc.config.built");
|
return Boolean.getBoolean("kc.config.built");
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,12 +19,9 @@ package org.keycloak.quarkus.runtime;
|
||||||
|
|
||||||
import static org.keycloak.quarkus.runtime.Environment.getKeycloakModeFromProfile;
|
import static org.keycloak.quarkus.runtime.Environment.getKeycloakModeFromProfile;
|
||||||
import static org.keycloak.quarkus.runtime.Environment.isDevProfile;
|
import static org.keycloak.quarkus.runtime.Environment.isDevProfile;
|
||||||
import static org.keycloak.quarkus.runtime.Environment.getProfileOrDefault;
|
|
||||||
import static org.keycloak.quarkus.runtime.Environment.isNonServerMode;
|
import static org.keycloak.quarkus.runtime.Environment.isNonServerMode;
|
||||||
import static org.keycloak.quarkus.runtime.Environment.isTestLaunchMode;
|
import static org.keycloak.quarkus.runtime.Environment.isTestLaunchMode;
|
||||||
import static org.keycloak.quarkus.runtime.cli.command.AbstractStartCommand.OPTIMIZED_BUILD_OPTION_LONG;
|
import static org.keycloak.quarkus.runtime.cli.command.AbstractStartCommand.OPTIMIZED_BUILD_OPTION_LONG;
|
||||||
import static org.keycloak.quarkus.runtime.cli.command.AbstractStartCommand.wasBuildEverRun;
|
|
||||||
import static org.keycloak.quarkus.runtime.cli.command.Start.isDevProfileNotAllowed;
|
|
||||||
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -83,12 +80,8 @@ public class KeycloakMain implements QuarkusApplication {
|
||||||
cliArgs.add("-h");
|
cliArgs.add("-h");
|
||||||
} else if (isFastStart(cliArgs)) { // fast path for starting the server without bootstrapping CLI
|
} else if (isFastStart(cliArgs)) { // fast path for starting the server without bootstrapping CLI
|
||||||
|
|
||||||
if (!wasBuildEverRun()) {
|
Environment.updateProfile(true);
|
||||||
handleUsageError(Messages.optimizedUsedForFirstStartup());
|
if (Environment.isDevProfile()) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isDevProfileNotAllowed()) {
|
|
||||||
handleUsageError(Messages.devProfileNotAllowedError(Start.NAME));
|
handleUsageError(Messages.devProfileNotAllowedError(Start.NAME));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -156,7 +149,7 @@ public class KeycloakMain implements QuarkusApplication {
|
||||||
Quarkus.run(KeycloakMain.class, (exitCode, cause) -> {
|
Quarkus.run(KeycloakMain.class, (exitCode, cause) -> {
|
||||||
if (cause != null) {
|
if (cause != null) {
|
||||||
errorHandler.error(errStream,
|
errorHandler.error(errStream,
|
||||||
String.format("Failed to start server in (%s) mode", getKeycloakModeFromProfile(getProfileOrDefault("prod"))),
|
String.format("Failed to start server in (%s) mode", getKeycloakModeFromProfile(org.keycloak.common.util.Environment.getProfile())),
|
||||||
cause.getCause());
|
cause.getCause());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,7 +161,7 @@ public class KeycloakMain implements QuarkusApplication {
|
||||||
}, args);
|
}, args);
|
||||||
} catch (Throwable cause) {
|
} catch (Throwable cause) {
|
||||||
errorHandler.error(errStream,
|
errorHandler.error(errStream,
|
||||||
String.format("Unexpected error when starting the server in (%s) mode", getKeycloakModeFromProfile(getProfileOrDefault("prod"))),
|
String.format("Unexpected error when starting the server in (%s) mode", getKeycloakModeFromProfile(org.keycloak.common.util.Environment.getProfile())),
|
||||||
cause.getCause());
|
cause.getCause());
|
||||||
System.exit(1);
|
System.exit(1);
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,8 +18,8 @@
|
||||||
package org.keycloak.quarkus.runtime.cli;
|
package org.keycloak.quarkus.runtime.cli;
|
||||||
|
|
||||||
import static java.lang.String.format;
|
import static java.lang.String.format;
|
||||||
import static java.util.Optional.ofNullable;
|
import static org.keycloak.quarkus.runtime.Environment.getProviderFiles;
|
||||||
import static java.util.stream.StreamSupport.stream;
|
import static org.keycloak.quarkus.runtime.Environment.isDevMode;
|
||||||
import static org.keycloak.quarkus.runtime.Environment.isRebuild;
|
import static org.keycloak.quarkus.runtime.Environment.isRebuild;
|
||||||
import static org.keycloak.quarkus.runtime.Environment.isRebuildCheck;
|
import static org.keycloak.quarkus.runtime.Environment.isRebuildCheck;
|
||||||
import static org.keycloak.quarkus.runtime.Environment.isRebuilt;
|
import static org.keycloak.quarkus.runtime.Environment.isRebuilt;
|
||||||
|
@ -27,16 +27,9 @@ import static org.keycloak.quarkus.runtime.cli.OptionRenderer.decorateDuplicitOp
|
||||||
import static org.keycloak.quarkus.runtime.cli.command.AbstractStartCommand.OPTIMIZED_BUILD_OPTION_LONG;
|
import static org.keycloak.quarkus.runtime.cli.command.AbstractStartCommand.OPTIMIZED_BUILD_OPTION_LONG;
|
||||||
import static org.keycloak.quarkus.runtime.configuration.ConfigArgsConfigSource.parseConfigArgs;
|
import static org.keycloak.quarkus.runtime.configuration.ConfigArgsConfigSource.parseConfigArgs;
|
||||||
import static org.keycloak.quarkus.runtime.configuration.Configuration.OPTION_PART_SEPARATOR;
|
import static org.keycloak.quarkus.runtime.configuration.Configuration.OPTION_PART_SEPARATOR;
|
||||||
import static org.keycloak.quarkus.runtime.configuration.Configuration.getBuildTimeProperty;
|
|
||||||
import static org.keycloak.quarkus.runtime.configuration.Configuration.getConfig;
|
|
||||||
import static org.keycloak.quarkus.runtime.Environment.isDevMode;
|
|
||||||
import static org.keycloak.quarkus.runtime.configuration.Configuration.getCurrentBuiltTimeProperty;
|
|
||||||
import static org.keycloak.quarkus.runtime.configuration.Configuration.getRawPersistedProperty;
|
import static org.keycloak.quarkus.runtime.configuration.Configuration.getRawPersistedProperty;
|
||||||
import static org.keycloak.quarkus.runtime.configuration.Configuration.getRuntimeProperty;
|
|
||||||
import static org.keycloak.quarkus.runtime.configuration.MicroProfileConfigProvider.NS_KEYCLOAK_PREFIX;
|
import static org.keycloak.quarkus.runtime.configuration.MicroProfileConfigProvider.NS_KEYCLOAK_PREFIX;
|
||||||
import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMappers.maskValue;
|
import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMappers.maskValue;
|
||||||
import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMappers.isBuildTimeProperty;
|
|
||||||
import static org.keycloak.utils.StringUtil.isNotBlank;
|
|
||||||
import static picocli.CommandLine.Model.UsageMessageSpec.SECTION_KEY_COMMAND_LIST;
|
import static picocli.CommandLine.Model.UsageMessageSpec.SECTION_KEY_COMMAND_LIST;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
@ -44,62 +37,59 @@ import java.io.PrintWriter;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.EnumMap;
|
import java.util.EnumMap;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.Properties;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.regex.Pattern;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import org.eclipse.microprofile.config.spi.ConfigSource;
|
|
||||||
import org.keycloak.common.profile.ProfileException;
|
import org.keycloak.common.profile.ProfileException;
|
||||||
import org.keycloak.config.DeprecatedMetadata;
|
import org.keycloak.config.DeprecatedMetadata;
|
||||||
import org.keycloak.config.Option;
|
import org.keycloak.config.Option;
|
||||||
import org.keycloak.config.OptionCategory;
|
import org.keycloak.config.OptionCategory;
|
||||||
|
import org.keycloak.quarkus.runtime.Environment;
|
||||||
|
import org.keycloak.quarkus.runtime.KeycloakMain;
|
||||||
|
import org.keycloak.quarkus.runtime.Messages;
|
||||||
import org.keycloak.quarkus.runtime.cli.command.AbstractCommand;
|
import org.keycloak.quarkus.runtime.cli.command.AbstractCommand;
|
||||||
import org.keycloak.quarkus.runtime.cli.command.BootstrapAdmin;
|
|
||||||
import org.keycloak.quarkus.runtime.cli.command.Build;
|
import org.keycloak.quarkus.runtime.cli.command.Build;
|
||||||
|
import org.keycloak.quarkus.runtime.cli.command.Completion;
|
||||||
import org.keycloak.quarkus.runtime.cli.command.Main;
|
import org.keycloak.quarkus.runtime.cli.command.Main;
|
||||||
import org.keycloak.quarkus.runtime.cli.command.ShowConfig;
|
import org.keycloak.quarkus.runtime.cli.command.ShowConfig;
|
||||||
import org.keycloak.quarkus.runtime.cli.command.Start;
|
|
||||||
import org.keycloak.quarkus.runtime.cli.command.StartDev;
|
import org.keycloak.quarkus.runtime.cli.command.StartDev;
|
||||||
import org.keycloak.quarkus.runtime.cli.command.Tools;
|
|
||||||
import org.keycloak.quarkus.runtime.configuration.ConfigArgsConfigSource;
|
import org.keycloak.quarkus.runtime.configuration.ConfigArgsConfigSource;
|
||||||
import org.keycloak.quarkus.runtime.configuration.Configuration;
|
import org.keycloak.quarkus.runtime.configuration.Configuration;
|
||||||
import org.keycloak.quarkus.runtime.configuration.DisabledMappersInterceptor;
|
import org.keycloak.quarkus.runtime.configuration.DisabledMappersInterceptor;
|
||||||
import org.keycloak.quarkus.runtime.configuration.KcUnmatchedArgumentException;
|
import org.keycloak.quarkus.runtime.configuration.KcUnmatchedArgumentException;
|
||||||
import org.keycloak.quarkus.runtime.configuration.PersistedConfigSource;
|
import org.keycloak.quarkus.runtime.configuration.KeycloakPropertiesConfigSource;
|
||||||
|
import org.keycloak.quarkus.runtime.configuration.MicroProfileConfigProvider;
|
||||||
import org.keycloak.quarkus.runtime.configuration.PropertyMappingInterceptor;
|
import org.keycloak.quarkus.runtime.configuration.PropertyMappingInterceptor;
|
||||||
import org.keycloak.quarkus.runtime.configuration.QuarkusPropertiesConfigSource;
|
import org.keycloak.quarkus.runtime.configuration.QuarkusPropertiesConfigSource;
|
||||||
import org.keycloak.quarkus.runtime.configuration.mappers.PropertyMappers;
|
|
||||||
import org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper;
|
import org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper;
|
||||||
import org.keycloak.quarkus.runtime.Environment;
|
import org.keycloak.quarkus.runtime.configuration.mappers.PropertyMappers;
|
||||||
import org.keycloak.quarkus.runtime.KeycloakMain;
|
|
||||||
|
|
||||||
import io.quarkus.bootstrap.runner.QuarkusEntryPoint;
|
import io.quarkus.bootstrap.runner.QuarkusEntryPoint;
|
||||||
|
import io.quarkus.runtime.LaunchMode;
|
||||||
import io.smallrye.config.ConfigValue;
|
import io.smallrye.config.ConfigValue;
|
||||||
|
|
||||||
import picocli.CommandLine;
|
import picocli.CommandLine;
|
||||||
import picocli.CommandLine.ParameterException;
|
|
||||||
import picocli.CommandLine.ParseResult;
|
|
||||||
import picocli.CommandLine.DuplicateOptionAnnotationsException;
|
import picocli.CommandLine.DuplicateOptionAnnotationsException;
|
||||||
import picocli.CommandLine.Help.Ansi;
|
import picocli.CommandLine.Help.Ansi;
|
||||||
import picocli.CommandLine.Help.Ansi.Style;
|
import picocli.CommandLine.Help.Ansi.Style;
|
||||||
import picocli.CommandLine.Help.ColorScheme;
|
import picocli.CommandLine.Help.ColorScheme;
|
||||||
import picocli.CommandLine.IFactory;
|
import picocli.CommandLine.IFactory;
|
||||||
|
import picocli.CommandLine.Model.ArgGroupSpec;
|
||||||
import picocli.CommandLine.Model.CommandSpec;
|
import picocli.CommandLine.Model.CommandSpec;
|
||||||
import picocli.CommandLine.Model.ISetter;
|
import picocli.CommandLine.Model.ISetter;
|
||||||
import picocli.CommandLine.Model.OptionSpec;
|
import picocli.CommandLine.Model.OptionSpec;
|
||||||
import picocli.CommandLine.Model.ArgGroupSpec;
|
import picocli.CommandLine.ParameterException;
|
||||||
|
import picocli.CommandLine.ParseResult;
|
||||||
|
|
||||||
public class Picocli {
|
public class Picocli {
|
||||||
|
|
||||||
|
private static final String KC_PROVIDER_FILE_PREFIX = "kc.provider.file.";
|
||||||
public static final String ARG_PREFIX = "--";
|
public static final String ARG_PREFIX = "--";
|
||||||
public static final String ARG_SHORT_PREFIX = "-";
|
public static final String ARG_SHORT_PREFIX = "-";
|
||||||
public static final String NO_PARAM_LABEL = "none";
|
public static final String NO_PARAM_LABEL = "none";
|
||||||
|
@ -172,7 +162,13 @@ public class Picocli {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentSpec != null) {
|
if (currentSpec != null) {
|
||||||
addCommandOptions(cliArgs, currentSpec.commandLine());
|
CommandLine commandLine = currentSpec.commandLine();
|
||||||
|
addCommandOptions(cliArgs, commandLine);
|
||||||
|
|
||||||
|
if (commandLine != null && commandLine.getCommand() instanceof AbstractCommand ac) {
|
||||||
|
// set current parsed command
|
||||||
|
Environment.setParsedCommand(ac);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isRebuildCheck()) {
|
if (isRebuildCheck()) {
|
||||||
|
@ -207,7 +203,7 @@ public class Picocli {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected int runReAugmentationIfNeeded(List<String> cliArgs, CommandLine cmd, CommandLine currentCommand) {
|
private int runReAugmentationIfNeeded(List<String> cliArgs, CommandLine cmd, CommandLine currentCommand) {
|
||||||
int exitCode = 0;
|
int exitCode = 0;
|
||||||
|
|
||||||
if (currentCommand == null) {
|
if (currentCommand == null) {
|
||||||
|
@ -220,14 +216,10 @@ public class Picocli {
|
||||||
return exitCode;
|
return exitCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentCommandName.equals(StartDev.NAME)) {
|
// TODO: ensure that the config has not yet been initialized
|
||||||
String profile = org.keycloak.common.util.Environment.getProfile();
|
// - there's currently no good way to do that directly on ConfigProviderResolver
|
||||||
|
initProfile(cliArgs, currentCommandName);
|
||||||
|
|
||||||
if (profile == null) {
|
|
||||||
// force the server image to be set with the dev profile
|
|
||||||
Environment.forceDevProfile();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (requiresReAugmentation(currentCommand)) {
|
if (requiresReAugmentation(currentCommand)) {
|
||||||
PropertyMappers.sanitizeDisabledMappers();
|
PropertyMappers.sanitizeDisabledMappers();
|
||||||
exitCode = runReAugmentation(cliArgs, cmd);
|
exitCode = runReAugmentation(cliArgs, cmd);
|
||||||
|
@ -236,34 +228,42 @@ public class Picocli {
|
||||||
return exitCode;
|
return exitCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void initProfile(List<String> cliArgs, String currentCommandName) {
|
||||||
|
if (currentCommandName.equals(StartDev.NAME)) {
|
||||||
|
// force the server image to be set with the dev profile
|
||||||
|
Environment.forceDevProfile();
|
||||||
|
} else {
|
||||||
|
Environment.updateProfile(false);
|
||||||
|
|
||||||
|
// override from the cli if specified
|
||||||
|
parseConfigArgs(cliArgs, (k, v) -> {
|
||||||
|
if (k.equals(Main.PROFILE_SHORT_NAME) || k.equals(Main.PROFILE_LONG_NAME)) {
|
||||||
|
Environment.setProfile(v);
|
||||||
|
}
|
||||||
|
}, ignored -> {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static boolean shouldSkipRebuild(List<String> cliArgs, String currentCommandName) {
|
private static boolean shouldSkipRebuild(List<String> cliArgs, String currentCommandName) {
|
||||||
return cliArgs.contains("--help")
|
return cliArgs.contains("--help")
|
||||||
|| cliArgs.contains("-h")
|
|| cliArgs.contains("-h")
|
||||||
|| cliArgs.contains("--help-all")
|
|| cliArgs.contains("--help-all")
|
||||||
|| currentCommandName.equals(Build.NAME)
|
|| currentCommandName.equals(Build.NAME)
|
||||||
|| currentCommandName.equals(ShowConfig.NAME)
|
|| currentCommandName.equals(ShowConfig.NAME)
|
||||||
|| currentCommandName.equals(BootstrapAdmin.NAME)
|
|| currentCommandName.equals(Completion.NAME);
|
||||||
|| currentCommandName.equals(Tools.NAME);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean requiresReAugmentation(CommandLine cmdCommand) {
|
private static boolean requiresReAugmentation(CommandLine cmdCommand) {
|
||||||
if (ConfigArgsConfigSource.getAllCliArgs().contains(Start.NAME)
|
Map<String, String> rawPersistedProperties = Configuration.getRawPersistedProperties();
|
||||||
// run time dev mode is not set
|
if (rawPersistedProperties.isEmpty()) {
|
||||||
&& !org.keycloak.common.util.Environment.isDevMode()
|
return true; // no build yet
|
||||||
// build time dev mode was set
|
|
||||||
&& org.keycloak.common.util.Environment.DEV_PROFILE_VALUE.equals(getBuildTimeProperty(org.keycloak.common.util.Environment.PROFILE).orElse(null))) {
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
var current = getNonPersistedBuildTimeOptions();
|
||||||
|
|
||||||
if (hasConfigChanges(cmdCommand)) {
|
// everything but the optimized value must match
|
||||||
if (!ConfigArgsConfigSource.getAllCliArgs().contains(StartDev.NAME) && "dev".equals(getConfig().getOptionalValue("kc.profile", String.class).orElse(null))) {
|
String key = Configuration.KC_OPTIMIZED;
|
||||||
return false;
|
Optional.ofNullable(rawPersistedProperties.get(key)).ifPresentOrElse(value -> current.put(key, value), () -> current.remove(key));
|
||||||
}
|
return !rawPersistedProperties.equals(current);
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return hasProviderChanges();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -294,7 +294,9 @@ public class Picocli {
|
||||||
private static int runReAugmentation(List<String> cliArgs, CommandLine cmd) {
|
private static int runReAugmentation(List<String> cliArgs, CommandLine cmd) {
|
||||||
if(!isDevMode() && cmd != null) {
|
if(!isDevMode() && cmd != null) {
|
||||||
cmd.getOut().println("Changes detected in configuration. Updating the server image.");
|
cmd.getOut().println("Changes detected in configuration. Updating the server image.");
|
||||||
checkChangesInBuildOptionsDuringAutoBuild();
|
if (Configuration.isOptimized()) {
|
||||||
|
checkChangesInBuildOptionsDuringAutoBuild(cmd.getOut());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
List<String> configArgsList = new ArrayList<>();
|
List<String> configArgsList = new ArrayList<>();
|
||||||
|
@ -316,36 +318,8 @@ public class Picocli {
|
||||||
return exitCode;
|
return exitCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean hasProviderChanges() {
|
private static boolean wasBuildEverRun() {
|
||||||
Map<String, String> persistedProps = PersistedConfigSource.getInstance().getProperties();
|
return !Configuration.getRawPersistedProperties().isEmpty();
|
||||||
Map<String, File> deployedProviders = Environment.getProviderFiles();
|
|
||||||
|
|
||||||
if (persistedProps.isEmpty()) {
|
|
||||||
return !deployedProviders.isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
Set<String> providerKeys = persistedProps.keySet().stream().filter(Picocli::isProviderKey).collect(Collectors.toSet());
|
|
||||||
|
|
||||||
if (deployedProviders.size() != providerKeys.size()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (String key : providerKeys) {
|
|
||||||
String fileName = key.substring("kc.provider.file".length() + 1, key.lastIndexOf('.'));
|
|
||||||
|
|
||||||
if (!deployedProviders.containsKey(fileName)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
File file = deployedProviders.get(fileName);
|
|
||||||
String lastModified = persistedProps.get(key);
|
|
||||||
|
|
||||||
if (!lastModified.equals(String.valueOf(file.lastModified()))) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -356,6 +330,10 @@ public class Picocli {
|
||||||
* @param outWriter
|
* @param outWriter
|
||||||
*/
|
*/
|
||||||
public static void validateConfig(List<String> cliArgs, AbstractCommand abstractCommand, PrintWriter outWriter) {
|
public static void validateConfig(List<String> cliArgs, AbstractCommand abstractCommand, PrintWriter outWriter) {
|
||||||
|
if (cliArgs.contains(OPTIMIZED_BUILD_OPTION_LONG) && !wasBuildEverRun()) {
|
||||||
|
throw new PropertyException(Messages.optimizedUsedForFirstStartup());
|
||||||
|
}
|
||||||
|
|
||||||
IncludeOptions options = getIncludeOptions(cliArgs, abstractCommand, abstractCommand.getName());
|
IncludeOptions options = getIncludeOptions(cliArgs, abstractCommand, abstractCommand.getName());
|
||||||
|
|
||||||
if (!options.includeBuildTime && !options.includeRuntime) {
|
if (!options.includeBuildTime && !options.includeRuntime) {
|
||||||
|
@ -391,8 +369,7 @@ public class Picocli {
|
||||||
ConfigValue configValue = Configuration.getConfigValue(mapper.getFrom());
|
ConfigValue configValue = Configuration.getConfigValue(mapper.getFrom());
|
||||||
String configValueStr = configValue.getValue();
|
String configValueStr = configValue.getValue();
|
||||||
|
|
||||||
// don't consider missing or anything below standard env properties
|
if (configValueStr == null || !isUserModifiable(configValue)) {
|
||||||
if (configValueStr == null || configValue.getConfigSourceOrdinal() < 300) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -455,14 +432,19 @@ public class Picocli {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean isUserModifiable(ConfigValue configValue) {
|
||||||
|
// This could check as low as SysPropConfigSource DEFAULT_ORDINAL, which is 400
|
||||||
|
// for now we won't validate these as it's not expected for the user to specify options via system properties
|
||||||
|
return configValue.getConfigSourceOrdinal() >= KeycloakPropertiesConfigSource.PROPERTIES_FILE_ORDINAL;
|
||||||
|
}
|
||||||
|
|
||||||
private static void checkSpiOptions(IncludeOptions options, final List<String> ignoredBuildTime,
|
private static void checkSpiOptions(IncludeOptions options, final List<String> ignoredBuildTime,
|
||||||
final List<String> ignoredRunTime) {
|
final List<String> ignoredRunTime) {
|
||||||
String kcSpiPrefix = NS_KEYCLOAK_PREFIX + "spi";
|
|
||||||
for (String key : Configuration.getConfig().getPropertyNames()) {
|
for (String key : Configuration.getConfig().getPropertyNames()) {
|
||||||
if (!key.startsWith(kcSpiPrefix)) {
|
if (!key.startsWith(PropertyMappers.KC_SPI_PREFIX)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
boolean buildTimeOption = key.endsWith("-provider") || key.endsWith("-provider-default") || key.endsWith("-enabled");
|
boolean buildTimeOption = PropertyMappers.isSpiBuildTimeProperty(key);
|
||||||
|
|
||||||
ConfigValue configValue = Configuration.getConfigValue(key);
|
ConfigValue configValue = Configuration.getConfigValue(key);
|
||||||
String configValueStr = configValue.getValue();
|
String configValueStr = configValue.getValue();
|
||||||
|
@ -558,124 +540,59 @@ public class Picocli {
|
||||||
String.join("\n", properties)), outWriter);
|
String.join("\n", properties)), outWriter);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean hasConfigChanges(CommandLine cmdCommand) {
|
public static Properties getNonPersistedBuildTimeOptions() {
|
||||||
Optional<String> currentProfile = ofNullable(org.keycloak.common.util.Environment.getProfile());
|
Properties properties = new Properties();
|
||||||
Optional<String> persistedProfile = getBuildTimeProperty("kc.profile");
|
// TODO: could get only non-persistent property names
|
||||||
|
Configuration.getPropertyNames().forEach(name -> {
|
||||||
if (!persistedProfile.orElse("").equals(currentProfile.orElse(""))) {
|
boolean quarkus = false;
|
||||||
return true;
|
PropertyMapper<?> mapper = PropertyMappers.getMapper(name);
|
||||||
}
|
if (mapper != null) {
|
||||||
|
if (!mapper.isBuildTime()) {
|
||||||
for (String propertyName : getConfig().getPropertyNames()) {
|
return;
|
||||||
// only check keycloak build-time properties
|
|
||||||
if (!isBuildTimeProperty(propertyName)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
ConfigValue configValue = getConfig().getConfigValue(propertyName);
|
|
||||||
|
|
||||||
if (configValue == null || configValue.getConfigSourceName() == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// try to resolve any property set using profiles
|
|
||||||
if (propertyName.startsWith("%")) {
|
|
||||||
propertyName = propertyName.substring(propertyName.indexOf('.') + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
String persistedValue = getBuildTimeProperty(propertyName).orElse("");
|
|
||||||
String runtimeValue = getRuntimeProperty(propertyName).orElse(null);
|
|
||||||
|
|
||||||
// compare only the relevant options for this command, as not all options might be set for this command
|
|
||||||
if (cmdCommand.getCommand() instanceof AbstractCommand) {
|
|
||||||
AbstractCommand abstractCommand = cmdCommand.getCommand();
|
|
||||||
PropertyMapper<?> mapper = PropertyMappers.getMapper(propertyName);
|
|
||||||
if (mapper != null) {
|
|
||||||
if (!abstractCommand.getOptionCategories().contains(mapper.getCategory())) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
name = mapper.getFrom();
|
||||||
|
if (properties.containsKey(name)) {
|
||||||
if (runtimeValue == null && isNotBlank(persistedValue)) {
|
return;
|
||||||
PropertyMapper<?> mapper = PropertyMappers.getMapper(propertyName);
|
|
||||||
|
|
||||||
if (mapper != null && persistedValue.equals(Option.getDefaultValueString(mapper.getDefaultValue().orElse(null)))) {
|
|
||||||
// same as default
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
} else if (name.startsWith(MicroProfileConfigProvider.NS_QUARKUS)) {
|
||||||
// probably because it was unset
|
// TODO: this is not correct - we are including runtime properties here, but at least they
|
||||||
return true;
|
// are already coming from a file
|
||||||
|
quarkus = true;
|
||||||
|
} else if (!PropertyMappers.isSpiBuildTimeProperty(name)) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
ConfigValue value = Configuration.getNonPersistedConfigValue(name);
|
||||||
// changes to a single property is enough to indicate changes to configuration
|
if (value.getValue() == null || value.getConfigSourceName() == null
|
||||||
if (!persistedValue.equals(runtimeValue)) {
|
|| (quarkus && !value.getConfigSourceName().equals(QuarkusPropertiesConfigSource.NAME))) {
|
||||||
return true;
|
// only persist build options resolved from config sources and not default values
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
// since we're presisting all quarkus values, this may leak some runtime information - we don't want
|
||||||
|
// to capture expanded expressions that may be referencing environment variables
|
||||||
|
String stringValue = value.getValue();
|
||||||
|
if (quarkus && value.getRawValue() != null) {
|
||||||
|
stringValue = value.getRawValue();
|
||||||
|
}
|
||||||
|
properties.put(name, stringValue);
|
||||||
|
});
|
||||||
|
|
||||||
|
// the following should be ignored when output the optimized check message
|
||||||
|
// they are either not set by the user, or not properly initialized
|
||||||
|
|
||||||
|
for (File jar : getProviderFiles().values()) {
|
||||||
|
properties.put(String.format(KC_PROVIDER_FILE_PREFIX + "%s.last-modified", jar.getName()), String.valueOf(jar.lastModified()));
|
||||||
}
|
}
|
||||||
|
|
||||||
//check for defined quarkus raw build properties for UserStorageProvider extensions
|
if (!Environment.isRebuildCheck()) {
|
||||||
if (QuarkusPropertiesConfigSource.getConfigurationFile() != null) {
|
// not auto-build (e.g.: start without optimized option) but a regular build to create an optimized server image
|
||||||
Optional<ConfigSource> quarkusPropertiesConfigSource = getConfig().getConfigSource(QuarkusPropertiesConfigSource.NAME);
|
Configuration.markAsOptimized(properties);
|
||||||
|
|
||||||
if (quarkusPropertiesConfigSource.isPresent()) {
|
|
||||||
Map<String, String> foundQuarkusBuildProperties = findSupportedRawQuarkusBuildProperties(quarkusPropertiesConfigSource.get().getProperties().entrySet());
|
|
||||||
|
|
||||||
//only check if buildProps are found in quarkus properties file.
|
|
||||||
if (!foundQuarkusBuildProperties.isEmpty()) {
|
|
||||||
Optional<ConfigSource> persistedConfigSource = getConfig().getConfigSource(PersistedConfigSource.NAME);
|
|
||||||
|
|
||||||
if(persistedConfigSource.isPresent()) {
|
|
||||||
for(String key : foundQuarkusBuildProperties.keySet()) {
|
|
||||||
if (notContainsKey(persistedConfigSource.get(), key)) {
|
|
||||||
//if persisted cs does not contain raw quarkus key from quarkus.properties, assume build is needed as the key is new.
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//if it contains the key, check if the value actually changed from the persisted one.
|
|
||||||
return hasAtLeastOneChangedBuildProperty(foundQuarkusBuildProperties, persistedConfigSource.get().getProperties().entrySet());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
String profile = org.keycloak.common.util.Environment.getProfile();
|
||||||
}
|
properties.put(org.keycloak.common.util.Environment.PROFILE, profile);
|
||||||
|
properties.put(LaunchMode.current().getProfileKey(), profile);
|
||||||
|
|
||||||
private static boolean hasAtLeastOneChangedBuildProperty(Map<String, String> foundQuarkusBuildProperties, Set<Map.Entry<String, String>> persistedEntries) {
|
return properties;
|
||||||
for(Map.Entry<String, String> persistedEntry : persistedEntries) {
|
|
||||||
if (foundQuarkusBuildProperties.containsKey(persistedEntry.getKey())) {
|
|
||||||
return isChangedValue(foundQuarkusBuildProperties, persistedEntry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean notContainsKey(ConfigSource persistedConfigSource, String key) {
|
|
||||||
return !persistedConfigSource.getProperties().containsKey(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Map<String, String> findSupportedRawQuarkusBuildProperties(Set<Map.Entry<String, String>> entries) {
|
|
||||||
Pattern buildTimePattern = Pattern.compile(QuarkusPropertiesConfigSource.QUARKUS_DATASOURCE_BUILDTIME_REGEX);
|
|
||||||
Map<String, String> result = new HashMap<>();
|
|
||||||
|
|
||||||
for(Map.Entry<String, String> entry : entries) {
|
|
||||||
if (buildTimePattern.matcher(entry.getKey()).matches()) {
|
|
||||||
result.put(entry.getKey(), entry.getValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isChangedValue(Map<String, String> foundQuarkusBuildProps, Map.Entry<String, String> persistedEntry) {
|
|
||||||
return !foundQuarkusBuildProps.get(persistedEntry.getKey()).equals(persistedEntry.getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isProviderKey(String key) {
|
|
||||||
return key.startsWith("kc.provider.file");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public CommandLine createCommandLine(Consumer<CommandSpec> consumer) {
|
public CommandLine createCommandLine(Consumer<CommandSpec> consumer) {
|
||||||
|
@ -740,12 +657,9 @@ public class Picocli {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void addCommandOptions(List<String> cliArgs, CommandLine command) {
|
private static void addCommandOptions(List<String> cliArgs, CommandLine command) {
|
||||||
if (command != null && command.getCommand() instanceof AbstractCommand ac) {
|
if (command != null && command.getCommand() instanceof AbstractCommand) {
|
||||||
IncludeOptions options = getIncludeOptions(cliArgs, command.getCommand(), command.getCommandName());
|
IncludeOptions options = getIncludeOptions(cliArgs, command.getCommand(), command.getCommandName());
|
||||||
|
|
||||||
// set current parsed command
|
|
||||||
Environment.setParsedCommand(ac);
|
|
||||||
|
|
||||||
if (!options.includeBuildTime && !options.includeRuntime) {
|
if (!options.includeBuildTime && !options.includeRuntime) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -938,48 +852,51 @@ public class Picocli {
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void checkChangesInBuildOptionsDuringAutoBuild() {
|
private static void checkChangesInBuildOptionsDuringAutoBuild(PrintWriter out) {
|
||||||
if (Configuration.isOptimized()) {
|
StringBuilder options = new StringBuilder();
|
||||||
List<PropertyMapper<?>> buildOptions = stream(Configuration.getPropertyNames(true).spliterator(), false)
|
|
||||||
.sorted()
|
|
||||||
.map(PropertyMappers::getMapper)
|
|
||||||
.filter(Objects::nonNull).collect(Collectors.toList());
|
|
||||||
|
|
||||||
if (buildOptions.isEmpty()) {
|
var current = getNonPersistedBuildTimeOptions();
|
||||||
return;
|
var persisted = Configuration.getRawPersistedProperties();
|
||||||
|
|
||||||
|
// TODO: order is not well defined here
|
||||||
|
|
||||||
|
current.forEach((key, value) -> {
|
||||||
|
String persistedValue = persisted.get(key);
|
||||||
|
if (!value.equals(persistedValue)) {
|
||||||
|
optionChanged(options, (String)key, persistedValue, (String)value);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
StringBuilder options = new StringBuilder();
|
persisted.forEach((key, value) -> {
|
||||||
|
if (current.get(key) == null) {
|
||||||
for (PropertyMapper<?> mapper : buildOptions) {
|
optionChanged(options, key, value, null);
|
||||||
String newValue = ofNullable(getCurrentBuiltTimeProperty(mapper.getFrom()))
|
|
||||||
.map(ConfigValue::getValue)
|
|
||||||
.orElse("<unset>");
|
|
||||||
String currentValue = getRawPersistedProperty(mapper.getFrom()).get();
|
|
||||||
|
|
||||||
if (newValue.equals(currentValue)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
String name = mapper.getOption().getKey();
|
|
||||||
|
|
||||||
options.append("\n\t- ")
|
|
||||||
.append(name).append("=").append(currentValue)
|
|
||||||
.append(" > ")
|
|
||||||
.append(name).append("=").append(newValue);
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if (options.length() > 0) {
|
if (options.length() > 0) {
|
||||||
System.out.println(
|
out.println(
|
||||||
Ansi.AUTO.string(
|
Ansi.AUTO.string(
|
||||||
new StringBuilder("@|bold,red ")
|
new StringBuilder("@|bold,red ")
|
||||||
.append("The previous optimized build will be overridden with the following build options:")
|
.append("The previous optimized build will be overridden with the following build options:")
|
||||||
.append(options)
|
.append(options)
|
||||||
.append("\nTo avoid that, run the 'build' command again and then start the optimized server instance using the '--optimized' flag.")
|
.append("\nTo avoid that, run the 'build' command again and then start the optimized server instance using the '--optimized' flag.")
|
||||||
.append("|@").toString()
|
.append("|@").toString()
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void optionChanged(StringBuilder options, String key, String oldValue, String newValue) {
|
||||||
|
// the assumption here is that no build time options need mask handling
|
||||||
|
boolean isIgnored = !key.startsWith(MicroProfileConfigProvider.NS_KEYCLOAK_PREFIX)
|
||||||
|
|| key.startsWith(KC_PROVIDER_FILE_PREFIX) || key.equals(Configuration.KC_OPTIMIZED)
|
||||||
|
|| key.equals(org.keycloak.common.util.Environment.PROFILE);
|
||||||
|
if (!isIgnored) {
|
||||||
|
key = key.substring(3);
|
||||||
|
options.append("\n\t- ").append(key).append("=")
|
||||||
|
.append(Optional.ofNullable(oldValue).orElse("<unset>")).append(" > ")
|
||||||
|
.append(key).append("=")
|
||||||
|
.append(Optional.ofNullable(newValue).orElse("<unset>"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,10 +19,6 @@ package org.keycloak.quarkus.runtime.cli.command;
|
||||||
|
|
||||||
import org.keycloak.config.OptionCategory;
|
import org.keycloak.config.OptionCategory;
|
||||||
import org.keycloak.quarkus.runtime.Environment;
|
import org.keycloak.quarkus.runtime.Environment;
|
||||||
import org.keycloak.quarkus.runtime.KeycloakMain;
|
|
||||||
import org.keycloak.quarkus.runtime.Messages;
|
|
||||||
import org.keycloak.quarkus.runtime.cli.ExecutionExceptionHandler;
|
|
||||||
import org.keycloak.quarkus.runtime.configuration.ConfigArgsConfigSource;
|
|
||||||
import org.keycloak.quarkus.runtime.configuration.mappers.HostnameV2PropertyMappers;
|
import org.keycloak.quarkus.runtime.configuration.mappers.HostnameV2PropertyMappers;
|
||||||
import org.keycloak.quarkus.runtime.configuration.mappers.HttpPropertyMappers;
|
import org.keycloak.quarkus.runtime.configuration.mappers.HttpPropertyMappers;
|
||||||
|
|
||||||
|
@ -46,10 +42,6 @@ public abstract class AbstractStartCommand extends AbstractCommand implements Ru
|
||||||
HostnameV2PropertyMappers.validateConfig();
|
HostnameV2PropertyMappers.validateConfig();
|
||||||
validateConfig();
|
validateConfig();
|
||||||
|
|
||||||
if (ConfigArgsConfigSource.getAllCliArgs().contains(OPTIMIZED_BUILD_OPTION_LONG) && !wasBuildEverRun()) {
|
|
||||||
executionError(spec.commandLine(), Messages.optimizedUsedForFirstStartup());
|
|
||||||
}
|
|
||||||
|
|
||||||
picocli.start(cmd);
|
picocli.start(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,15 +21,14 @@ import static org.keycloak.config.ClassLoaderOptions.QUARKUS_REMOVED_ARTIFACTS_P
|
||||||
import static org.keycloak.quarkus.runtime.Environment.getHomePath;
|
import static org.keycloak.quarkus.runtime.Environment.getHomePath;
|
||||||
import static org.keycloak.quarkus.runtime.Environment.isDevProfile;
|
import static org.keycloak.quarkus.runtime.Environment.isDevProfile;
|
||||||
import static org.keycloak.quarkus.runtime.cli.Picocli.println;
|
import static org.keycloak.quarkus.runtime.cli.Picocli.println;
|
||||||
import static org.keycloak.quarkus.runtime.configuration.ConfigArgsConfigSource.getAllCliArgs;
|
|
||||||
|
|
||||||
import io.quarkus.runtime.LaunchMode;
|
import io.quarkus.runtime.LaunchMode;
|
||||||
|
|
||||||
import org.keycloak.config.OptionCategory;
|
import org.keycloak.config.OptionCategory;
|
||||||
import org.keycloak.quarkus.runtime.Environment;
|
import org.keycloak.quarkus.runtime.Environment;
|
||||||
import org.keycloak.quarkus.runtime.Messages;
|
import org.keycloak.quarkus.runtime.Messages;
|
||||||
import org.keycloak.quarkus.runtime.configuration.Configuration;
|
import org.keycloak.quarkus.runtime.configuration.Configuration;
|
||||||
|
|
||||||
import io.quarkus.bootstrap.runner.QuarkusEntryPoint;
|
|
||||||
import io.quarkus.bootstrap.runner.RunnerClassLoader;
|
import io.quarkus.bootstrap.runner.RunnerClassLoader;
|
||||||
|
|
||||||
import io.smallrye.config.ConfigValue;
|
import io.smallrye.config.ConfigValue;
|
||||||
|
@ -68,7 +67,10 @@ public final class Build extends AbstractCommand implements Runnable {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
exitWithErrorIfDevProfileIsSetAndNotStartDev();
|
if (org.keycloak.common.util.Environment.getProfile() == null) {
|
||||||
|
Environment.setProfile(Environment.PROD_PROFILE_VALUE);
|
||||||
|
}
|
||||||
|
exitWithErrorIfDevProfileIsSet();
|
||||||
|
|
||||||
System.setProperty("quarkus.launch.rebuild", "true");
|
System.setProperty("quarkus.launch.rebuild", "true");
|
||||||
validateConfig();
|
validateConfig();
|
||||||
|
@ -94,7 +96,7 @@ public final class Build extends AbstractCommand implements Runnable {
|
||||||
|
|
||||||
private static void configureBuildClassLoader() {
|
private static void configureBuildClassLoader() {
|
||||||
// ignored artifacts must be set prior to starting re-augmentation
|
// ignored artifacts must be set prior to starting re-augmentation
|
||||||
Optional.ofNullable(Configuration.getCurrentBuiltTimeProperty(QUARKUS_REMOVED_ARTIFACTS_PROPERTY))
|
Optional.ofNullable(Configuration.getNonPersistedConfigValue(QUARKUS_REMOVED_ARTIFACTS_PROPERTY))
|
||||||
.map(ConfigValue::getValue)
|
.map(ConfigValue::getValue)
|
||||||
.ifPresent(s -> System.setProperty(QUARKUS_REMOVED_ARTIFACTS_PROPERTY, s));
|
.ifPresent(s -> System.setProperty(QUARKUS_REMOVED_ARTIFACTS_PROPERTY, s));
|
||||||
}
|
}
|
||||||
|
@ -110,9 +112,14 @@ public final class Build extends AbstractCommand implements Runnable {
|
||||||
return super.getOptionCategories();
|
return super.getOptionCategories();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void exitWithErrorIfDevProfileIsSetAndNotStartDev() {
|
private void exitWithErrorIfDevProfileIsSet() {
|
||||||
if (Environment.isDevProfile() && !getAllCliArgs().contains(StartDev.NAME)) {
|
if (Environment.isDevProfile()) {
|
||||||
executionError(spec.commandLine(), Messages.devProfileNotAllowedError(NAME));
|
String cmd = Environment.getParsedCommand().map(AbstractCommand::getName).orElse(getName());
|
||||||
|
// we allow start-dev, and import|export|bootstrap-admin --profile=dev
|
||||||
|
// but not start --profile=dev, nor build --profile=dev
|
||||||
|
if (Start.NAME.equals(cmd) || Build.NAME.equals(cmd)) {
|
||||||
|
executionError(spec.commandLine(), Messages.devProfileNotAllowedError(cmd));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ package org.keycloak.quarkus.runtime.cli.command;
|
||||||
import picocli.AutoComplete;
|
import picocli.AutoComplete;
|
||||||
import picocli.CommandLine.Command;
|
import picocli.CommandLine.Command;
|
||||||
|
|
||||||
@Command(name = "completion",
|
@Command(name = Completion.NAME,
|
||||||
header = "Generate bash/zsh completion script for ${ROOT-COMMAND-NAME:-the root command of this command}.",
|
header = "Generate bash/zsh completion script for ${ROOT-COMMAND-NAME:-the root command of this command}.",
|
||||||
description = {
|
description = {
|
||||||
"",
|
"",
|
||||||
|
@ -29,4 +29,6 @@ import picocli.CommandLine.Command;
|
||||||
"",
|
"",
|
||||||
" source <(${PARENT-COMMAND-FULL-NAME:-$PARENTCOMMAND} ${COMMAND-NAME})"})
|
" source <(${PARENT-COMMAND-FULL-NAME:-$PARENTCOMMAND} ${COMMAND-NAME})"})
|
||||||
public class Completion extends AutoComplete.GenerateCompletion {
|
public class Completion extends AutoComplete.GenerateCompletion {
|
||||||
|
|
||||||
|
public static final String NAME = "completion";
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,17 +17,13 @@
|
||||||
|
|
||||||
package org.keycloak.quarkus.runtime.cli.command;
|
package org.keycloak.quarkus.runtime.cli.command;
|
||||||
|
|
||||||
import static org.keycloak.quarkus.runtime.Environment.getCurrentOrPersistedProfile;
|
|
||||||
import static org.keycloak.quarkus.runtime.Environment.setProfile;
|
|
||||||
import static org.keycloak.quarkus.runtime.configuration.Configuration.getConfigValue;
|
import static org.keycloak.quarkus.runtime.configuration.Configuration.getConfigValue;
|
||||||
import static org.keycloak.quarkus.runtime.configuration.Configuration.getPropertyNames;
|
import static org.keycloak.quarkus.runtime.configuration.Configuration.getPropertyNames;
|
||||||
import static org.keycloak.quarkus.runtime.configuration.Configuration.getRuntimeProperty;
|
|
||||||
import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMappers.maskValue;
|
import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMappers.maskValue;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.StreamSupport;
|
import java.util.stream.StreamSupport;
|
||||||
|
@ -60,23 +56,18 @@ public final class ShowConfig extends AbstractCommand implements Runnable {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
System.setProperty("kc.show.config", filter);
|
String profile = Environment.updateProfile(true);
|
||||||
String configArgs = System.getProperty("kc.show.config");
|
|
||||||
String profile = Optional.ofNullable(getCurrentOrPersistedProfile()).orElse(Environment.PROD_PROFILE_VALUE);
|
|
||||||
setProfile(profile);
|
|
||||||
|
|
||||||
Map<String, Set<String>> properties = getPropertiesByGroup();
|
Map<String, Set<String>> properties = getPropertiesByGroup();
|
||||||
printRunTimeConfig(properties, profile);
|
printRunTimeConfig(properties, profile);
|
||||||
|
|
||||||
if (configArgs.equalsIgnoreCase("all")) {
|
if (filter.equalsIgnoreCase("all")) {
|
||||||
spec.commandLine().getOut().println("Quarkus Configuration:");
|
spec.commandLine().getOut().println("Quarkus Configuration:");
|
||||||
properties.get(MicroProfileConfigProvider.NS_QUARKUS).stream().sorted()
|
properties.get(MicroProfileConfigProvider.NS_QUARKUS).stream().sorted()
|
||||||
.forEachOrdered(this::printProperty);
|
.forEachOrdered(this::printProperty);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Boolean.getBoolean("kc.show.config.runtime")) {
|
Quarkus.asyncExit(0);
|
||||||
Quarkus.asyncExit(0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void printRunTimeConfig(Map<String, Set<String>> properties, String profile) {
|
private void printRunTimeConfig(Map<String, Set<String>> properties, String profile) {
|
||||||
|
@ -119,12 +110,8 @@ public final class ShowConfig extends AbstractCommand implements Runnable {
|
||||||
|
|
||||||
PropertyMapper<?> mapper = PropertyMappers.getMapper(property);
|
PropertyMapper<?> mapper = PropertyMappers.getMapper(property);
|
||||||
|
|
||||||
if (mapper == null) {
|
if (mapper == null && configValue.getSourceName().equals("SysPropConfigSource") && !allowedSystemPropertyKeys.contains(property)) {
|
||||||
if (configValue.getSourceName().equals("SysPropConfigSource") && !allowedSystemPropertyKeys.contains(property)) {
|
return; // most system properties are internally used, and not relevant during show-config
|
||||||
return; // most system properties are internally used, and not relevant during show-config
|
|
||||||
}
|
|
||||||
} else if (mapper.isRunTime()) {
|
|
||||||
value = getRuntimeProperty(property).orElse(value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
value = maskValue(configValue.getName(), value, configValue.getConfigSourceName());
|
value = maskValue(configValue.getName(), value, configValue.getConfigSourceName());
|
||||||
|
|
|
@ -17,9 +17,7 @@
|
||||||
|
|
||||||
package org.keycloak.quarkus.runtime.cli.command;
|
package org.keycloak.quarkus.runtime.cli.command;
|
||||||
|
|
||||||
import static org.keycloak.quarkus.runtime.Environment.setProfile;
|
|
||||||
import static org.keycloak.quarkus.runtime.cli.command.AbstractStartCommand.OPTIMIZED_BUILD_OPTION_LONG;
|
import static org.keycloak.quarkus.runtime.cli.command.AbstractStartCommand.OPTIMIZED_BUILD_OPTION_LONG;
|
||||||
import static org.keycloak.quarkus.runtime.configuration.Configuration.getRawPersistedProperty;
|
|
||||||
|
|
||||||
import org.keycloak.quarkus.runtime.Environment;
|
import org.keycloak.quarkus.runtime.Environment;
|
||||||
import org.keycloak.quarkus.runtime.Messages;
|
import org.keycloak.quarkus.runtime.Messages;
|
||||||
|
@ -27,8 +25,6 @@ import org.keycloak.quarkus.runtime.Messages;
|
||||||
import picocli.CommandLine;
|
import picocli.CommandLine;
|
||||||
import picocli.CommandLine.Command;
|
import picocli.CommandLine.Command;
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
@Command(name = Start.NAME,
|
@Command(name = Start.NAME,
|
||||||
header = "Start the server.",
|
header = "Start the server.",
|
||||||
description = {
|
description = {
|
||||||
|
@ -52,24 +48,12 @@ public final class Start extends AbstractStartCommand implements Runnable {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doBeforeRun() {
|
protected void doBeforeRun() {
|
||||||
devProfileNotAllowedError();
|
Environment.updateProfile(true);
|
||||||
}
|
if (Environment.isDevProfile()) {
|
||||||
|
|
||||||
private void devProfileNotAllowedError() {
|
|
||||||
if (isDevProfileNotAllowed()) {
|
|
||||||
executionError(spec.commandLine(), Messages.devProfileNotAllowedError(NAME));
|
executionError(spec.commandLine(), Messages.devProfileNotAllowedError(NAME));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isDevProfileNotAllowed() {
|
|
||||||
Optional<String> currentProfile = Optional.ofNullable(org.keycloak.common.util.Environment.getProfile());
|
|
||||||
Optional<String> persistedProfile = getRawPersistedProperty("kc.profile");
|
|
||||||
|
|
||||||
setProfile(currentProfile.orElse(persistedProfile.orElse("prod")));
|
|
||||||
|
|
||||||
return Environment.isDevProfile();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean includeRuntime() {
|
public boolean includeRuntime() {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
|
|
||||||
package org.keycloak.quarkus.runtime.configuration;
|
package org.keycloak.quarkus.runtime.configuration;
|
||||||
|
|
||||||
import static org.keycloak.quarkus.runtime.Environment.getProfileOrDefault;
|
|
||||||
import static org.keycloak.quarkus.runtime.cli.Picocli.ARG_PREFIX;
|
import static org.keycloak.quarkus.runtime.cli.Picocli.ARG_PREFIX;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -28,7 +27,6 @@ import io.smallrye.config.ConfigValue;
|
||||||
import io.smallrye.config.SmallRyeConfig;
|
import io.smallrye.config.SmallRyeConfig;
|
||||||
|
|
||||||
import org.eclipse.microprofile.config.spi.ConfigProviderResolver;
|
import org.eclipse.microprofile.config.spi.ConfigProviderResolver;
|
||||||
import org.eclipse.microprofile.config.spi.ConfigSource;
|
|
||||||
import org.keycloak.config.Option;
|
import org.keycloak.config.Option;
|
||||||
import org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper;
|
import org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper;
|
||||||
import org.keycloak.quarkus.runtime.configuration.mappers.PropertyMappers;
|
import org.keycloak.quarkus.runtime.configuration.mappers.PropertyMappers;
|
||||||
|
@ -43,7 +41,7 @@ public final class Configuration {
|
||||||
|
|
||||||
public static final char OPTION_PART_SEPARATOR_CHAR = '-';
|
public static final char OPTION_PART_SEPARATOR_CHAR = '-';
|
||||||
public static final String OPTION_PART_SEPARATOR = String.valueOf(OPTION_PART_SEPARATOR_CHAR);
|
public static final String OPTION_PART_SEPARATOR = String.valueOf(OPTION_PART_SEPARATOR_CHAR);
|
||||||
private static final String KC_OPTIMIZED = NS_KEYCLOAK_PREFIX + "optimized";
|
public static final String KC_OPTIMIZED = NS_KEYCLOAK_PREFIX + "optimized";
|
||||||
|
|
||||||
private Configuration() {
|
private Configuration() {
|
||||||
|
|
||||||
|
@ -79,34 +77,9 @@ public final class Configuration {
|
||||||
return (SmallRyeConfig) ConfigProviderResolver.instance().getConfig();
|
return (SmallRyeConfig) ConfigProviderResolver.instance().getConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Optional<String> getBuildTimeProperty(String name) {
|
/**
|
||||||
Optional<String> value = getRawPersistedProperty(name);
|
* Raw persisted keycloak properties will match the resolved value of what was originally specified by the user
|
||||||
|
*/
|
||||||
if (value.isEmpty()) {
|
|
||||||
PropertyMapper<?> mapper = PropertyMappers.getMapper(name);
|
|
||||||
|
|
||||||
if (mapper != null) {
|
|
||||||
value = getRawPersistedProperty(mapper.getFrom());
|
|
||||||
|
|
||||||
if (value.isEmpty() && mapper.getTo() != null) {
|
|
||||||
value = getRawPersistedProperty(mapper.getTo());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.isEmpty()) {
|
|
||||||
String profile = org.keycloak.common.util.Environment.getProfile();
|
|
||||||
|
|
||||||
if (profile == null) {
|
|
||||||
profile = getConfig().getRawValue(org.keycloak.common.util.Environment.PROFILE);
|
|
||||||
}
|
|
||||||
|
|
||||||
value = getRawPersistedProperty("%" + profile + "." + name);
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Optional<String> getRawPersistedProperty(String name) {
|
public static Optional<String> getRawPersistedProperty(String name) {
|
||||||
return Optional.ofNullable(PersistedConfigSource.getInstance().getValue(name));
|
return Optional.ofNullable(PersistedConfigSource.getInstance().getValue(name));
|
||||||
}
|
}
|
||||||
|
@ -170,26 +143,6 @@ public final class Configuration {
|
||||||
return mapper.getTo() == null ? mapper.getFrom() : mapper.getTo();
|
return mapper.getTo() == null ? mapper.getFrom() : mapper.getTo();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Optional<String> getRuntimeProperty(String name) {
|
|
||||||
for (ConfigSource configSource : getConfig().getConfigSources()) {
|
|
||||||
if (PersistedConfigSource.NAME.equals(configSource.getName())) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
String value = getValue(configSource, name);
|
|
||||||
|
|
||||||
if (value == null) {
|
|
||||||
value = getValue(configSource, getMappedPropertyName(name));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value != null) {
|
|
||||||
return Optional.of(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String toEnvVarFormat(String key) {
|
public static String toEnvVarFormat(String key) {
|
||||||
return replaceNonAlphanumericByUnderscores(key).toUpperCase();
|
return replaceNonAlphanumericByUnderscores(key).toUpperCase();
|
||||||
}
|
}
|
||||||
|
@ -233,16 +186,6 @@ public final class Configuration {
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String getValue(ConfigSource configSource, String name) {
|
|
||||||
String value = configSource.getValue("%".concat(getProfileOrDefault("prod").concat(".").concat(name)));
|
|
||||||
|
|
||||||
if (value == null) {
|
|
||||||
value = configSource.getValue(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isOptimized() {
|
public static boolean isOptimized() {
|
||||||
return Configuration.getRawPersistedProperty(KC_OPTIMIZED).isPresent();
|
return Configuration.getRawPersistedProperty(KC_OPTIMIZED).isPresent();
|
||||||
}
|
}
|
||||||
|
@ -251,7 +194,7 @@ public final class Configuration {
|
||||||
properties.put(Configuration.KC_OPTIMIZED, Boolean.TRUE.toString());
|
properties.put(Configuration.KC_OPTIMIZED, Boolean.TRUE.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ConfigValue getCurrentBuiltTimeProperty(String name) {
|
public static ConfigValue getNonPersistedConfigValue(String name) {
|
||||||
return PersistedConfigSource.getInstance().runWithDisabled(() -> getConfigValue(name));
|
return PersistedConfigSource.getInstance().runWithDisabled(() -> getConfigValue(name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,6 @@ import io.smallrye.config.AbstractLocationConfigSourceLoader;
|
||||||
import io.smallrye.config.PropertiesConfigSource;
|
import io.smallrye.config.PropertiesConfigSource;
|
||||||
import io.smallrye.config.common.utils.ConfigSourceUtil;
|
import io.smallrye.config.common.utils.ConfigSourceUtil;
|
||||||
|
|
||||||
import static org.keycloak.common.util.StringPropertyReplacer.replaceProperties;
|
|
||||||
import static org.keycloak.quarkus.runtime.configuration.Configuration.getMappedPropertyName;
|
import static org.keycloak.quarkus.runtime.configuration.Configuration.getMappedPropertyName;
|
||||||
import static org.keycloak.quarkus.runtime.configuration.MicroProfileConfigProvider.NS_KEYCLOAK;
|
import static org.keycloak.quarkus.runtime.configuration.MicroProfileConfigProvider.NS_KEYCLOAK;
|
||||||
import static org.keycloak.quarkus.runtime.configuration.MicroProfileConfigProvider.NS_KEYCLOAK_PREFIX;
|
import static org.keycloak.quarkus.runtime.configuration.MicroProfileConfigProvider.NS_KEYCLOAK_PREFIX;
|
||||||
|
@ -51,6 +50,8 @@ import static org.keycloak.quarkus.runtime.configuration.MicroProfileConfigProvi
|
||||||
*/
|
*/
|
||||||
public class KeycloakPropertiesConfigSource extends AbstractLocationConfigSourceLoader {
|
public class KeycloakPropertiesConfigSource extends AbstractLocationConfigSourceLoader {
|
||||||
|
|
||||||
|
public static final int PROPERTIES_FILE_ORDINAL = 475;
|
||||||
|
|
||||||
private static final Pattern DOT_SPLIT = Pattern.compile("\\.");
|
private static final Pattern DOT_SPLIT = Pattern.compile("\\.");
|
||||||
private static final String KEYCLOAK_CONFIG_FILE_ENV = "KC_CONFIG_FILE";
|
private static final String KEYCLOAK_CONFIG_FILE_ENV = "KC_CONFIG_FILE";
|
||||||
private static final String KEYCLOAK_CONF_FILE = "keycloak.conf";
|
private static final String KEYCLOAK_CONF_FILE = "keycloak.conf";
|
||||||
|
@ -124,7 +125,7 @@ public class KeycloakPropertiesConfigSource extends AbstractLocationConfigSource
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ConfigSource> getConfigSources(final ClassLoader classLoader, Path configFile) {
|
public List<ConfigSource> getConfigSources(final ClassLoader classLoader, Path configFile) {
|
||||||
return loadConfigSources(configFile.toUri().toString(), 450, classLoader);
|
return loadConfigSources(configFile.toUri().toString(), PROPERTIES_FILE_ORDINAL, classLoader);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -17,8 +17,6 @@
|
||||||
|
|
||||||
package org.keycloak.quarkus.runtime.configuration;
|
package org.keycloak.quarkus.runtime.configuration;
|
||||||
|
|
||||||
import static java.lang.Boolean.parseBoolean;
|
|
||||||
import static org.keycloak.quarkus.runtime.configuration.Configuration.getRawPersistedProperty;
|
|
||||||
import static org.keycloak.quarkus.runtime.configuration.MicroProfileConfigProvider.NS_QUARKUS;
|
import static org.keycloak.quarkus.runtime.configuration.MicroProfileConfigProvider.NS_QUARKUS;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
@ -36,7 +34,6 @@ import org.eclipse.microprofile.config.spi.ConfigSourceProvider;
|
||||||
import org.keycloak.quarkus.runtime.Environment;
|
import org.keycloak.quarkus.runtime.Environment;
|
||||||
|
|
||||||
import io.smallrye.config.AbstractLocationConfigSourceLoader;
|
import io.smallrye.config.AbstractLocationConfigSourceLoader;
|
||||||
import io.smallrye.config.ConfigValue;
|
|
||||||
import io.smallrye.config.PropertiesConfigSource;
|
import io.smallrye.config.PropertiesConfigSource;
|
||||||
import io.smallrye.config.common.utils.ConfigSourceUtil;
|
import io.smallrye.config.common.utils.ConfigSourceUtil;
|
||||||
|
|
||||||
|
@ -46,23 +43,8 @@ import io.smallrye.config.common.utils.ConfigSourceUtil;
|
||||||
public final class QuarkusPropertiesConfigSource extends AbstractLocationConfigSourceLoader implements ConfigSourceProvider {
|
public final class QuarkusPropertiesConfigSource extends AbstractLocationConfigSourceLoader implements ConfigSourceProvider {
|
||||||
|
|
||||||
private static final String FILE_NAME = "quarkus.properties";
|
private static final String FILE_NAME = "quarkus.properties";
|
||||||
public static final String QUARKUS_PROPERTY_ENABLED = "kc.quarkus-properties-enabled";
|
|
||||||
public static final String NAME = "QuarkusProperties";
|
public static final String NAME = "QuarkusProperties";
|
||||||
|
|
||||||
//for auto-build working with multiple datasources
|
|
||||||
public static final String QUARKUS_DATASOURCE_BUILDTIME_REGEX = "^quarkus\\.datasource\\.[A-Za-z0-9\\-_]+\\.(db-kind|jdbc\\.driver|jdbc\\.transactions|jdbc\\.enable-metrics)$";
|
|
||||||
|
|
||||||
public static boolean isSameSource(ConfigValue value) {
|
|
||||||
if (value == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// workaround for https://github.com/smallrye/smallrye-config/issues/1207
|
|
||||||
// replace by the following line when fixed:
|
|
||||||
// return NAME.equals(value.getConfigSourceName());
|
|
||||||
return value.getConfigSourceName() != null && value.getConfigSourceName().endsWith(FILE_NAME);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Path getConfigurationFile() {
|
public static Path getConfigurationFile() {
|
||||||
String homeDir = Environment.getHomeDir();
|
String homeDir = Environment.getHomeDir();
|
||||||
|
|
||||||
|
@ -77,6 +59,8 @@ public final class QuarkusPropertiesConfigSource extends AbstractLocationConfigS
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean loadingFile;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String[] getFileExtensions() {
|
protected String[] getFileExtensions() {
|
||||||
return new String[] { "properties" };
|
return new String[] { "properties" };
|
||||||
|
@ -84,10 +68,11 @@ public final class QuarkusPropertiesConfigSource extends AbstractLocationConfigS
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected ConfigSource loadConfigSource(URL url, int ordinal) throws IOException {
|
protected ConfigSource loadConfigSource(URL url, int ordinal) throws IOException {
|
||||||
return new PropertiesConfigSource(ConfigSourceUtil.urlToMap(url), FILE_NAME, ordinal) {
|
String name = loadingFile ? NAME : (NAME + " " + url);
|
||||||
|
return new PropertiesConfigSource(ConfigSourceUtil.urlToMap(url), name, ordinal) {
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return NAME;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -102,7 +87,7 @@ public final class QuarkusPropertiesConfigSource extends AbstractLocationConfigS
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<ConfigSource> getConfigSources(final ClassLoader classLoader) {
|
public synchronized List<ConfigSource> getConfigSources(final ClassLoader classLoader) {
|
||||||
List<ConfigSource> configSources = new ArrayList<>();
|
List<ConfigSource> configSources = new ArrayList<>();
|
||||||
|
|
||||||
configSources.addAll(loadConfigSources("META-INF/services/" + FILE_NAME, 450, classLoader));
|
configSources.addAll(loadConfigSources("META-INF/services/" + FILE_NAME, 450, classLoader));
|
||||||
|
@ -110,7 +95,12 @@ public final class QuarkusPropertiesConfigSource extends AbstractLocationConfigS
|
||||||
Path configFile = getConfigurationFile();
|
Path configFile = getConfigurationFile();
|
||||||
|
|
||||||
if (configFile != null) {
|
if (configFile != null) {
|
||||||
configSources.addAll(loadConfigSources(configFile.toUri().toString(), 500, classLoader));
|
loadingFile = true;
|
||||||
|
try {
|
||||||
|
configSources.addAll(loadConfigSources(configFile.toUri().toString(), KeycloakPropertiesConfigSource.PROPERTIES_FILE_ORDINAL, classLoader));
|
||||||
|
} finally {
|
||||||
|
loadingFile = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return configSources;
|
return configSources;
|
||||||
|
|
|
@ -34,6 +34,7 @@ import java.util.stream.Stream;
|
||||||
|
|
||||||
import io.smallrye.config.ConfigSourceInterceptorContext;
|
import io.smallrye.config.ConfigSourceInterceptorContext;
|
||||||
import io.smallrye.config.ConfigValue;
|
import io.smallrye.config.ConfigValue;
|
||||||
|
import io.smallrye.config.ConfigValue.ConfigValueBuilder;
|
||||||
|
|
||||||
import org.keycloak.config.DeprecatedMetadata;
|
import org.keycloak.config.DeprecatedMetadata;
|
||||||
import org.keycloak.config.Option;
|
import org.keycloak.config.Option;
|
||||||
|
@ -131,9 +132,12 @@ public class PropertyMapper<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config != null && config.getValue() != null) {
|
if (config != null && config.getValue() != null) {
|
||||||
config = transformValue(name, config.getValue(), context, config.getConfigSourceName(), parentValue);
|
config = transformValue(name, config, context, parentValue);
|
||||||
} else {
|
} else {
|
||||||
config = transformValue(name, this.option.getDefaultValue().map(Option::getDefaultValueString).orElse(null), context, null, false);
|
String defaultValue = this.option.getDefaultValue().map(Option::getDefaultValueString).orElse(null);
|
||||||
|
config = transformValue(name, new ConfigValueBuilder().withName(name)
|
||||||
|
.withValue(defaultValue).withRawValue(defaultValue).build(),
|
||||||
|
context, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config != null) {
|
if (config != null) {
|
||||||
|
@ -232,24 +236,27 @@ public class PropertyMapper<T> {
|
||||||
return option.getDeprecatedMetadata();
|
return option.getDeprecatedMetadata();
|
||||||
}
|
}
|
||||||
|
|
||||||
private ConfigValue transformValue(String name, String value, ConfigSourceInterceptorContext context, String configSourceName, boolean parentValue) {
|
private ConfigValue transformValue(String name, ConfigValue configValue, ConfigSourceInterceptorContext context, boolean parentValue) {
|
||||||
|
String value = configValue.getValue();
|
||||||
String mappedValue = value;
|
String mappedValue = value;
|
||||||
|
|
||||||
|
boolean mapped = false;
|
||||||
var theMapper = parentValue ? this.parentMapper : this.mapper;
|
var theMapper = parentValue ? this.parentMapper : this.mapper;
|
||||||
if (theMapper != null && (!name.equals(getFrom()) || parentValue)) {
|
if (theMapper != null && (!name.equals(getFrom()) || parentValue)) {
|
||||||
mappedValue = theMapper.apply(value, context);
|
mappedValue = theMapper.apply(value, context);
|
||||||
|
mapped = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value == null && mappedValue == null) {
|
if (value == null && mappedValue == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ConfigValue.builder()
|
if (!mapped && name.equals(configValue.getName())) {
|
||||||
.withName(name)
|
return configValue;
|
||||||
.withValue(mappedValue)
|
}
|
||||||
.withRawValue(value)
|
|
||||||
.withConfigSourceName(configSourceName)
|
// by unsetting the ordinal this will not be seen as directly modified by the user
|
||||||
.build();
|
return configValue.from().withValue(mappedValue).withRawValue(value).withConfigSourceOrdinal(0).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private ConfigValue convertValue(ConfigValue configValue) {
|
private ConfigValue convertValue(ConfigValue configValue) {
|
||||||
|
|
|
@ -13,7 +13,6 @@ import org.keycloak.quarkus.runtime.cli.PropertyException;
|
||||||
import org.keycloak.quarkus.runtime.cli.command.AbstractCommand;
|
import org.keycloak.quarkus.runtime.cli.command.AbstractCommand;
|
||||||
import org.keycloak.quarkus.runtime.cli.command.Build;
|
import org.keycloak.quarkus.runtime.cli.command.Build;
|
||||||
import org.keycloak.quarkus.runtime.cli.command.ShowConfig;
|
import org.keycloak.quarkus.runtime.cli.command.ShowConfig;
|
||||||
import org.keycloak.quarkus.runtime.configuration.ConfigArgsConfigSource;
|
|
||||||
import org.keycloak.quarkus.runtime.configuration.DisabledMappersInterceptor;
|
import org.keycloak.quarkus.runtime.configuration.DisabledMappersInterceptor;
|
||||||
import org.keycloak.quarkus.runtime.configuration.PersistedConfigSource;
|
import org.keycloak.quarkus.runtime.configuration.PersistedConfigSource;
|
||||||
|
|
||||||
|
@ -78,32 +77,10 @@ public final class PropertyMappers {
|
||||||
return getMapperOrDefault(name, PropertyMapper.IDENTITY).getConfigValue(name, context);
|
return getMapperOrDefault(name, PropertyMapper.IDENTITY).getConfigValue(name, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isBuildTimeProperty(String name) {
|
|
||||||
if (isFeaturesBuildTimeProperty(name) || isSpiBuildTimeProperty(name)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
final PropertyMapper<?> mapper = getMapperOrDefault(name, null);
|
|
||||||
boolean isBuildTimeProperty = mapper == null ? false : mapper.isBuildTime();
|
|
||||||
|
|
||||||
return isBuildTimeProperty
|
|
||||||
&& !"kc.version".equals(name)
|
|
||||||
&& !"kc.home.dir".equals(name)
|
|
||||||
&& !"kc.config.file".equals(name)
|
|
||||||
&& !org.keycloak.common.util.Environment.PROFILE.equals(name)
|
|
||||||
&& !"kc.show.config".equals(name)
|
|
||||||
&& !"kc.show.config.runtime".equals(name)
|
|
||||||
&& !"kc.config-file".equals(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isSpiBuildTimeProperty(String name) {
|
public static boolean isSpiBuildTimeProperty(String name) {
|
||||||
return name.startsWith(KC_SPI_PREFIX) && (name.endsWith("-provider") || name.endsWith("-enabled") || name.endsWith("-provider-default"));
|
return name.startsWith(KC_SPI_PREFIX) && (name.endsWith("-provider") || name.endsWith("-enabled") || name.endsWith("-provider-default"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isFeaturesBuildTimeProperty(String name) {
|
|
||||||
return name.startsWith("kc.features");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Map<OptionCategory, List<PropertyMapper<?>>> getRuntimeMappers() {
|
public static Map<OptionCategory, List<PropertyMapper<?>>> getRuntimeMappers() {
|
||||||
return MAPPERS.getRuntimeMappers();
|
return MAPPERS.getRuntimeMappers();
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,13 +21,19 @@ import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import static org.hamcrest.Matchers.containsString;
|
import static org.hamcrest.Matchers.containsString;
|
||||||
import static org.hamcrest.Matchers.not;
|
import static org.hamcrest.Matchers.not;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.keycloak.quarkus.runtime.Environment;
|
||||||
import org.keycloak.quarkus.runtime.cli.Picocli;
|
import org.keycloak.quarkus.runtime.cli.Picocli;
|
||||||
import org.keycloak.quarkus.runtime.configuration.ConfigArgsConfigSource;
|
import org.keycloak.quarkus.runtime.configuration.ConfigArgsConfigSource;
|
||||||
import org.keycloak.quarkus.runtime.configuration.test.AbstractConfigurationTest;
|
import org.keycloak.quarkus.runtime.configuration.test.AbstractConfigurationTest;
|
||||||
|
@ -45,6 +51,8 @@ public class PicocliTest extends AbstractConfigurationTest {
|
||||||
final StringWriter out = new StringWriter();
|
final StringWriter out = new StringWriter();
|
||||||
SmallRyeConfig config;
|
SmallRyeConfig config;
|
||||||
int exitCode = Integer.MAX_VALUE;
|
int exitCode = Integer.MAX_VALUE;
|
||||||
|
boolean reaug;
|
||||||
|
private Properties buildProps;
|
||||||
|
|
||||||
String getErrString() {
|
String getErrString() {
|
||||||
return normalize(err);
|
return normalize(err);
|
||||||
|
@ -76,10 +84,6 @@ public class PicocliTest extends AbstractConfigurationTest {
|
||||||
this.exitCode = exitCode;
|
this.exitCode = exitCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected int runReAugmentationIfNeeded(List<String> cliArgs, CommandLine cmd, CommandLine currentCommand) {
|
|
||||||
throw new AssertionError("Should not reaugment");
|
|
||||||
};
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void parseAndRun(List<String> cliArgs) {
|
public void parseAndRun(List<String> cliArgs) {
|
||||||
config = createConfig();
|
config = createConfig();
|
||||||
|
@ -93,7 +97,8 @@ public class PicocliTest extends AbstractConfigurationTest {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void build() throws Throwable {
|
public void build() throws Throwable {
|
||||||
// skip
|
reaug = true;
|
||||||
|
this.buildProps = getNonPersistedBuildTimeOptions();
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -255,4 +260,112 @@ public class PicocliTest extends AbstractConfigurationTest {
|
||||||
assertThat(nonRunningPicocli.getOutString(), containsString("The following run time options were found, but will be ignored during build time: kc.spi-something-pass"));
|
assertThat(nonRunningPicocli.getOutString(), containsString("The following run time options were found, but will be ignored during build time: kc.spi-something-pass"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void failBuildDev() {
|
||||||
|
NonRunningPicocli nonRunningPicocli = pseudoLaunch("--profile=dev", "build");
|
||||||
|
assertThat(nonRunningPicocli.getErrString(), containsString("You can not 'build' the server in development mode."));
|
||||||
|
assertEquals(CommandLine.ExitCode.SOFTWARE, nonRunningPicocli.exitCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void failStartBuildDev() {
|
||||||
|
NonRunningPicocli nonRunningPicocli = pseudoLaunch("--profile=dev", "start");
|
||||||
|
assertThat(nonRunningPicocli.getErrString(), containsString("You can not 'start' the server in development mode."));
|
||||||
|
assertEquals(CommandLine.ExitCode.SOFTWARE, nonRunningPicocli.exitCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void failIfOptimizedUsedForFirstStartupExport() {
|
||||||
|
NonRunningPicocli nonRunningPicocli = pseudoLaunch("export", "--optimized", "--dir=data");
|
||||||
|
assertEquals(CommandLine.ExitCode.USAGE, nonRunningPicocli.exitCode);
|
||||||
|
assertThat(nonRunningPicocli.getErrString(), containsString("The '--optimized' flag was used for first ever server start."));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReaugFromProdToDev() {
|
||||||
|
build("build");
|
||||||
|
|
||||||
|
Environment.setRebuildCheck(); // will be reset by the system properties logic
|
||||||
|
NonRunningPicocli nonRunningPicocli = pseudoLaunch("start-dev", "--hostname=name", "--http-enabled=true");
|
||||||
|
assertEquals(CommandLine.ExitCode.OK, nonRunningPicocli.exitCode);
|
||||||
|
assertTrue(nonRunningPicocli.reaug);
|
||||||
|
assertEquals("dev", nonRunningPicocli.buildProps.getProperty(org.keycloak.common.util.Environment.PROFILE));;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs a fake build to setup the state of the persisted build properties
|
||||||
|
*/
|
||||||
|
private void build(String... args) {
|
||||||
|
if (Stream.of(args).anyMatch("start-dev"::equals)) {
|
||||||
|
Environment.setRebuildCheck(); // auto-build
|
||||||
|
}
|
||||||
|
NonRunningPicocli nonRunningPicocli = pseudoLaunch(args);
|
||||||
|
assertTrue(nonRunningPicocli.reaug);
|
||||||
|
assertEquals(CommandLine.ExitCode.OK, nonRunningPicocli.exitCode);
|
||||||
|
onAfter();
|
||||||
|
addPersistedConfigValues((Map)nonRunningPicocli.buildProps);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReaugFromProdToDevExport() {
|
||||||
|
build("build");
|
||||||
|
|
||||||
|
Environment.setRebuildCheck(); // will be reset by the system properties logic
|
||||||
|
NonRunningPicocli nonRunningPicocli = pseudoLaunch("--profile=dev", "export", "--file=file");
|
||||||
|
assertEquals(CommandLine.ExitCode.OK, nonRunningPicocli.exitCode);
|
||||||
|
assertTrue(nonRunningPicocli.reaug);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNoReaugFromProdToExport() {
|
||||||
|
build("build");
|
||||||
|
|
||||||
|
Environment.setRebuildCheck(); // will be reset by the system properties logic
|
||||||
|
NonRunningPicocli nonRunningPicocli = pseudoLaunch("export", "--file=file");
|
||||||
|
assertEquals(CommandLine.ExitCode.OK, nonRunningPicocli.exitCode);
|
||||||
|
assertFalse(nonRunningPicocli.reaug);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReaugFromDevToProd() {
|
||||||
|
build("start-dev");
|
||||||
|
|
||||||
|
Environment.setRebuildCheck(); // will be reset by the system properties logic
|
||||||
|
NonRunningPicocli nonRunningPicocli = pseudoLaunch("start", "--hostname=name", "--http-enabled=true");
|
||||||
|
assertEquals(CommandLine.ExitCode.OK, nonRunningPicocli.exitCode);
|
||||||
|
assertTrue(nonRunningPicocli.reaug);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNoReaugFromDevToDevExport() {
|
||||||
|
build("start-dev");
|
||||||
|
|
||||||
|
Environment.setRebuildCheck(); // will be reset by the system properties logic
|
||||||
|
NonRunningPicocli nonRunningPicocli = pseudoLaunch("--profile=dev", "export", "--file=file");
|
||||||
|
assertEquals(CommandLine.ExitCode.OK, nonRunningPicocli.exitCode);
|
||||||
|
assertFalse(nonRunningPicocli.reaug);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReaugFromDevToProdExport() {
|
||||||
|
build("start-dev");
|
||||||
|
|
||||||
|
Environment.setRebuildCheck(); // will be reset by the system properties logic
|
||||||
|
NonRunningPicocli nonRunningPicocli = pseudoLaunch("export", "--file=file");
|
||||||
|
assertEquals(CommandLine.ExitCode.OK, nonRunningPicocli.exitCode);
|
||||||
|
assertTrue(nonRunningPicocli.reaug);
|
||||||
|
assertEquals("prod", nonRunningPicocli.buildProps.getProperty(org.keycloak.common.util.Environment.PROFILE));;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOptimizedReaugmentationMessage() {
|
||||||
|
build("build");
|
||||||
|
|
||||||
|
Environment.setRebuildCheck(); // will be reset by the system properties logic
|
||||||
|
NonRunningPicocli nonRunningPicocli = pseudoLaunch("start", "--features=docker", "--hostname=name", "--http-enabled=true");
|
||||||
|
assertEquals(CommandLine.ExitCode.OK, nonRunningPicocli.exitCode);
|
||||||
|
assertThat(nonRunningPicocli.getOutString(), containsString("features=<unset> > features=docker"));
|
||||||
|
assertTrue(nonRunningPicocli.reaug);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,8 @@ import io.quarkus.runtime.configuration.ConfigUtils;
|
||||||
import io.smallrye.config.ConfigValue;
|
import io.smallrye.config.ConfigValue;
|
||||||
import io.smallrye.config.SmallRyeConfig;
|
import io.smallrye.config.SmallRyeConfig;
|
||||||
import io.smallrye.config.SmallRyeConfigProviderResolver;
|
import io.smallrye.config.SmallRyeConfigProviderResolver;
|
||||||
|
import io.smallrye.config.ConfigValue.ConfigValueBuilder;
|
||||||
|
|
||||||
import org.eclipse.microprofile.config.ConfigProvider;
|
import org.eclipse.microprofile.config.ConfigProvider;
|
||||||
import org.eclipse.microprofile.config.spi.ConfigProviderResolver;
|
import org.eclipse.microprofile.config.spi.ConfigProviderResolver;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
|
@ -30,6 +32,7 @@ import org.keycloak.quarkus.runtime.configuration.ConfigArgsConfigSource;
|
||||||
import org.keycloak.quarkus.runtime.configuration.Configuration;
|
import org.keycloak.quarkus.runtime.configuration.Configuration;
|
||||||
import org.keycloak.quarkus.runtime.configuration.KeycloakConfigSourceProvider;
|
import org.keycloak.quarkus.runtime.configuration.KeycloakConfigSourceProvider;
|
||||||
import org.keycloak.quarkus.runtime.configuration.MicroProfileConfigProvider;
|
import org.keycloak.quarkus.runtime.configuration.MicroProfileConfigProvider;
|
||||||
|
import org.keycloak.quarkus.runtime.configuration.PersistedConfigSource;
|
||||||
import org.keycloak.quarkus.runtime.configuration.mappers.PropertyMappers;
|
import org.keycloak.quarkus.runtime.configuration.mappers.PropertyMappers;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
|
@ -113,6 +116,7 @@ public abstract class AbstractConfigurationTest {
|
||||||
SmallRyeConfigProviderResolver.class.cast(ConfigProviderResolver.instance()).releaseConfig(ConfigProvider.getConfig());
|
SmallRyeConfigProviderResolver.class.cast(ConfigProviderResolver.instance()).releaseConfig(ConfigProvider.getConfig());
|
||||||
PropertyMappers.reset();
|
PropertyMappers.reset();
|
||||||
ConfigArgsConfigSource.setCliArgs();
|
ConfigArgsConfigSource.setCliArgs();
|
||||||
|
PersistedConfigSource.getInstance().getConfigValueProperties().clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Config.Scope initConfig(String... scope) {
|
protected Config.Scope initConfig(String... scope) {
|
||||||
|
@ -153,4 +157,9 @@ public abstract class AbstractConfigurationTest {
|
||||||
protected void assertExternalConfig(Map<String, String> expectedValues) {
|
protected void assertExternalConfig(Map<String, String> expectedValues) {
|
||||||
expectedValues.forEach(this::assertExternalConfig);
|
expectedValues.forEach(this::assertExternalConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected static void addPersistedConfigValues(Map<String, String> values) {
|
||||||
|
var configValueProps = PersistedConfigSource.getInstance().getConfigValueProperties();
|
||||||
|
values.forEach((k, v) -> configValueProps.put(k, new ConfigValueBuilder().withName(k).withValue(v).build()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
package org.keycloak.quarkus.runtime.configuration.test;
|
|
||||||
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.keycloak.quarkus.runtime.configuration.QuarkusPropertiesConfigSource;
|
|
||||||
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertFalse;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
|
|
||||||
public class ConfigRegExPatternMatchingTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void quarkusPropertyMultipleDatasourcePatternTest(){
|
|
||||||
Pattern p = Pattern.compile(QuarkusPropertiesConfigSource.QUARKUS_DATASOURCE_BUILDTIME_REGEX);
|
|
||||||
assertTrue(p.matcher("quarkus.datasource.user.jdbc.transactions").matches());
|
|
||||||
assertTrue(p.matcher("quarkus.datasource.user-store.jdbc.enable-metrics").matches());
|
|
||||||
assertTrue(p.matcher("quarkus.datasource.user12_store.db-kind").matches());
|
|
||||||
assertTrue(p.matcher("quarkus.datasource.user12_-__--store.db-kind").matches());
|
|
||||||
assertFalse(p.matcher("quarkus.datasource.user-store.db-username").matches());
|
|
||||||
assertFalse(p.matcher("quarkus.datasource.user-store.db-kin").matches());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -22,7 +22,6 @@ import static org.junit.Assert.assertNull;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
import static org.keycloak.quarkus.runtime.Environment.isWindows;
|
import static org.keycloak.quarkus.runtime.Environment.isWindows;
|
||||||
|
|
||||||
import java.nio.file.FileSystem;
|
|
||||||
import java.nio.file.FileSystems;
|
import java.nio.file.FileSystems;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
|
@ -22,6 +22,8 @@ import static org.hamcrest.CoreMatchers.not;
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Disabled;
|
||||||
import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
|
import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
|
||||||
import org.junit.jupiter.api.Order;
|
import org.junit.jupiter.api.Order;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
@ -52,6 +54,7 @@ public class QuarkusPropertiesAutoBuildDistTest {
|
||||||
@BeforeStartDistribution(EnableAdditionalConsoleHandler.class)
|
@BeforeStartDistribution(EnableAdditionalConsoleHandler.class)
|
||||||
@Launch({ "start" })
|
@Launch({ "start" })
|
||||||
@Order(2)
|
@Order(2)
|
||||||
|
@Disabled(value = "We don't properly differentiate between quarkus runtime and build time properties")
|
||||||
void testQuarkusRuntimePropDoesNotTriggerReAug(LaunchResult result) {
|
void testQuarkusRuntimePropDoesNotTriggerReAug(LaunchResult result) {
|
||||||
CLIResult cliResult = (CLIResult) result;
|
CLIResult cliResult = (CLIResult) result;
|
||||||
cliResult.assertNoBuild();
|
cliResult.assertNoBuild();
|
||||||
|
@ -63,6 +66,7 @@ public class QuarkusPropertiesAutoBuildDistTest {
|
||||||
@BeforeStartDistribution(DisableAdditionalConsoleHandler.class)
|
@BeforeStartDistribution(DisableAdditionalConsoleHandler.class)
|
||||||
@Launch({ "start" })
|
@Launch({ "start" })
|
||||||
@Order(3)
|
@Order(3)
|
||||||
|
@Disabled(value = "We don't properly differentiate between quarkus runtime and build time properties")
|
||||||
void testNoReAugAfterChangingRuntimeProperty(LaunchResult result) {
|
void testNoReAugAfterChangingRuntimeProperty(LaunchResult result) {
|
||||||
CLIResult cliResult = (CLIResult) result;
|
CLIResult cliResult = (CLIResult) result;
|
||||||
cliResult.assertNoBuild();
|
cliResult.assertNoBuild();
|
||||||
|
@ -82,6 +86,7 @@ public class QuarkusPropertiesAutoBuildDistTest {
|
||||||
@BeforeStartDistribution(ChangeAdditionalDatasourceUsername.class)
|
@BeforeStartDistribution(ChangeAdditionalDatasourceUsername.class)
|
||||||
@Launch({ "start" })
|
@Launch({ "start" })
|
||||||
@Order(5)
|
@Order(5)
|
||||||
|
@Disabled(value = "We don't properly differentiate between quarkus runtime and build time properties")
|
||||||
void testNoReAugForAdditionalDatasourceRuntimeProperty(LaunchResult result) {
|
void testNoReAugForAdditionalDatasourceRuntimeProperty(LaunchResult result) {
|
||||||
CLIResult cliResult = (CLIResult) result;
|
CLIResult cliResult = (CLIResult) result;
|
||||||
cliResult.assertNoBuild();
|
cliResult.assertNoBuild();
|
||||||
|
|
|
@ -100,6 +100,7 @@ public class QuarkusPropertiesDistTest {
|
||||||
@BeforeStartDistribution(UpdateConsoleHandlerFromQuarkusProps.class)
|
@BeforeStartDistribution(UpdateConsoleHandlerFromQuarkusProps.class)
|
||||||
@Launch({"start", "--http-enabled=true", "--hostname-strict=false"})
|
@Launch({"start", "--http-enabled=true", "--hostname-strict=false"})
|
||||||
@Order(6)
|
@Order(6)
|
||||||
|
@Disabled(value = "We don't properly differentiate between quarkus runtime and build time properties")
|
||||||
void testRuntimePropFromQuarkusPropsIsAppliedWithoutRebuild(LaunchResult result) {
|
void testRuntimePropFromQuarkusPropsIsAppliedWithoutRebuild(LaunchResult result) {
|
||||||
CLIResult cliResult = (CLIResult) result;
|
CLIResult cliResult = (CLIResult) result;
|
||||||
assertThat(cliResult.getOutput(), not(containsString("Keycloak is the best")));
|
assertThat(cliResult.getOutput(), not(containsString("Keycloak is the best")));
|
||||||
|
|
|
@ -28,7 +28,9 @@ import org.keycloak.it.junit5.extension.CLIResult;
|
||||||
import org.keycloak.it.junit5.extension.DistributionTest;
|
import org.keycloak.it.junit5.extension.DistributionTest;
|
||||||
import org.keycloak.it.junit5.extension.RawDistOnly;
|
import org.keycloak.it.junit5.extension.RawDistOnly;
|
||||||
import org.keycloak.it.utils.KeycloakDistribution;
|
import org.keycloak.it.utils.KeycloakDistribution;
|
||||||
|
import org.keycloak.it.utils.RawKeycloakDistribution;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
@ -87,4 +89,17 @@ public class StartDevCommandDistTest {
|
||||||
cliResult.assertStartedDevMode();
|
cliResult.assertStartedDevMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testStartDevThenImportRebuild(KeycloakDistribution dist) throws Exception {
|
||||||
|
RawKeycloakDistribution rawDist = dist.unwrap(RawKeycloakDistribution.class);
|
||||||
|
CLIResult result = rawDist.run("start-dev");
|
||||||
|
assertTrue(result.getErrorOutput().isEmpty(), result.getErrorOutput());
|
||||||
|
|
||||||
|
File target = new File("./target");
|
||||||
|
|
||||||
|
// feature change should trigger a build
|
||||||
|
result = rawDist.run("--profile=dev", "export", "--features=docker", "--dir=" + target.getAbsolutePath());
|
||||||
|
result.assertMessage("Updating the configuration and installing your custom providers, if any. Please wait.");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue