Make quarkus runtime properties also available as runtime properties in keycloak
Changes behaviour to: - all raw quarkus config properties are handled as runtime config in keycloak, with the exception of raw properties we need for additional datasources, there we check for build- vs runtime - unknown quarkus buildtime properties require a build first or the usual quarkus warning is shown - wrapped quarkus properties still get ignored / overwritten by our configuration layer (no change in behaviour here) Closes #10968
This commit is contained in:
parent
66de8adefd
commit
9e57f836f2
13 changed files with 326 additions and 56 deletions
|
@ -165,16 +165,19 @@ Once the first user with administrative rights exists, you can use the UI or the
|
||||||
== Unsupported server options
|
== Unsupported server options
|
||||||
|
|
||||||
In most cases, the available options from the server configuration should suffice to configure the server.
|
In most cases, the available options from the server configuration should suffice to configure the server.
|
||||||
However, you might need to use properties directly from Quarkus to enable a specific behavior or capability that is missing from the server configuration.
|
However, you might need to use properties directly from Quarkus to enable a specific behavior or capability that is missing in the keycloak configuration.
|
||||||
|
|
||||||
As much as possible, avoid using properties directly from Quarkus. If your need is essential, consider opening an https://github.com/keycloak/keycloak/issues/new?assignees=&labels=kind%2Fenhancement%2Cstatus%2Ftriage&template=enhancement.yml[issue] first and help us
|
As much as possible, avoid using properties directly from Quarkus. If your need is essential, consider opening an https://github.com/keycloak/keycloak/issues/new?assignees=&labels=kind%2Fenhancement%2Cstatus%2Ftriage&template=enhancement.yml[issue] first and help us
|
||||||
to improve the server configuration.
|
to improve the server configuration.
|
||||||
|
|
||||||
To configure the server using Quarkus properties, perform the following steps:
|
If that's not possible for you, you can configure the server using Quarkus properties, perform the following steps:
|
||||||
|
|
||||||
. Create a `conf/quarkus.properties` file and define any property you need.
|
. Create a `conf/quarkus.properties` file and define any property you need.
|
||||||
. Run the `build` command to apply the settings to the server
|
|
||||||
|
|
||||||
For a complete list of Quarkus properties, see the https://quarkus.io/guides/all-config[Quarkus documentation] .
|
For a complete list of Quarkus properties, see the https://quarkus.io/guides/all-config[Quarkus documentation] .
|
||||||
|
|
||||||
|
When a raw quarkus property is a runtime property, it is also handled as runtime property for keycloak. When a quarkus property is a build time property, you have to invoke a new keycloak build first for the property to apply.
|
||||||
|
|
||||||
|
Note that some quarkus properties are mapped to internally by the Keycloak configuration, for example `quarkus.http.port` and similar properties that are needed to configure Keycloak. If the property is used by Keycloak, and you define the same property key in the quarkus.properties file, the keycloak configuration value takes precedence over the raw quarkus configuration value, so the value you set in `quarkus.properties` will be ignored when there is a value in the actual Keycloak configuration.
|
||||||
|
|
||||||
</@tmpl.guide>
|
</@tmpl.guide>
|
||||||
|
|
|
@ -34,6 +34,7 @@ import static picocli.CommandLine.Model.UsageMessageSpec.SECTION_KEY_COMMAND_LIS
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -42,8 +43,10 @@ import java.util.Set;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import java.util.function.UnaryOperator;
|
import java.util.function.UnaryOperator;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.eclipse.microprofile.config.spi.ConfigSource;
|
||||||
import org.keycloak.quarkus.runtime.cli.command.Build;
|
import org.keycloak.quarkus.runtime.cli.command.Build;
|
||||||
import org.keycloak.quarkus.runtime.cli.command.ImportRealmMixin;
|
import org.keycloak.quarkus.runtime.cli.command.ImportRealmMixin;
|
||||||
import org.keycloak.quarkus.runtime.cli.command.Main;
|
import org.keycloak.quarkus.runtime.cli.command.Main;
|
||||||
|
@ -51,6 +54,7 @@ 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.configuration.ConfigArgsConfigSource;
|
import org.keycloak.quarkus.runtime.configuration.ConfigArgsConfigSource;
|
||||||
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.ConfigCategory;
|
import org.keycloak.quarkus.runtime.configuration.mappers.ConfigCategory;
|
||||||
import org.keycloak.quarkus.runtime.configuration.mappers.PropertyMappers;
|
import org.keycloak.quarkus.runtime.configuration.mappers.PropertyMappers;
|
||||||
import org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper;
|
import org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper;
|
||||||
|
@ -65,9 +69,9 @@ import picocli.CommandLine.Model.ArgGroupSpec;
|
||||||
public final class Picocli {
|
public final class Picocli {
|
||||||
|
|
||||||
public static final String ARG_PREFIX = "--";
|
public static final String ARG_PREFIX = "--";
|
||||||
private static final String ARG_KEY_VALUE_SEPARATOR = "=";
|
|
||||||
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";
|
||||||
|
private static final String ARG_KEY_VALUE_SEPARATOR = "=";
|
||||||
|
|
||||||
private Picocli() {
|
private Picocli() {
|
||||||
}
|
}
|
||||||
|
@ -94,6 +98,7 @@ public final class Picocli {
|
||||||
|
|
||||||
private static int runReAugmentationIfNeeded(List<String> cliArgs, CommandLine cmd) {
|
private static int runReAugmentationIfNeeded(List<String> cliArgs, CommandLine cmd) {
|
||||||
int exitCode = 0;
|
int exitCode = 0;
|
||||||
|
|
||||||
if (hasAutoBuildOption(cliArgs) && !isHelpCommand(cliArgs)) {
|
if (hasAutoBuildOption(cliArgs) && !isHelpCommand(cliArgs)) {
|
||||||
if (cliArgs.contains(StartDev.NAME)) {
|
if (cliArgs.contains(StartDev.NAME)) {
|
||||||
String profile = Environment.getProfile();
|
String profile = Environment.getProfile();
|
||||||
|
@ -269,9 +274,65 @@ public final class Picocli {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//check for defined quarkus raw build properties for UserStorageProvider extensions
|
||||||
|
if (QuarkusPropertiesConfigSource.getConfigurationFile() != null) {
|
||||||
|
Optional<ConfigSource> quarkusPropertiesConfigSource = getConfig().getConfigSource(QuarkusPropertiesConfigSource.NAME);
|
||||||
|
|
||||||
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean hasAtLeastOneChangedBuildProperty(Map<String, String> foundQuarkusBuildProperties, Set<Map.Entry<String, String>> persistedEntries) {
|
||||||
|
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) {
|
private static boolean isProviderKey(String key) {
|
||||||
return key.startsWith("kc.provider.file");
|
return key.startsWith("kc.provider.file");
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,11 +49,6 @@ public class PropertyMappingInterceptor implements ConfigSourceInterceptor {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isPersistedOnlyProperty(value)) {
|
|
||||||
// quarkus properties values always resolved from persisted config source
|
|
||||||
return value.withValue(PersistedConfigSource.getInstance().getValue(name));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.getValue().indexOf("${") == -1) {
|
if (value.getValue().indexOf("${") == -1) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
@ -70,19 +65,4 @@ public class PropertyMappingInterceptor implements ConfigSourceInterceptor {
|
||||||
return prop.getValue();
|
return prop.getValue();
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isPersistedOnlyProperty(ConfigValue value) {
|
|
||||||
if (isQuarkusPropertiesEnabled && value.getName().startsWith(NS_QUARKUS)) {
|
|
||||||
String configSourceName = value.getConfigSourceName();
|
|
||||||
|
|
||||||
return Environment.isRuntimeMode()
|
|
||||||
&& configSourceName != null
|
|
||||||
&& !configSourceName.equals(PersistedConfigSource.NAME)
|
|
||||||
&& !configSourceName.equals(AbstractRawDefaultConfigSource.NAME)
|
|
||||||
&& !configSourceName.contains("Runtime Defaults")
|
|
||||||
&& !configSourceName.contains("application.properties");
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,9 +45,12 @@ 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 NAME = "QuarkusProperties";
|
|
||||||
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 QUARKUS_PROPERTY_ENABLED = "kc.quarkus-properties-enabled";
|
||||||
|
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) {
|
public static boolean isSameSource(ConfigValue value) {
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
|
@ -91,13 +94,7 @@ public final class QuarkusPropertiesConfigSource extends AbstractLocationConfigS
|
||||||
@Override
|
@Override
|
||||||
public String getValue(String propertyName) {
|
public String getValue(String propertyName) {
|
||||||
if (propertyName.startsWith(NS_QUARKUS)) {
|
if (propertyName.startsWith(NS_QUARKUS)) {
|
||||||
String value = super.getValue(propertyName);
|
return super.getValue(propertyName);
|
||||||
|
|
||||||
if (value == null) {
|
|
||||||
return PersistedConfigSource.getInstance().getValue(propertyName);
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
@ -111,13 +108,11 @@ public final class QuarkusPropertiesConfigSource extends AbstractLocationConfigS
|
||||||
|
|
||||||
configSources.addAll(loadConfigSources("META-INF/services/" + FILE_NAME, 450, classLoader));
|
configSources.addAll(loadConfigSources("META-INF/services/" + FILE_NAME, 450, classLoader));
|
||||||
|
|
||||||
if (Environment.isRebuild() || Environment.isRebuildCheck()) {
|
|
||||||
Path configFile = getConfigurationFile();
|
Path configFile = getConfigurationFile();
|
||||||
|
|
||||||
if (configFile != null) {
|
if (configFile != null) {
|
||||||
configSources.addAll(loadConfigSources(configFile.toUri().toString(), 500, classLoader));
|
configSources.addAll(loadConfigSources(configFile.toUri().toString(), 500, classLoader));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return configSources;
|
return configSources;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package org.keycloak.quarkus.runtime.configuration.mappers;
|
package org.keycloak.quarkus.runtime.configuration.mappers;
|
||||||
|
|
||||||
|
import io.quarkus.runtime.QuarkusApplication;
|
||||||
|
import io.quarkus.runtime.configuration.ConfigurationRuntimeConfig;
|
||||||
import io.smallrye.config.ConfigSourceInterceptorContext;
|
import io.smallrye.config.ConfigSourceInterceptorContext;
|
||||||
import io.smallrye.config.ConfigValue;
|
import io.smallrye.config.ConfigValue;
|
||||||
import org.keycloak.quarkus.runtime.Environment;
|
import org.keycloak.quarkus.runtime.Environment;
|
||||||
|
@ -53,14 +55,10 @@ public final class PropertyMappers {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isBuildTimeProperty(String name) {
|
public static boolean isBuildTimeProperty(String name) {
|
||||||
if (isFeaturesBuildTimeProperty(name) || isSpiBuildTimeProperty(name) || name.startsWith(MicroProfileConfigProvider.NS_QUARKUS_PREFIX)) {
|
if (isFeaturesBuildTimeProperty(name) || isSpiBuildTimeProperty(name)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!name.startsWith(MicroProfileConfigProvider.NS_KEYCLOAK_PREFIX)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean isBuildTimeProperty = MAPPERS.entrySet().stream()
|
boolean isBuildTimeProperty = MAPPERS.entrySet().stream()
|
||||||
.anyMatch(new Predicate<Map.Entry<String, PropertyMapper>>() {
|
.anyMatch(new Predicate<Map.Entry<String, PropertyMapper>>() {
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
|
@ -93,6 +93,10 @@ public interface CLIResult extends LaunchResult {
|
||||||
assertFalse(getOutput().contains("Server configuration updated and persisted"));
|
assertFalse(getOutput().contains("Server configuration updated and persisted"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default void assertBuildRuntimeMismatchWarning(String quarkusBuildtimePropKey) {
|
||||||
|
assertTrue(getOutput().contains(" - " + quarkusBuildtimePropKey + " is set to 'false' but it is build time fixed to 'true'. Did you change the property " + quarkusBuildtimePropKey + " after building the application?"));
|
||||||
|
}
|
||||||
|
|
||||||
default boolean isClustered() {
|
default boolean isClustered() {
|
||||||
return getOutput().contains("Starting JGroups channel `ISPN`");
|
return getOutput().contains("Starting JGroups channel `ISPN`");
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,6 +82,8 @@ public class CLITestExtension extends QuarkusMainTestExtension {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (distConfig != null) {
|
if (distConfig != null) {
|
||||||
|
onKeepServerAlive(context.getRequiredTestMethod().getAnnotation(KeepServerAlive.class));
|
||||||
|
|
||||||
if (launch != null) {
|
if (launch != null) {
|
||||||
if (dist == null) {
|
if (dist == null) {
|
||||||
dist = createDistribution(distConfig);
|
dist = createDistribution(distConfig);
|
||||||
|
@ -109,6 +111,16 @@ public class CLITestExtension extends QuarkusMainTestExtension {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void onKeepServerAlive(KeepServerAlive annotation) {
|
||||||
|
if(annotation != null && dist != null) {
|
||||||
|
try {
|
||||||
|
dist.setManualStop(true);
|
||||||
|
} catch (Exception cause) {
|
||||||
|
throw new RuntimeException("Error when invoking " + annotation, cause);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void afterEach(ExtensionContext context) throws Exception {
|
public void afterEach(ExtensionContext context) throws Exception {
|
||||||
DistributionTest distConfig = getDistributionConfig(context);
|
DistributionTest distConfig = getDistributionConfig(context);
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021 Red Hat, Inc. and/or its affiliates
|
||||||
|
* and other contributors as indicated by the @author tags.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.keycloak.it.junit5.extension;
|
||||||
|
|
||||||
|
import org.keycloak.it.utils.KeycloakDistribution;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link KeepServerAlive} is used in a distributiontest to keep the server alive on test / method level.
|
||||||
|
* Used when when {@link DistributionTest} is invoked on class level not using keepAlive=true param, but
|
||||||
|
* for a specific test we need to run e.g. RestAssured verifications.
|
||||||
|
*/
|
||||||
|
@Target({ ElementType.METHOD })
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface KeepServerAlive {
|
||||||
|
|
||||||
|
}
|
|
@ -40,6 +40,10 @@ public interface KeycloakDistribution {
|
||||||
return commands.toArray(new String[0]);
|
return commands.toArray(new String[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default void setManualStop(boolean manualStop) {
|
||||||
|
throw new RuntimeException("Not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
default void setQuarkusProperty(String key, String value) {
|
default void setQuarkusProperty(String key, String value) {
|
||||||
throw new RuntimeException("Not implemented");
|
throw new RuntimeException("Not implemented");
|
||||||
}
|
}
|
||||||
|
|
|
@ -322,6 +322,11 @@ public final class RawKeycloakDistribution implements KeycloakDistribution {
|
||||||
keycloak = builder.start();
|
keycloak = builder.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setManualStop(boolean manualStop) {
|
||||||
|
this.manualStop = manualStop;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setProperty(String key, String value) {
|
public void setProperty(String key, String value) {
|
||||||
setProperty(key, value, distPath.resolve("conf").resolve("keycloak.conf").toFile());
|
setProperty(key, value, distPath.resolve("conf").resolve("keycloak.conf").toFile());
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
package org.keycloak.it.cli.dist;
|
package org.keycloak.it.cli.dist;
|
||||||
|
|
||||||
|
import static io.restassured.RestAssured.when;
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
@ -35,7 +36,6 @@ import io.quarkus.test.junit.main.Launch;
|
||||||
import io.quarkus.test.junit.main.LaunchResult;
|
import io.quarkus.test.junit.main.LaunchResult;
|
||||||
|
|
||||||
@DistributionTest(reInstall = DistributionTest.ReInstall.NEVER)
|
@DistributionTest(reInstall = DistributionTest.ReInstall.NEVER)
|
||||||
@BeforeStartDistribution(QuarkusPropertiesAutoBuildDistTest.UpdateConsoleLogLevelToWarn.class)
|
|
||||||
@RawDistOnly(reason = "Containers are immutable")
|
@RawDistOnly(reason = "Containers are immutable")
|
||||||
@TestMethodOrder(OrderAnnotation.class)
|
@TestMethodOrder(OrderAnnotation.class)
|
||||||
public class QuarkusPropertiesAutoBuildDistTest {
|
public class QuarkusPropertiesAutoBuildDistTest {
|
||||||
|
@ -43,16 +43,16 @@ public class QuarkusPropertiesAutoBuildDistTest {
|
||||||
@Test
|
@Test
|
||||||
@Launch({ "start", "--auto-build", "--http-enabled=true", "--hostname-strict=false", "--cache=local" })
|
@Launch({ "start", "--auto-build", "--http-enabled=true", "--hostname-strict=false", "--cache=local" })
|
||||||
@Order(1)
|
@Order(1)
|
||||||
void testReAugOnFirstRun(LaunchResult result) {
|
void reAugOnFirstRun(LaunchResult result) {
|
||||||
CLIResult cliResult = (CLIResult) result;
|
CLIResult cliResult = (CLIResult) result;
|
||||||
cliResult.assertBuild();
|
cliResult.assertBuild();
|
||||||
assertFalse(cliResult.getOutput().contains("INFO [io.quarkus]"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@BeforeStartDistribution(QuarkusPropertiesAutoBuildDistTest.UpdateConsoleLogLevelToWarn.class)
|
||||||
@Launch({ "start", "--auto-build", "--http-enabled=true", "--hostname-strict=false", "--cache=local" })
|
@Launch({ "start", "--auto-build", "--http-enabled=true", "--hostname-strict=false", "--cache=local" })
|
||||||
@Order(2)
|
@Order(2)
|
||||||
void testSecondStartDoNotTriggerReAug(LaunchResult result) {
|
void testQuarkusRuntimePropDoesNotTriggerReAug(LaunchResult result) {
|
||||||
CLIResult cliResult = (CLIResult) result;
|
CLIResult cliResult = (CLIResult) result;
|
||||||
cliResult.assertNoBuild();
|
cliResult.assertNoBuild();
|
||||||
assertFalse(cliResult.getOutput().contains("INFO [io.quarkus]"));
|
assertFalse(cliResult.getOutput().contains("INFO [io.quarkus]"));
|
||||||
|
@ -62,14 +62,60 @@ public class QuarkusPropertiesAutoBuildDistTest {
|
||||||
@BeforeStartDistribution(UpdateConsoleLogLevelToInfo.class)
|
@BeforeStartDistribution(UpdateConsoleLogLevelToInfo.class)
|
||||||
@Launch({ "start", "--auto-build", "--http-enabled=true", "--hostname-strict=false", "--cache=local" })
|
@Launch({ "start", "--auto-build", "--http-enabled=true", "--hostname-strict=false", "--cache=local" })
|
||||||
@Order(3)
|
@Order(3)
|
||||||
void testReAugAfterChangingProperty(LaunchResult result) {
|
void testNoReAugAfterChangingRuntimeProperty(LaunchResult result) {
|
||||||
CLIResult cliResult = (CLIResult) result;
|
CLIResult cliResult = (CLIResult) result;
|
||||||
cliResult.assertBuild();
|
cliResult.assertNoBuild();
|
||||||
assertTrue(cliResult.getOutput().contains("INFO [io.quarkus]"));
|
assertTrue(cliResult.getOutput().contains("INFO [io.quarkus]"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class UpdateConsoleLogLevelToWarn implements Consumer<KeycloakDistribution> {
|
@Test
|
||||||
|
@BeforeStartDistribution(AddAdditionalDatasource.class)
|
||||||
|
@Launch({ "start", "--auto-build", "--http-enabled=true", "--hostname-strict=false", "--cache=local" })
|
||||||
|
@Order(4)
|
||||||
|
void testReAugForAdditionalDatasource(LaunchResult result) {
|
||||||
|
CLIResult cliResult = (CLIResult) result;
|
||||||
|
cliResult.assertBuild();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@BeforeStartDistribution(ChangeAdditionalDatasourceUsername.class)
|
||||||
|
@Launch({ "start", "--auto-build", "--http-enabled=true", "--hostname-strict=false", "--cache=local" })
|
||||||
|
@Order(5)
|
||||||
|
void testNoReAugForAdditionalDatasourceRuntimeProperty(LaunchResult result) {
|
||||||
|
CLIResult cliResult = (CLIResult) result;
|
||||||
|
cliResult.assertNoBuild();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@BeforeStartDistribution(ChangeAdditionalDatasourceDbKind.class)
|
||||||
|
@Launch({ "start", "--auto-build", "--http-enabled=true", "--hostname-strict=false", "--cache=local" })
|
||||||
|
@Order(6)
|
||||||
|
void testNoReAugWhenBuildTimePropertiesAreTheSame(LaunchResult result) {
|
||||||
|
CLIResult cliResult = (CLIResult) result;
|
||||||
|
cliResult.assertNoBuild();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@BeforeStartDistribution(AddAdditionalDatasource2.class)
|
||||||
|
@Launch({ "start", "--auto-build", "--http-enabled=true", "--hostname-strict=false", "--cache=local" })
|
||||||
|
@Order(7)
|
||||||
|
void testReAugWhenAnotherDatasourceAdded(LaunchResult result) {
|
||||||
|
CLIResult cliResult = (CLIResult) result;
|
||||||
|
cliResult.assertBuild();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@BeforeStartDistribution(EnableDatasourceMetrics.class)
|
||||||
|
@Launch({ "start", "--auto-build", "--http-enabled=true", "--hostname-strict=false", "--cache=local" })
|
||||||
|
@Order(8)
|
||||||
|
void testWrappedBuildPropertyTriggersBuildButGetsIgnoredWhenSetByQuarkus(LaunchResult result) {
|
||||||
|
CLIResult cliResult = (CLIResult) result;
|
||||||
|
cliResult.assertBuild();
|
||||||
|
when().get("/metrics").then()
|
||||||
|
.statusCode(404);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class UpdateConsoleLogLevelToWarn implements Consumer<KeycloakDistribution> {
|
||||||
@Override
|
@Override
|
||||||
public void accept(KeycloakDistribution distribution) {
|
public void accept(KeycloakDistribution distribution) {
|
||||||
distribution.setQuarkusProperty("quarkus.log.console.level", "WARN");
|
distribution.setQuarkusProperty("quarkus.log.console.level", "WARN");
|
||||||
|
@ -84,4 +130,44 @@ public class QuarkusPropertiesAutoBuildDistTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class AddAdditionalDatasource implements Consumer<KeycloakDistribution> {
|
||||||
|
@Override
|
||||||
|
public void accept(KeycloakDistribution distribution) {
|
||||||
|
distribution.setQuarkusProperty("quarkus.datasource.user-store.db-kind", "h2");
|
||||||
|
distribution.setQuarkusProperty("quarkus.datasource.user-store.username","sa");
|
||||||
|
distribution.setQuarkusProperty("quarkus.datasource.user-store.jdbc.url","jdbc:h2:mem:user-store;DB_CLOSE_DELAY=-1");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class AddAdditionalDatasource2 implements Consumer<KeycloakDistribution> {
|
||||||
|
@Override
|
||||||
|
public void accept(KeycloakDistribution distribution) {
|
||||||
|
distribution.setQuarkusProperty("quarkus.datasource.user-store2.db-kind", "h2");
|
||||||
|
distribution.setQuarkusProperty("quarkus.datasource.user-store2.db-transactions", "enabled");
|
||||||
|
distribution.setQuarkusProperty("quarkus.datasource.user-store2.username","sa");
|
||||||
|
distribution.setQuarkusProperty("quarkus.datasource.user-store2.jdbc.url","jdbc:h2:mem:user-store2;DB_CLOSE_DELAY=-1");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ChangeAdditionalDatasourceUsername implements Consumer<KeycloakDistribution> {
|
||||||
|
@Override
|
||||||
|
public void accept(KeycloakDistribution distribution) {
|
||||||
|
distribution.setQuarkusProperty("quarkus.datasource.user-store.username","foo");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ChangeAdditionalDatasourceDbKind implements Consumer<KeycloakDistribution> {
|
||||||
|
@Override
|
||||||
|
public void accept(KeycloakDistribution distribution) {
|
||||||
|
distribution.setQuarkusProperty("quarkus.datasource.user-store.db-kind","h2");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class EnableDatasourceMetrics implements Consumer<KeycloakDistribution> {
|
||||||
|
@Override
|
||||||
|
public void accept(KeycloakDistribution distribution) {
|
||||||
|
distribution.setManualStop(true);
|
||||||
|
distribution.setQuarkusProperty("quarkus.datasource.metrics.enabled","true");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -17,6 +17,8 @@
|
||||||
|
|
||||||
package org.keycloak.it.cli.dist;
|
package org.keycloak.it.cli.dist;
|
||||||
|
|
||||||
|
import static io.restassured.RestAssured.when;
|
||||||
|
import static org.hamcrest.Matchers.containsString;
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
@ -28,6 +30,7 @@ import org.junit.jupiter.api.TestMethodOrder;
|
||||||
import org.keycloak.it.junit5.extension.BeforeStartDistribution;
|
import org.keycloak.it.junit5.extension.BeforeStartDistribution;
|
||||||
import org.keycloak.it.junit5.extension.CLIResult;
|
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.KeepServerAlive;
|
||||||
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;
|
||||||
|
|
||||||
|
@ -35,21 +38,23 @@ import io.quarkus.test.junit.main.Launch;
|
||||||
import io.quarkus.test.junit.main.LaunchResult;
|
import io.quarkus.test.junit.main.LaunchResult;
|
||||||
|
|
||||||
@DistributionTest(reInstall = DistributionTest.ReInstall.NEVER)
|
@DistributionTest(reInstall = DistributionTest.ReInstall.NEVER)
|
||||||
@BeforeStartDistribution(QuarkusPropertiesDistTest.UpdateConsoleLogLevelToWarn.class)
|
|
||||||
@RawDistOnly(reason = "Containers are immutable")
|
@RawDistOnly(reason = "Containers are immutable")
|
||||||
@TestMethodOrder(OrderAnnotation.class)
|
@TestMethodOrder(OrderAnnotation.class)
|
||||||
public class QuarkusPropertiesDistTest {
|
public class QuarkusPropertiesDistTest {
|
||||||
|
|
||||||
|
private static final String QUARKUS_BUILDTIME_HIBERNATE_METRICS_KEY = "quarkus.hibernate-orm.metrics.enabled";
|
||||||
|
private static final String QUARKUS_RUNTIME_CONSOLE_LOGLVL_KEY = "quarkus.log.console.level";
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Launch({ "build", "--cache=local" })
|
@Launch({ "build", "--cache=local" })
|
||||||
@Order(1)
|
@Order(1)
|
||||||
void testBuildWithPropertyFromQuarkusProperties(LaunchResult result) {
|
void testBuildWithPropertyFromQuarkusProperties(LaunchResult result) {
|
||||||
CLIResult cliResult = (CLIResult) result;
|
CLIResult cliResult = (CLIResult) result;
|
||||||
assertFalse(cliResult.getOutput().contains("INFO"));
|
|
||||||
cliResult.assertBuild();
|
cliResult.assertBuild();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@BeforeStartDistribution(QuarkusPropertiesDistTest.UpdateConsoleLogLevelToWarnFromQuarkusProps.class)
|
||||||
@Launch({ "start", "--http-enabled=true", "--hostname-strict=false" })
|
@Launch({ "start", "--http-enabled=true", "--hostname-strict=false" })
|
||||||
@Order(2)
|
@Order(2)
|
||||||
void testPropertyEnabledAtRuntime(LaunchResult result) {
|
void testPropertyEnabledAtRuntime(LaunchResult result) {
|
||||||
|
@ -75,7 +80,7 @@ public class QuarkusPropertiesDistTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@BeforeStartDistribution(UpdateConsoleLogLevelToInfo.class)
|
@BeforeStartDistribution(UpdateConsoleLogLevelToInfoFromKeycloakConf.class)
|
||||||
@Launch({ "build" })
|
@Launch({ "build" })
|
||||||
@Order(5)
|
@Order(5)
|
||||||
void testIgnoreQuarkusPropertyFromKeycloakConf(LaunchResult result) {
|
void testIgnoreQuarkusPropertyFromKeycloakConf(LaunchResult result) {
|
||||||
|
@ -84,20 +89,76 @@ public class QuarkusPropertiesDistTest {
|
||||||
cliResult.assertBuild();
|
cliResult.assertBuild();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class UpdateConsoleLogLevelToWarn implements Consumer<KeycloakDistribution> {
|
@Test
|
||||||
|
@BeforeStartDistribution(UpdateConsoleLogLevelToInfoFromQuarkusProps.class)
|
||||||
|
@Launch({ "start", "--http-enabled=true", "--hostname-strict=false" })
|
||||||
|
@Order(6)
|
||||||
|
void testRuntimePropFromQuarkusPropsIsAppliedWithoutRebuild(LaunchResult result) {
|
||||||
|
CLIResult cliResult = (CLIResult) result;
|
||||||
|
assertTrue(cliResult.getOutput().contains("INFO"));
|
||||||
|
cliResult.assertNoBuild();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@BeforeStartDistribution(UpdateHibernateMetricsFromQuarkusProps.class)
|
||||||
|
@Launch({ "start", "--http-enabled=true", "--hostname-strict=false" })
|
||||||
|
@Order(7)
|
||||||
|
void testBuildRunTimeMismatchOnQuarkusBuildPropWarning(LaunchResult result) {
|
||||||
|
CLIResult cliResult = (CLIResult) result;
|
||||||
|
cliResult.assertBuildRuntimeMismatchWarning(QUARKUS_BUILDTIME_HIBERNATE_METRICS_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@BeforeStartDistribution(UpdateHibernateMetricsFromQuarkusProps.class)
|
||||||
|
@Launch({ "build", "--metrics-enabled=true" })
|
||||||
|
@Order(8)
|
||||||
|
void buildFirstWithUnknownQuarkusBuildProperty(LaunchResult result) {
|
||||||
|
CLIResult cliResult = (CLIResult) result;
|
||||||
|
cliResult.assertBuild();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@KeepServerAlive
|
||||||
|
@Launch({ "start", "--http-enabled=true", "--hostname-strict=false" })
|
||||||
|
@Order(9)
|
||||||
|
void testUnknownQuarkusBuildTimePropertyApplied(LaunchResult result) {
|
||||||
|
CLIResult cliResult = (CLIResult) result;
|
||||||
|
cliResult.assertNoBuild();
|
||||||
|
when().get("/metrics").then().statusCode(200)
|
||||||
|
.body(containsString("vendor_hibernate_cache_query_plan_total"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class UpdateConsoleLogLevelToWarnFromQuarkusProps implements Consumer<KeycloakDistribution> {
|
||||||
@Override
|
@Override
|
||||||
public void accept(KeycloakDistribution distribution) {
|
public void accept(KeycloakDistribution distribution) {
|
||||||
distribution.setQuarkusProperty("quarkus.log.console.level", "WARN");
|
distribution.setQuarkusProperty(QUARKUS_RUNTIME_CONSOLE_LOGLVL_KEY, "WARN");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class UpdateConsoleLogLevelToInfo implements Consumer<KeycloakDistribution> {
|
public static class UpdateConsoleLogLevelToInfoFromKeycloakConf implements Consumer<KeycloakDistribution> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void accept(KeycloakDistribution distribution) {
|
public void accept(KeycloakDistribution distribution) {
|
||||||
distribution.deleteQuarkusProperties();
|
distribution.deleteQuarkusProperties();
|
||||||
distribution.setProperty("quarkus.log.console.level", "INFO");
|
distribution.setProperty(QUARKUS_RUNTIME_CONSOLE_LOGLVL_KEY, "INFO");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class UpdateConsoleLogLevelToInfoFromQuarkusProps implements Consumer<KeycloakDistribution> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void accept(KeycloakDistribution distribution) {
|
||||||
|
distribution.deleteQuarkusProperties();
|
||||||
|
distribution.setQuarkusProperty(QUARKUS_RUNTIME_CONSOLE_LOGLVL_KEY, "INFO");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class UpdateHibernateMetricsFromQuarkusProps implements Consumer<KeycloakDistribution> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void accept(KeycloakDistribution distribution) {
|
||||||
|
distribution.deleteQuarkusProperties();
|
||||||
|
distribution.setQuarkusProperty(QUARKUS_BUILDTIME_HIBERNATE_METRICS_KEY, "true");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in a new issue