Improvements to configuration

This commit is contained in:
Pedro Igor 2020-10-29 21:35:03 -03:00 committed by Marek Posolda
parent e131de9574
commit 452bcd8bd9
11 changed files with 174 additions and 116 deletions

View file

@ -104,6 +104,10 @@
<source>target/keycloak-quarkus-server/cluster-default.xml</source> <source>target/keycloak-quarkus-server/cluster-default.xml</source>
<outputDirectory>conf</outputDirectory> <outputDirectory>conf</outputDirectory>
</file> </file>
<file>
<source>target/keycloak-quarkus-server-app/META-INF/keycloak.properties</source>
<outputDirectory>conf</outputDirectory>
</file>
</files> </files>
</assembly> </assembly>

View file

@ -70,6 +70,12 @@
<type>jar</type> <type>jar</type>
<outputDirectory>target/keycloak-quarkus-server</outputDirectory> <outputDirectory>target/keycloak-quarkus-server</outputDirectory>
</artifactItem> </artifactItem>
<artifactItem>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-quarkus-server-app</artifactId>
<type>jar</type>
<outputDirectory>target/keycloak-quarkus-server-app</outputDirectory>
</artifactItem>
</artifactItems> </artifactItems>
</configuration> </configuration>
</execution> </execution>

View file

@ -0,0 +1,3 @@
# Configure the server
Use files in this directory to configure the server

View file

@ -1,12 +0,0 @@
# Default and non-production grade database vendor
db=h2-file
# Default, and insecure, and non-production grade configuration for the development profile
%dev.http.enabled=true
%dev.db.username = sa
%dev.db.password = keycloak
# Logging configuration. INFO is the default level for most of the categories
#quarkus.log.level = DEBUG
quarkus.log.category."org.jboss.resteasy.resteasy_jaxrs.i18n".level=WARN
quarkus.log.category."org.infinispan.transaction.lookup.JBossStandaloneJTAManagerLookup".level=WARN

View file

@ -102,7 +102,7 @@ class KeycloakProcessor {
* *
* @param recorder * @param recorder
*/ */
@Record(ExecutionTime.STATIC_INIT) @Record(ExecutionTime.RUNTIME_INIT)
@BuildStep @BuildStep
void configureProviders(KeycloakRecorder recorder) { void configureProviders(KeycloakRecorder recorder) {
Profile.setInstance(recorder.createProfile()); Profile.setInstance(recorder.createProfile());

View file

@ -0,0 +1,123 @@
/*
* Copyright 2020 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.configuration;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
class Database {
private static Map<String, Vendor> DATABASES = new HashMap<>();
static {
for (Vendor vendor : Vendor.values()) {
DATABASES.put(vendor.name().toLowerCase(), vendor);
for (String alias : vendor.aliases) {
DATABASES.put(alias, vendor);
}
}
}
static boolean isSupported(String alias) {
return DATABASES.containsKey(alias);
}
static Optional<String> getDefaultUrl(String alias) {
Vendor vendor = DATABASES.get(alias);
if (vendor == null) {
return Optional.empty();
}
return Optional.of(vendor.defaultUrl.apply(alias));
}
static Optional<String> getDriver(String alias) {
Vendor vendor = DATABASES.get(alias);
if (vendor == null) {
return Optional.empty();
}
return Optional.of(vendor.driver);
}
static Optional<String> getDialect(String alias) {
Vendor vendor = DATABASES.get(alias);
if (vendor == null) {
return Optional.empty();
}
return Optional.of(vendor.dialect.apply(alias));
}
private enum Vendor {
H2("org.h2.jdbcx.JdbcDataSource", "io.quarkus.hibernate.orm.runtime.dialect.QuarkusH2Dialect",
new Function<String, String>() {
@Override
public String apply(String alias) {
if ("h2-file".equalsIgnoreCase(alias)) {
return "jdbc:h2:file:${kc.home.dir:${kc.db.url.path:~}}/${kc.data.dir:data}/keycloakdb${kc.db.url.properties:;;AUTO_SERVER=TRUE}";
}
return "jdbc:h2:mem:keycloakdb${kc.db.url.properties:}";
}
}, "h2-mem", "h2-file"),
MYSQL("com.mysql.cj.jdbc.MysqlXADataSource", "org.hibernate.dialect.MySQL8Dialect",
"jdbc:mysql://${kc.db.url.host:localhost}/${kc.db.url.database:keycloak}${kc.db.url.properties:}"),
MARIADB("org.mariadb.jdbc.MySQLDataSource", "org.hibernate.dialect.MariaDBDialect",
"jdbc:mariadb://${kc.db.url.host:localhost}/${kc.db.url.database:keycloak}${kc.db.url.properties:}"),
POSTGRES("org.postgresql.xa.PGXADataSource", new Function<String, String>() {
@Override
public String apply(String alias) {
if ("postgres-95".equalsIgnoreCase(alias)) {
return "io.quarkus.hibernate.orm.runtime.dialect.QuarkusPostgreSQL95Dialect";
}
return "io.quarkus.hibernate.orm.runtime.dialect.QuarkusPostgreSQL10Dialect";
}
}, "jdbc:postgresql://${kc.db.url.host:localhost}/${kc.db.url.database:keycloak}${kc.db.url.properties:}",
"postgres-95", "postgres-10");
final String driver;
final Function<String, String> dialect;
final Function<String, String> defaultUrl;
final String[] aliases;
Vendor(String driver, String dialect, String defaultUrl, String... aliases) {
this(driver, (alias) -> dialect, (alias) -> defaultUrl, aliases);
}
Vendor(String driver, String dialect, Function<String, String> defaultUrl, String... aliases) {
this(driver, (alias) -> dialect, defaultUrl, aliases);
}
Vendor(String driver, Function<String, String> dialect, String defaultUrl, String... aliases) {
this(driver, dialect, (alias) -> defaultUrl, aliases);
}
Vendor(String driver, Function<String, String> dialect, Function<String, String> defaultUrl, String... aliases) {
this.driver = driver;
this.dialect = dialect;
this.defaultUrl = defaultUrl;
this.aliases = aliases;
}
}
}

View file

@ -17,15 +17,12 @@
package org.keycloak.configuration; package org.keycloak.configuration;
import java.io.BufferedReader;
import java.io.Closeable; import java.io.Closeable;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOError; import java.io.IOError;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.NoSuchFileException; import java.nio.file.NoSuchFileException;
import java.nio.file.Path; import java.nio.file.Path;
@ -62,13 +59,9 @@ public abstract class KeycloakPropertiesConfigSource extends PropertiesConfigSou
return Collections.emptyMap(); return Collections.emptyMap();
} }
try (Closeable ignored = is) { try (Closeable ignored = is) {
try (InputStreamReader isr = new InputStreamReader(is, StandardCharsets.UTF_8)) { Properties properties = new Properties();
try (BufferedReader br = new BufferedReader(isr)) { properties.load(is);
final Properties properties = new Properties(); return transform((Map<String, String>) (Map) properties);
properties.load(br);
return transform((Map<String, String>) (Map) properties);
}
}
} catch (IOException e) { } catch (IOException e) {
throw new IOError(e); throw new IOError(e);
} }

View file

@ -53,8 +53,7 @@ public final class PropertyMappers {
Boolean enabled = Boolean.valueOf(value); Boolean enabled = Boolean.valueOf(value);
ConfigValue proxy = context.proceed(MicroProfileConfigProvider.NS_KEYCLOAK_PREFIX + "proxy"); ConfigValue proxy = context.proceed(MicroProfileConfigProvider.NS_KEYCLOAK_PREFIX + "proxy");
if ("dev".equalsIgnoreCase(ProfileManager.getActiveProfile()) || if (Environment.isDevMode() || (proxy != null && "edge".equalsIgnoreCase(proxy.getValue()))) {
(proxy != null && "edge".equalsIgnoreCase(proxy.getValue()))) {
enabled = true; enabled = true;
} }
@ -119,74 +118,17 @@ public final class PropertyMappers {
} }
private static void configureDatabasePropertyMappers() { private static void configureDatabasePropertyMappers() {
createBuildTimeProperty("db", "quarkus.hibernate-orm.dialect", (db, context) -> { createBuildTimeProperty("db", "quarkus.hibernate-orm.dialect", (db, context) -> Database.getDialect(db).orElse(null), null);
switch (db.toLowerCase()) { create("db", "quarkus.datasource.jdbc.driver", (db, context) -> Database.getDriver(db).orElse(null), null);
case "h2-file": createBuildTimeProperty("db", "quarkus.datasource.db-kind", (db, context) -> {
case "h2-mem": if (Database.isSupported(db)) {
return "io.quarkus.hibernate.orm.runtime.dialect.QuarkusH2Dialect"; return db;
case "mariadb":
return "org.hibernate.dialect.MariaDBDialect";
case "mysql":
return "org.hibernate.dialect.MySQL8Dialect";
case "postgres-95":
return "io.quarkus.hibernate.orm.runtime.dialect.QuarkusPostgreSQL95Dialect";
case "postgres": // shorthand for the recommended postgres version
case "postgres-10":
return "io.quarkus.hibernate.orm.runtime.dialect.QuarkusPostgreSQL10Dialect";
}
return null;
}, null);
create("db", "quarkus.datasource.jdbc.driver", (db, context) -> {
switch (db.toLowerCase()) {
case "h2-file":
case "h2-mem":
return "org.h2.jdbcx.JdbcDataSource";
case "mariadb":
return "org.mariadb.jdbc.MySQLDataSource";
case "mysql":
return "com.mysql.cj.jdbc.MysqlXADataSource";
case "postgres":
case "postgres-95":
case "postgres-10":
return "org.postgresql.xa.PGXADataSource";
}
return null;
}, null);
create("db", "quarkus.datasource.db-kind", (db, context) -> {
switch (db.toLowerCase()) {
case "h2-file":
case "h2-mem":
return "h2";
case "mariadb":
return "mariadb";
case "mysql":
return "mysql";
case "postgres":
case "postgres-95":
case "postgres-10":
return "postgresql";
} }
addInitializationException(invalidDatabaseVendor(db, "h2-file", "h2-mem", "mariadb", "mysql", "postgres", "postgres-95", "postgres-10")); addInitializationException(invalidDatabaseVendor(db, "h2-file", "h2-mem", "mariadb", "mysql", "postgres", "postgres-95", "postgres-10"));
return "h2"; return "h2";
}, "The database vendor. Possible values are: h2-mem, h2-file, mariadb, mysql, postgres95, postgres10."); }, "The database vendor. Possible values are: h2-mem, h2-file, mariadb, mysql, postgres95, postgres10.");
create("db", "quarkus.datasource.jdbc.transactions", (db, context) -> "xa", null); create("db", "quarkus.datasource.jdbc.transactions", (db, context) -> "xa", null);
create("db.url", "db", "quarkus.datasource.jdbc.url", (value, context) -> { create("db.url", "db", "quarkus.datasource.jdbc.url", (value, context) -> Database.getDefaultUrl(value).orElse(value), "The database JDBC URL. If not provided a default URL is set based on the selected database vendor. For instance, if using 'postgres', the JDBC URL would be 'jdbc:postgresql://localhost/keycloak'. The host, database and properties can be overridden by setting the following system properties, respectively: -Dkc.db.url.host, -Dkc.db.url.database, -Dkc.db.url.properties.");
switch (value.toLowerCase()) {
case "h2-file":
return "jdbc:h2:file:${kc.home.dir:${kc.db.url.path:~}}/${kc.data.dir:data}/keycloakdb${kc.db.url.properties:;;AUTO_SERVER=TRUE}";
case "h2-mem":
return "jdbc:h2:mem:keycloakdb${kc.db.url.properties:}";
case "mariadb":
return "jdbc:mariadb://${kc.db.url.host:localhost}/${kc.db.url.database:keycloak}${kc.db.url.properties:}";
case "postgres":
case "postgres-95":
case "postgres-10":
return "jdbc:postgresql://${kc.db.url.host:localhost}/${kc.db.url.database:keycloak}${kc.db.url.properties:}";
case "mysql":
return "jdbc:mysql://${kc.db.url.host:localhost}/${kc.db.url.database:keycloak}${kc.db.url.properties:}";
}
return value;
}, "The database JDBC URL. If not provided a default URL is set based on the selected database vendor. For instance, if using 'postgres', the JDBC URL would be 'jdbc:postgresql://localhost/keycloak'. The host, database and properties can be overridden by setting the following system properties, respectively: -Dkc.db.url.host, -Dkc.db.url.database, -Dkc.db.url.properties.");
create("db.username", "quarkus.datasource.username", "The database username."); create("db.username", "quarkus.datasource.username", "The database username.");
create("db.password", "quarkus.datasource.password", "The database password", true); create("db.password", "quarkus.datasource.password", "The database password", true);
create("db.schema", "quarkus.datasource.schema", "The database schema."); create("db.schema", "quarkus.datasource.schema", "The database schema.");
@ -212,11 +154,6 @@ public final class PropertyMappers {
.anyMatch(entry -> entry.getValue().getFrom().equals(name) && entry.getValue().isBuildTime()); .anyMatch(entry -> entry.getValue().getFrom().equals(name) && entry.getValue().isBuildTime());
} }
public static boolean isSupported(String name) {
return PropertyMapper.MAPPERS.entrySet().stream()
.anyMatch(entry -> toCLIFormat(entry.getValue().getFrom()).equals(name));
}
public static String toCLIFormat(String name) { public static String toCLIFormat(String name) {
if (name.indexOf('.') == -1) { if (name.indexOf('.') == -1) {
return name; return name;

View file

@ -17,14 +17,9 @@
package org.keycloak.provider.quarkus; package org.keycloak.provider.quarkus;
import java.io.BufferedReader; import java.net.URL;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.stream.Collectors;
import org.infinispan.commons.util.FileLookupFactory; import org.infinispan.commons.util.FileLookupFactory;
import org.infinispan.configuration.parsing.ConfigurationBuilderHolder; import org.infinispan.configuration.parsing.ConfigurationBuilderHolder;
@ -45,8 +40,7 @@ public final class QuarkusCacheManagerProvider implements ManagedCacheManagerPro
@Override @Override
public <C> C getCacheManager(Config.Scope config) { public <C> C getCacheManager(Config.Scope config) {
try { try {
String configurationAsString = loadConfigurationToString(config); ConfigurationBuilderHolder builder = new ParserRegistry().parse(loadConfiguration(config));
ConfigurationBuilderHolder builder = new ParserRegistry().parse(configurationAsString);
if (builder.getNamedConfigurationBuilders().get("sessions").clustering().cacheMode().isClustered()) { if (builder.getNamedConfigurationBuilders().get("sessions").clustering().cacheMode().isClustered()) {
configureTransportStack(config, builder); configureTransportStack(config, builder);
@ -58,17 +52,12 @@ public final class QuarkusCacheManagerProvider implements ManagedCacheManagerPro
} }
} }
private String loadConfigurationToString(Config.Scope config) throws FileNotFoundException { private URL loadConfiguration(Config.Scope config) {
BufferedReader configurationReader = new BufferedReader(new InputStreamReader(loadConfiguration(config), StandardCharsets.UTF_8));
return configurationReader.lines().collect(Collectors.joining(System.lineSeparator()));
}
private InputStream loadConfiguration(Config.Scope config) throws FileNotFoundException {
String pathPrefix; String pathPrefix;
String homeDir = Environment.getHomeDir(); String homeDir = Environment.getHomeDir();
if (homeDir == null) { if (homeDir == null) {
log.warn("Keycloak home directory not set."); log.warn("Keycloak home directory not set");
pathPrefix = ""; pathPrefix = "";
} else { } else {
pathPrefix = homeDir + "/conf/"; pathPrefix = homeDir + "/conf/";
@ -78,18 +67,24 @@ public final class QuarkusCacheManagerProvider implements ManagedCacheManagerPro
String configFile = config.get("configFile"); String configFile = config.get("configFile");
if (configFile != null) { if (configFile != null) {
Path configPath = Paths.get(pathPrefix + configFile); Path configPath = Paths.get(pathPrefix + configFile);
String path;
if (configPath.toFile().exists()) { if (configPath.toFile().exists()) {
log.infof("Loading cache configuration from %s", configPath); path = configPath.toFile().getAbsolutePath();
return FileLookupFactory.newInstance()
.lookupFileStrict(configPath.toUri(), Thread.currentThread().getContextClassLoader());
} else { } else {
log.infof("Loading cache configuration from %s", configPath); path = configPath.getFileName().toString();
return FileLookupFactory.newInstance()
.lookupFileStrict(configPath.getFileName().toString(), Thread.currentThread().getContextClassLoader());
} }
log.infof("Loading cluster configuration from %s", configPath);
URL url = FileLookupFactory.newInstance().lookupFileLocation(path, Thread.currentThread().getContextClassLoader());
if (url == null) {
throw new IllegalArgumentException("Could not load cluster configuration file at [" + configPath + "]");
}
return url;
} else { } else {
throw new IllegalStateException("Option 'configFile' needs to be specified"); throw new IllegalArgumentException("Option 'configFile' needs to be specified");
} }
} }

View file

@ -79,4 +79,8 @@ public final class Environment {
public static SmallRyeConfig getConfig() { public static SmallRyeConfig getConfig() {
return KeycloakRecorder.getConfig(); return KeycloakRecorder.getConfig();
} }
public static boolean isDevMode() {
return "dev".equalsIgnoreCase(getProfile());
}
} }

View file

@ -6,3 +6,8 @@ db=h2-file
%dev.db.username = sa %dev.db.username = sa
%dev.db.password = keycloak %dev.db.password = keycloak
%dev.cluster=local %dev.cluster=local
# Logging configuration. INFO is the default level for most of the categories
#quarkus.log.level = DEBUG
quarkus.log.category."org.jboss.resteasy.resteasy_jaxrs.i18n".level=WARN
quarkus.log.category."org.infinispan.transaction.lookup.JBossStandaloneJTAManagerLookup".level=WARN