Introducing --optimise option

Closes #10737
This commit is contained in:
Pedro Igor 2022-07-15 08:26:50 -03:00
parent b959e5c32a
commit 89028613d8
32 changed files with 545 additions and 441 deletions

View file

@ -53,6 +53,19 @@ db-url-host=mykeycloakdb
The configuration source and the corresponding format you should use is use-case specific. That decision depends on the platform where the server is deployed and the runtime optimizations you are seeking. For instance, if you deploy the server into Kubernetes, you would probably rely The configuration source and the corresponding format you should use is use-case specific. That decision depends on the platform where the server is deployed and the runtime optimizations you are seeking. For instance, if you deploy the server into Kubernetes, you would probably rely
on environment variables to configure the server. However, you are not limited to a single configuration source or format. on environment variables to configure the server. However, you are not limited to a single configuration source or format.
== Starting the server
You start the server by entering the following command:
.Starting the server
<@kc.start parameters="--db postgres --db-url-host keycloak-postgres --db-username keycloak --db-password change_me --hostname mykeycloak.acme.com"/>
Under certain circumstances, you might prefer to allow a longer startup time in favor of updating the values of build options when starting the server, hence performing the two configuration stages
when starting the server.
By default, running the `start` command is going to automatically run the `build` command prior to starting the server. While this is a handy way to start the server with any available option,
including **build options** that otherwise are only available to the `build` command, it introduces an additional overhead in the server startup time. For most environments, this approach is not optimal.
== Configuration commands and stages == Configuration commands and stages
The server options are narrowed to a specific command or configuration stage. The goal is to perform a series of optimizations in a specific order to achieve optimal startup and runtime performance. This configuration occurs in two stages: The server options are narrowed to a specific command or configuration stage. The goal is to perform a series of optimizations in a specific order to achieve optimal startup and runtime performance. This configuration occurs in two stages:
@ -84,18 +97,20 @@ The following are some optimizations performed by the `build` command:
Once you run the `build` command, you do not need to set the same **build options** when you start the server. Once you run the `build` command, you do not need to set the same **build options** when you start the server.
=== Second stage: Starting Keycloak === Second stage: Starting an optimized server instance
The second stage involves starting the server by using the `start` command and including **configuration options** with the appropriate values. In order to achieve an optimal startup time when the server was previously built with any **build option**, you should run the `start` command together with the `--optimise` option and including any **configuration option** you want to set.
.Run the `start` command to start the server while setting various database options .Run the `start` command to start the server while setting various database options
<@kc.start parameters="--db-url-host=keycloak-postgres --db-username=keycloak --db-password=change_me --hostname mykeycloak.acme.com"/> <@kc.start parameters="--optimise --db-url-host=keycloak-postgres --db-username=keycloak --db-password=change_me --hostname mykeycloak.acme.com"/>
The `--optimise` indicates that the server was previously configured with any build option so that it no longer need to run the `build` command prior to start the server.
Note that if you invoke commands containing special shell characters such as `;` using the CLI, you need to escape those characters. In that situation, you might choose to use the `keycloak.conf` file to set configuration options instead. Note that if you invoke commands containing special shell characters such as `;` using the CLI, you need to escape those characters. In that situation, you might choose to use the `keycloak.conf` file to set configuration options instead.
== Optimization by using a configuration file == Optimization by using a configuration file
Most optimizations to startup and memory footprint can be achieved by using the `build` command. Additionally, you can use the `conf/keycloak.conf` file to set configuration options. Using this file avoids some necessary parsing steps when providing configuration options using the CLI or environment variables. Most optimizations to startup and memory footprint can be achieved by using the `build` command. Additionally, you can use the `conf/keycloak.conf` file to set configuration options. Using this file avoids some necessary parsing steps when providing configuration options using the CLI.
.Set any build option .Set any build option
<@kc.build parameters="--db=postgres"/> <@kc.build parameters="--db=postgres"/>
@ -109,21 +124,10 @@ hostname=mykeycloak.acme.com
``` ```
.Start the server .Start the server
<@kc.start/> <@kc.start parameters="--optimise"/>
By using the `keycloak.conf` file, the server can omit some steps at startup. As a result, the server starts faster. By using the `keycloak.conf` file, the server can omit some steps at startup. As a result, the server starts faster.
== The auto-build option: automatic detection when the server needs a build
Under certain circumstances, you might prefer to allow a longer startup time in favor of updating the values of build options when starting the server. Using the `auto-build` option, you can perform the two configuration stages by using a single command. Note that using `auto-build` is very likely to double the startup time for Keycloak. For most environments, this approach is not optimal.
You start the server by entering the following command:
.Using the `auto-build` option
<@kc.start parameters="--auto-build --db postgres --db-url-host keycloak-postgres --db-username keycloak --db-password change_me --hostname mykeycloak.acme.com"/>
By including the `auto-build` option, the server calculates the build options that have changed and runs the `build` command, if necessary, before starting the server.
== Configuring the server by using configuration files == Configuring the server by using configuration files
By default, the server always fetches configuration options from the `conf/keycloak.conf` file. For a new installation, By default, the server always fetches configuration options from the `conf/keycloak.conf` file. For a new installation,

View file

@ -74,7 +74,7 @@ To start the image, run:
podman|docker run --name optimized_keycloak -p 8443:8443 \ podman|docker run --name optimized_keycloak -p 8443:8443 \
-e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=change_me \ -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=change_me \
prebuilt_keycloak \ prebuilt_keycloak \
start start --optimised
---- ----
Keycloak starts in production mode, using only secured HTTPS communication, and is available on `https://localhost:8443`. Keycloak starts in production mode, using only secured HTTPS communication, and is available on `https://localhost:8443`.
Notice that the startup log contains the following line: Notice that the startup log contains the following line:
@ -105,10 +105,10 @@ Invoking this command starts the Keycloak server in development mode.
This mode should be strictly avoided in production environments because it has insecure defaults. This mode should be strictly avoided in production environments because it has insecure defaults.
For more information about running Keycloak in production, take a look at the <@links.server id="configuration-production"/> guide. For more information about running Keycloak in production, take a look at the <@links.server id="configuration-production"/> guide.
== Use auto-build to run a standard keycloak container == Running a standard keycloak container
In keeping with concepts such as immutable infrastructure, containers need to be re-provisioned routinely. In keeping with concepts such as immutable infrastructure, containers need to be re-provisioned routinely.
In these environments, you need containers that start fast, therefore you need to create an optimized image as described in the preceding section. In these environments, you need containers that start fast, therefore you need to create an optimized image as described in the preceding section.
However, if your environment has different requirements, you can run a standard Keycloak image using the `--auto-build` flag. However, if your environment has different requirements, you can run a standard Keycloak image by just running the `start` command.
For example: For example:
[source, bash] [source, bash]
@ -117,7 +117,6 @@ podman|docker run --name keycloak_auto_build -p 8080:8080 \
-e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=change_me \ -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=change_me \
quay.io/keycloak/keycloak:latest \ quay.io/keycloak/keycloak:latest \
start \ start \
--auto-build \
--db=postgres --features=token-exchange \ --db=postgres --features=token-exchange \
--db-url=<JDBC-URL> --db-username=<DB-USER> --db-password=<DB-PASSWORD> \ --db-url=<JDBC-URL> --db-username=<DB-USER> --db-password=<DB-PASSWORD> \
--https-key-store-file=<file> --https-key-store-password=<password> --https-key-store-file=<file> --https-key-store-password=<password>

View file

@ -513,8 +513,9 @@ public class KeycloakDeployment extends OperatorManagedResource implements Statu
Container container = baseDeployment.getSpec().getTemplate().getSpec().getContainers().get(0); Container container = baseDeployment.getSpec().getTemplate().getSpec().getContainers().get(0);
var customImage = Optional.ofNullable(keycloakCR.getSpec().getImage()); var customImage = Optional.ofNullable(keycloakCR.getSpec().getImage());
container.setImage(customImage.orElse(config.keycloak().image())); container.setImage(customImage.orElse(config.keycloak().image()));
if (customImage.isEmpty()) {
container.getArgs().add("--auto-build"); if (customImage.isPresent()) {
container.getArgs().add("--optimised");
} }
container.setImagePullPolicy(config.keycloak().imagePullPolicy()); container.setImagePullPolicy(config.keycloak().imagePullPolicy());

View file

@ -388,8 +388,7 @@ public class KeycloakDeploymentTest extends BaseOperatorTest {
.list() .list()
.getItems(); .getItems();
assertEquals(1, pods.get(0).getSpec().getContainers().get(0).getArgs().size()); assertThat(pods.get(0).getSpec().getContainers().get(0).getArgs()).containsExactly("start", "--optimised");
assertEquals("start", pods.get(0).getSpec().getContainers().get(0).getArgs().get(0));
} catch (Exception e) { } catch (Exception e) {
savePodLogs(); savePodLogs();
throw e; throw e;

View file

@ -24,6 +24,7 @@ set "SERVER_OPTS=-Djava.util.logging.manager=org.jboss.logmanager.LogManager -Dq
set DEBUG_MODE=false set DEBUG_MODE=false
set DEBUG_PORT_VAR=8787 set DEBUG_PORT_VAR=8787
set DEBUG_SUSPEND_VAR=n set DEBUG_SUSPEND_VAR=n
set CONFIG_ARGS=
rem Read command-line args, the ~ removes the quotes from the parameter rem Read command-line args, the ~ removes the quotes from the parameter
:READ-ARGS :READ-ARGS
@ -128,10 +129,12 @@ set "JAVA_RUN_OPTS=%JAVA_OPTS% -Dkc.home.dir="%DIRNAME%.." -Djboss.server.config
SetLocal EnableDelayedExpansion SetLocal EnableDelayedExpansion
set "AUTO_BUILD_OPTION=auto-build" set "ONLY_BUILD_OPTION= build"
set "NO_AUTO_BUILD_OPTION=optimised"
if not "!CONFIG_ARGS:%AUTO_BUILD_OPTION%=!"=="!CONFIG_ARGS!" ( if "!CONFIG_ARGS:%NO_AUTO_BUILD_OPTION%=!"=="!CONFIG_ARGS!" if "!CONFIG_ARGS:%ONLY_BUILD_OPTION%=!"=="!CONFIG_ARGS!" (
"%JAVA%" -Dkc.config.rebuild-and-exit=true %JAVA_RUN_OPTS% "%JAVA%" -Dkc.config.build-and-exit=true %JAVA_RUN_OPTS%
set "JAVA_RUN_OPTS=-Dkc.config.built=true %JAVA_RUN_OPTS%"
) )
"%JAVA%" %JAVA_RUN_OPTS% "%JAVA%" %JAVA_RUN_OPTS%

View file

@ -48,7 +48,7 @@ do
*) *)
if [[ $1 = --* || ! $1 =~ ^-D.* ]]; then if [[ $1 = --* || ! $1 =~ ^-D.* ]]; then
if [[ "$1" = "start-dev" ]]; then if [[ "$1" = "start-dev" ]]; then
CONFIG_ARGS="$CONFIG_ARGS --profile=dev $1 --auto-build" CONFIG_ARGS="$CONFIG_ARGS --profile=dev $1"
else else
CONFIG_ARGS="$CONFIG_ARGS $1" CONFIG_ARGS="$CONFIG_ARGS $1"
fi fi
@ -96,8 +96,9 @@ CLASSPATH_OPTS="'$DIRNAME'/../lib/quarkus-run.jar"
JAVA_RUN_OPTS="$JAVA_OPTS $SERVER_OPTS -cp $CLASSPATH_OPTS io.quarkus.bootstrap.runner.QuarkusEntryPoint ${CONFIG_ARGS#?}" JAVA_RUN_OPTS="$JAVA_OPTS $SERVER_OPTS -cp $CLASSPATH_OPTS io.quarkus.bootstrap.runner.QuarkusEntryPoint ${CONFIG_ARGS#?}"
if [[ $CONFIG_ARGS = *"--auto-build"* ]]; then if [[ (! $CONFIG_ARGS = *"--optimised"*) ]] && [[ ! "$CONFIG_ARGS" == " build"* ]]; then
eval "$JAVA" -Dkc.config.rebuild-and-exit=true $JAVA_RUN_OPTS eval "$JAVA" -Dkc.config.build-and-exit=true $JAVA_RUN_OPTS
JAVA_RUN_OPTS="-Dkc.config.built=true $JAVA_RUN_OPTS"
EXIT_CODE=$? EXIT_CODE=$?
if [ $EXIT_CODE != 0 ]; then if [ $EXIT_CODE != 0 ]; then
exit $EXIT_CODE exit $EXIT_CODE

View file

@ -224,6 +224,10 @@ public final class Environment {
} }
public static boolean isRebuildCheck() { public static boolean isRebuildCheck() {
return Boolean.getBoolean("kc.config.rebuild-and-exit"); return Boolean.getBoolean("kc.config.build-and-exit");
}
public static boolean isRebuilt() {
return Boolean.getBoolean("kc.config.built");
} }
} }

View file

@ -23,6 +23,7 @@ import static org.keycloak.quarkus.runtime.Environment.getProfileOrDefault;
import static org.keycloak.quarkus.runtime.Environment.isImportExportMode; import static org.keycloak.quarkus.runtime.Environment.isImportExportMode;
import static org.keycloak.quarkus.runtime.Environment.isTestLaunchMode; import static org.keycloak.quarkus.runtime.Environment.isTestLaunchMode;
import static org.keycloak.quarkus.runtime.cli.Picocli.parseAndRun; import static org.keycloak.quarkus.runtime.cli.Picocli.parseAndRun;
import static org.keycloak.quarkus.runtime.cli.command.AbstractStartCommand.*;
import static org.keycloak.quarkus.runtime.cli.command.Start.isDevProfileNotAllowed; import static org.keycloak.quarkus.runtime.cli.command.Start.isDevProfileNotAllowed;
import java.io.PrintWriter; import java.io.PrintWriter;
@ -69,7 +70,7 @@ public class KeycloakMain implements QuarkusApplication {
cliArgs = new ArrayList<>(cliArgs); cliArgs = new ArrayList<>(cliArgs);
// default to show help message // default to show help message
cliArgs.add("-h"); cliArgs.add("-h");
} else if (cliArgs.contains(Start.NAME) && cliArgs.size() == 1) { } else if (isFastStart(cliArgs)) {
// fast path for starting the server without bootstrapping CLI // fast path for starting the server without bootstrapping CLI
ExecutionExceptionHandler errorHandler = new ExecutionExceptionHandler(); ExecutionExceptionHandler errorHandler = new ExecutionExceptionHandler();
PrintWriter errStream = new PrintWriter(System.err, true); PrintWriter errStream = new PrintWriter(System.err, true);
@ -88,6 +89,11 @@ public class KeycloakMain implements QuarkusApplication {
parseAndRun(cliArgs); parseAndRun(cliArgs);
} }
private static boolean isFastStart(List<String> cliArgs) {
// 'start --optimised' should start the server without parsing CLI
return cliArgs.size() == 2 && cliArgs.get(0).equals(Start.NAME) && cliArgs.stream().anyMatch(OPTIMISED_BUILD_OPTION_LONG::equals);
}
public static void start(ExecutionExceptionHandler errorHandler, PrintWriter errStream) { public static void start(ExecutionExceptionHandler errorHandler, PrintWriter errStream) {
ClassLoader originalCl = Thread.currentThread().getContextClassLoader(); ClassLoader originalCl = Thread.currentThread().getContextClassLoader();

View file

@ -17,6 +17,9 @@
package org.keycloak.quarkus.runtime.cli; package org.keycloak.quarkus.runtime.cli;
import static org.keycloak.quarkus.runtime.Environment.isRebuildCheck;
import static org.keycloak.quarkus.runtime.Environment.isRebuilt;
import static org.keycloak.quarkus.runtime.cli.command.AbstractStartCommand.*;
import static org.keycloak.quarkus.runtime.cli.command.AbstractStartCommand.AUTO_BUILD_OPTION_LONG; import static org.keycloak.quarkus.runtime.cli.command.AbstractStartCommand.AUTO_BUILD_OPTION_LONG;
import static org.keycloak.quarkus.runtime.cli.command.AbstractStartCommand.AUTO_BUILD_OPTION_SHORT; import static org.keycloak.quarkus.runtime.cli.command.AbstractStartCommand.AUTO_BUILD_OPTION_SHORT;
import static org.keycloak.quarkus.runtime.configuration.ConfigArgsConfigSource.parseConfigArgs; import static org.keycloak.quarkus.runtime.configuration.ConfigArgsConfigSource.parseConfigArgs;
@ -40,6 +43,7 @@ import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.function.Predicate;
import java.util.function.UnaryOperator; import java.util.function.UnaryOperator;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -47,6 +51,7 @@ import java.util.stream.Collectors;
import org.eclipse.microprofile.config.spi.ConfigSource; import org.eclipse.microprofile.config.spi.ConfigSource;
import org.keycloak.config.MultiOption; import org.keycloak.config.MultiOption;
import org.keycloak.config.OptionCategory; import org.keycloak.config.OptionCategory;
import org.keycloak.quarkus.runtime.cli.command.AbstractStartCommand;
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;
@ -98,7 +103,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 (!isHelpCommand(cliArgs)) {
if (cliArgs.contains(StartDev.NAME)) { if (cliArgs.contains(StartDev.NAME)) {
String profile = Environment.getProfile(); String profile = Environment.getProfile();
@ -118,10 +123,6 @@ public final class Picocli {
return cliArgs.contains("--help") || cliArgs.contains("-h") || cliArgs.contains("--help-all"); return cliArgs.contains("--help") || cliArgs.contains("-h") || cliArgs.contains("--help-all");
} }
public static boolean hasAutoBuildOption(List<String> cliArgs) {
return cliArgs.contains(AUTO_BUILD_OPTION_LONG) || cliArgs.contains(AUTO_BUILD_OPTION_SHORT);
}
public static boolean requiresReAugmentation(CommandLine cmd) { public static boolean requiresReAugmentation(CommandLine cmd) {
if (hasConfigChanges()) { if (hasConfigChanges()) {
if (!ConfigArgsConfigSource.getAllCliArgs().contains(StartDev.NAME) && "dev".equals(getConfig().getOptionalValue("kc.profile", String.class).orElse(null))) { if (!ConfigArgsConfigSource.getAllCliArgs().contains(StartDev.NAME) && "dev".equals(getConfig().getOptionalValue("kc.profile", String.class).orElse(null))) {
@ -169,24 +170,18 @@ public final class Picocli {
List<String> configArgsList = new ArrayList<>(cliArgs); List<String> configArgsList = new ArrayList<>(cliArgs);
// remove this once auto-build option is removed
configArgsList.remove(AUTO_BUILD_OPTION_LONG); configArgsList.remove(AUTO_BUILD_OPTION_LONG);
configArgsList.remove(AUTO_BUILD_OPTION_SHORT); configArgsList.remove(AUTO_BUILD_OPTION_SHORT);
configArgsList.remove(ImportRealmMixin.IMPORT_REALM); configArgsList.remove(ImportRealmMixin.IMPORT_REALM);
configArgsList.replaceAll(new UnaryOperator<String>() { configArgsList.replaceAll(Picocli::replaceStartWithBuild);
@Override
public String apply(String arg) {
if (arg.equals(Start.NAME) || arg.equals(StartDev.NAME)) {
return Build.NAME;
}
return arg;
}
});
exitCode = cmd.execute(configArgsList.toArray(new String[0])); exitCode = cmd.execute(configArgsList.toArray(new String[0]));
if(!isDevMode() && exitCode == cmd.getCommandSpec().exitCodeOnSuccess()) { if(!isDevMode() && exitCode == cmd.getCommandSpec().exitCodeOnSuccess()) {
cmd.getOut().printf("Next time you run the server, just run:%n%n\t%s %s %s%n%n", Environment.getCommand(), Start.NAME, String.join(" ", getSanitizedRuntimeCliOptions())); cmd.getOut().printf("Next time you run the server, just run:%n%n\t%s %s %s %s%n%n", Environment.getCommand(), Start.NAME, OPTIMISED_BUILD_OPTION_LONG, String.join(" ", getSanitizedRuntimeCliOptions()));
} }
return exitCode; return exitCode;
@ -346,9 +341,9 @@ public final class Picocli {
.build()); .build());
} }
addOption(spec, Start.NAME, hasAutoBuildOption(cliArgs), true); addOption(spec, Start.NAME, isRebuilt(), true);
addOption(spec, StartDev.NAME, true, true); addOption(spec, StartDev.NAME, true, true);
addOption(spec, Build.NAME, true, hasAutoBuildOption(cliArgs)); addOption(spec, Build.NAME, true, isRebuildCheck());
CommandLine cmd = new CommandLine(spec); CommandLine cmd = new CommandLine(spec);
@ -467,8 +462,19 @@ public final class Picocli {
} }
} }
} }
if (!isRebuildCheck() && (arg.startsWith(AbstractStartCommand.AUTO_BUILD_OPTION_SHORT) || arg.startsWith(AUTO_BUILD_OPTION_LONG))) {
System.out.println(DEFAULT_WARN_MESSAGE_REPEATED_AUTO_BUILD_OPTION);
}
} }
return args; return args;
} }
private static String replaceStartWithBuild(String arg) {
if (arg.equals(Start.NAME) || arg.equals(StartDev.NAME)) {
return Build.NAME;
}
return arg;
}
} }

View file

@ -24,8 +24,11 @@ import picocli.CommandLine;
public abstract class AbstractStartCommand extends AbstractCommand implements Runnable { public abstract class AbstractStartCommand extends AbstractCommand implements Runnable {
// remove this once auto-build is removed
public static final String AUTO_BUILD_OPTION_LONG = "--auto-build"; public static final String AUTO_BUILD_OPTION_LONG = "--auto-build";
public static final String AUTO_BUILD_OPTION_SHORT = "-b"; public static final String AUTO_BUILD_OPTION_SHORT = "-b";
public static final String OPTIMISED_BUILD_OPTION_LONG = "--optimised";
public static final String DEFAULT_WARN_MESSAGE_REPEATED_AUTO_BUILD_OPTION = "WARNING: The '" + AUTO_BUILD_OPTION_LONG + "' option for 'start' command is DEPRECATED and no longer needed. When executing the '" + Start.NAME + "' command, a new server image is automatically built based on the configuration. If you want to disable this behavior and achieve an optimal startup time, use the '" + OPTIMISED_BUILD_OPTION_LONG + "' option instead.";
@Override @Override
public void run() { public void run() {

View file

@ -56,10 +56,7 @@ import picocli.CommandLine.Command;
+ " Enable metrics endpoints:%n%n" + " Enable metrics endpoints:%n%n"
+ " $ ${PARENT-COMMAND-FULL-NAME:-$PARENTCOMMAND} ${COMMAND-NAME} --metrics-enabled=true%n%n" + " $ ${PARENT-COMMAND-FULL-NAME:-$PARENTCOMMAND} ${COMMAND-NAME} --metrics-enabled=true%n%n"
+ " Change the relative path:%n%n" + " Change the relative path:%n%n"
+ " $ ${PARENT-COMMAND-FULL-NAME:-$PARENTCOMMAND} ${COMMAND-NAME} --http-relative-path=/auth%n%n" + " $ ${PARENT-COMMAND-FULL-NAME:-$PARENTCOMMAND} ${COMMAND-NAME} --http-relative-path=/auth%n")
+ "You can also use the \"--auto-build\" option when starting the server to avoid running this command every time you change a configuration:%n%n"
+ " $ ${PARENT-COMMAND-FULL-NAME:-$PARENTCOMMAND} start --auto-build <OPTIONS>%n%n"
+ "By doing that you have an additional overhead when the server is starting.")
public final class Build extends AbstractCommand implements Runnable { public final class Build extends AbstractCommand implements Runnable {
public static final String NAME = "build"; public static final String NAME = "build";

View file

@ -35,21 +35,27 @@ import java.util.Optional;
description = { description = {
"%nUse this command to run the server in production." "%nUse this command to run the server in production."
}, },
footer = "%nYou may use the \"--auto-build\" option when starting the server to avoid running the \"build\" command everytime you need to change a static property:%n%n" footer = "%nBy default, this command tries to update the server configuration by running a '" + Build.NAME + "' before starting the server. You can disable this behavior by using the '" + Start.OPTIMISED_BUILD_OPTION_LONG + "' option:%n%n"
+ " $ ${PARENT-COMMAND-FULL-NAME:-$PARENTCOMMAND} ${COMMAND-NAME} --auto-build <OPTIONS>%n%n" + " $ ${PARENT-COMMAND-FULL-NAME:-$PARENTCOMMAND} ${COMMAND-NAME} '" + Start.OPTIMISED_BUILD_OPTION_LONG + "'%n%n"
+ "By doing that you have an additional overhead when the server is starting. Run \"${PARENT-COMMAND-FULL-NAME:-$PARENTCOMMAND} build -h\" for more details.") + "By doing that, the server should start faster based on any previous configuration you have set when manually running the '" + Build.NAME + "' command.")
public final class Start extends AbstractStartCommand implements Runnable { public final class Start extends AbstractStartCommand implements Runnable {
public static final String NAME = "start"; public static final String NAME = "start";
@CommandLine.Option(names = {AUTO_BUILD_OPTION_SHORT, AUTO_BUILD_OPTION_LONG}, @CommandLine.Option(names = {AUTO_BUILD_OPTION_SHORT, AUTO_BUILD_OPTION_LONG},
description = "Automatically detects whether the server configuration changed and a new server image must be built" + description = "(Deprecated) Automatically detects whether the server configuration changed and a new server image must be built" +
" prior to starting the server. This option provides an alternative to manually running the '" + Build.NAME + "'" + " prior to starting the server. This option provides an alternative to manually running the '" + Build.NAME + "'" +
" prior to starting the server. Use this configuration carefully in production as it might impact the startup time.", " prior to starting the server. Use this configuration carefully in production as it might impact the startup time.",
paramLabel = NO_PARAM_LABEL, paramLabel = NO_PARAM_LABEL,
order = 1) order = 1)
Boolean autoConfig; Boolean autoConfig;
@CommandLine.Option(names = {OPTIMISED_BUILD_OPTION_LONG},
description = "Use this option to achieve an optional startup time if you have previously built a server image using the 'build' command.",
paramLabel = NO_PARAM_LABEL,
order = 1)
Boolean noAutoConfig;
@CommandLine.Mixin @CommandLine.Mixin
ImportRealmMixin importRealmMixin; ImportRealmMixin importRealmMixin;

View file

@ -21,7 +21,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.api.Assertions.fail;
import static org.testcontainers.shaded.org.hamcrest.MatcherAssert.assertThat; import static org.testcontainers.shaded.org.hamcrest.MatcherAssert.assertThat;
import static org.testcontainers.shaded.org.hamcrest.Matchers.containsString; import static org.testcontainers.shaded.org.hamcrest.Matchers.*;
import java.util.List; import java.util.List;
@ -92,6 +92,15 @@ public interface CLIResult extends LaunchResult {
assertThat(getOutput(), containsString(message)); assertThat(getOutput(), containsString(message));
} }
default void assertNoMessage(String message) {
assertThat(getOutput(), not(containsString(message)));
}
default void assertMessageWasShownExactlyNumberOfTimes(String message, long numberOfShownTimes) {
long msgCount = getOutput().lines().filter(oneMessage -> oneMessage.contains(message)).count();
assertThat(msgCount, equalTo(numberOfShownTimes));
}
default void assertBuild() { default void assertBuild() {
assertMessage("Server configuration updated and persisted"); assertMessage("Server configuration updated and persisted");
} }
@ -140,4 +149,5 @@ public interface CLIResult extends LaunchResult {
fail("No JSON found in output."); fail("No JSON found in output.");
} }
} }
} }

View file

@ -27,6 +27,8 @@ import io.quarkus.test.junit.main.LaunchResult;
import org.keycloak.quarkus.runtime.cli.command.Start; 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 static org.keycloak.quarkus.runtime.cli.command.AbstractStartCommand.OPTIMISED_BUILD_OPTION_LONG;
@CLITest @CLITest
public class HelpCommandTest { public class HelpCommandTest {
@ -52,7 +54,7 @@ public class HelpCommandTest {
} }
@Test @Test
@Launch({ Start.NAME, "--help" }) @Launch({ Start.NAME, "--help", OPTIMISED_BUILD_OPTION_LONG})
void testStartHelp(LaunchResult result) { void testStartHelp(LaunchResult result) {
CLIResult cliResult = (CLIResult) result; CLIResult cliResult = (CLIResult) result;
cliResult.assertHelp(); cliResult.assertHelp();

View file

@ -18,6 +18,7 @@
package org.keycloak.it.cli; package org.keycloak.it.cli;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.keycloak.quarkus.runtime.cli.command.AbstractStartCommand.OPTIMISED_BUILD_OPTION_LONG;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.keycloak.it.junit5.extension.CLIResult; import org.keycloak.it.junit5.extension.CLIResult;
@ -51,7 +52,7 @@ public class StartCommandTest {
} }
@Test @Test
@Launch({ "-v", "start", "--db=dev-mem" }) @Launch({ "-v", "start", "--db=dev-mem", OPTIMISED_BUILD_OPTION_LONG})
void failBuildPropertyNotAvailable(LaunchResult result) { void failBuildPropertyNotAvailable(LaunchResult result) {
CLIResult cliResult = (CLIResult) result; CLIResult cliResult = (CLIResult) result;
cliResult.assertError("Unknown option: '--db'"); cliResult.assertError("Unknown option: '--db'");

View file

@ -31,6 +31,8 @@ import org.keycloak.it.utils.KeycloakDistribution;
import io.quarkus.test.junit.main.Launch; import io.quarkus.test.junit.main.Launch;
import io.quarkus.test.junit.main.LaunchResult; import io.quarkus.test.junit.main.LaunchResult;
import static org.keycloak.quarkus.runtime.cli.command.AbstractStartCommand.OPTIMISED_BUILD_OPTION_LONG;
@DistributionTest(reInstall = DistributionTest.ReInstall.NEVER) @DistributionTest(reInstall = DistributionTest.ReInstall.NEVER)
@RawDistOnly(reason = "Containers are immutable") @RawDistOnly(reason = "Containers are immutable")
@TestMethodOrder(OrderAnnotation.class) @TestMethodOrder(OrderAnnotation.class)
@ -45,7 +47,7 @@ public class BuildAndStartDistTest {
} }
@Test @Test
@Launch({ "start", "--http-enabled=true", "--hostname-strict=false" }) @Launch({ "start", "--http-enabled=true", "--hostname-strict=false", OPTIMISED_BUILD_OPTION_LONG})
@Order(2) @Order(2)
void testStartUsingCliArgs(LaunchResult result) { void testStartUsingCliArgs(LaunchResult result) {
CLIResult cliResult = (CLIResult) result; CLIResult cliResult = (CLIResult) result;
@ -63,7 +65,7 @@ public class BuildAndStartDistTest {
} }
@Test @Test
@Launch({ "start" }) @Launch({ "start", OPTIMISED_BUILD_OPTION_LONG})
@Order(4) @Order(4)
void testStartUsingConfFile(LaunchResult result) { void testStartUsingConfFile(LaunchResult result) {
CLIResult cliResult = (CLIResult) result; CLIResult cliResult = (CLIResult) result;

View file

@ -72,7 +72,7 @@ public class ClusterConfigDistTest {
@Test @Test
@EnabledOnOs(value = { OS.LINUX, OS.MAC }, disabledReason = "different shell escaping behaviour on Windows.") @EnabledOnOs(value = { OS.LINUX, OS.MAC }, disabledReason = "different shell escaping behaviour on Windows.")
@Launch({ "start", "--auto-build", "--log-level=info,org.infinispan.remoting.transport.jgroups.JGroupsTransport:debug","--http-enabled=true", "--hostname-strict=false" }) @Launch({ "start", "--log-level=info,org.infinispan.remoting.transport.jgroups.JGroupsTransport:debug","--http-enabled=true", "--hostname-strict=false" })
void testStartDefaultsToClustering(LaunchResult result) { void testStartDefaultsToClustering(LaunchResult result) {
CLIResult cliResult = (CLIResult) result; CLIResult cliResult = (CLIResult) result;
cliResult.assertStarted(); cliResult.assertStarted();
@ -82,7 +82,7 @@ public class ClusterConfigDistTest {
@Test @Test
@EnabledOnOs(value = { OS.WINDOWS }, disabledReason = "different shell behaviour on Windows.") @EnabledOnOs(value = { OS.WINDOWS }, disabledReason = "different shell behaviour on Windows.")
@Launch({ "start", "--auto-build", "--log-level=\"info,org.infinispan.remoting.transport.jgroups.JGroupsTransport:debug","--http-enabled=true\"", "--hostname-strict=false" }) @Launch({ "start", "--log-level=\"info,org.infinispan.remoting.transport.jgroups.JGroupsTransport:debug","--http-enabled=true\"", "--hostname-strict=false" })
void testWinStartDefaultsToClustering(LaunchResult result) { void testWinStartDefaultsToClustering(LaunchResult result) {
CLIResult cliResult = (CLIResult) result; CLIResult cliResult = (CLIResult) result;
cliResult.assertStarted(); cliResult.assertStarted();

View file

@ -19,6 +19,7 @@ import org.keycloak.quarkus.runtime.cli.command.StartDev;
import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.keycloak.quarkus.runtime.cli.command.AbstractStartCommand.OPTIMISED_BUILD_OPTION_LONG;
@DistributionTest @DistributionTest
@RawDistOnly(reason = "Containers are immutable") @RawDistOnly(reason = "Containers are immutable")
@ -35,7 +36,7 @@ public class FeaturesDistTest {
} }
@Test @Test
@Launch({ Start.NAME, "--http-enabled=true", "--hostname-strict=false"}) @Launch({ Start.NAME, "--http-enabled=true", "--hostname-strict=false", OPTIMISED_BUILD_OPTION_LONG})
@Order(2) @Order(2)
public void testFeatureEnabledOnStart(LaunchResult result) { public void testFeatureEnabledOnStart(LaunchResult result) {
assertPreviewFeaturesEnabled((CLIResult) result); assertPreviewFeaturesEnabled((CLIResult) result);

View file

@ -135,6 +135,7 @@ public class LoggingDistTest {
void testWinLogLevelSettingsAppliedWhenJsonEnabled(LaunchResult result) { void testWinLogLevelSettingsAppliedWhenJsonEnabled(LaunchResult result) {
CLIResult cliResult = (CLIResult) result; CLIResult cliResult = (CLIResult) result;
assertFalse(cliResult.getOutput().contains("\"loggerName\":\"io.quarkus\",\"level\":\"INFO\")")); assertFalse(cliResult.getOutput().contains("\"loggerName\":\"io.quarkus\",\"level\":\"INFO\")"));
assertTrue(cliResult.getOutput().contains("\"loggerName\":\"org.keycloak.quarkus.runtime.storage.legacy.database.LegacyJpaConnectionProviderFactory\",\"level\":\"DEBUG\""));
assertTrue(cliResult.getOutput().contains("\"loggerName\":\"org.keycloak.services.resources.KeycloakApplication\",\"level\":\"DEBUG\"")); assertTrue(cliResult.getOutput().contains("\"loggerName\":\"org.keycloak.services.resources.KeycloakApplication\",\"level\":\"DEBUG\""));
assertTrue(cliResult.getOutput().contains("\"loggerName\":\"org.infinispan.CONTAINER\",\"level\":\"INFO\"")); assertTrue(cliResult.getOutput().contains("\"loggerName\":\"org.infinispan.CONTAINER\",\"level\":\"INFO\""));
} }

View file

@ -41,7 +41,7 @@ import io.quarkus.test.junit.main.LaunchResult;
public class QuarkusPropertiesAutoBuildDistTest { public class QuarkusPropertiesAutoBuildDistTest {
@Test @Test
@Launch({ "start", "--auto-build", "--http-enabled=true", "--hostname-strict=false", "--cache=local" }) @Launch({ "start", "--http-enabled=true", "--hostname-strict=false", "--cache=local" })
@Order(1) @Order(1)
void reAugOnFirstRun(LaunchResult result) { void reAugOnFirstRun(LaunchResult result) {
CLIResult cliResult = (CLIResult) result; CLIResult cliResult = (CLIResult) result;
@ -50,7 +50,7 @@ public class QuarkusPropertiesAutoBuildDistTest {
@Test @Test
@BeforeStartDistribution(QuarkusPropertiesAutoBuildDistTest.UpdateConsoleLogLevelToWarn.class) @BeforeStartDistribution(QuarkusPropertiesAutoBuildDistTest.UpdateConsoleLogLevelToWarn.class)
@Launch({ "start", "--auto-build", "--http-enabled=true", "--hostname-strict=false", "--cache=local" }) @Launch({ "start", "--http-enabled=true", "--hostname-strict=false", "--cache=local" })
@Order(2) @Order(2)
void testQuarkusRuntimePropDoesNotTriggerReAug(LaunchResult result) { void testQuarkusRuntimePropDoesNotTriggerReAug(LaunchResult result) {
CLIResult cliResult = (CLIResult) result; CLIResult cliResult = (CLIResult) result;
@ -60,7 +60,7 @@ public class QuarkusPropertiesAutoBuildDistTest {
@Test @Test
@BeforeStartDistribution(UpdateConsoleLogLevelToInfo.class) @BeforeStartDistribution(UpdateConsoleLogLevelToInfo.class)
@Launch({ "start", "--auto-build", "--http-enabled=true", "--hostname-strict=false", "--cache=local" }) @Launch({ "start", "--http-enabled=true", "--hostname-strict=false", "--cache=local" })
@Order(3) @Order(3)
void testNoReAugAfterChangingRuntimeProperty(LaunchResult result) { void testNoReAugAfterChangingRuntimeProperty(LaunchResult result) {
CLIResult cliResult = (CLIResult) result; CLIResult cliResult = (CLIResult) result;
@ -70,7 +70,7 @@ public class QuarkusPropertiesAutoBuildDistTest {
@Test @Test
@BeforeStartDistribution(AddAdditionalDatasource.class) @BeforeStartDistribution(AddAdditionalDatasource.class)
@Launch({ "start", "--auto-build", "--http-enabled=true", "--hostname-strict=false", "--cache=local" }) @Launch({ "start", "--http-enabled=true", "--hostname-strict=false", "--cache=local" })
@Order(4) @Order(4)
void testReAugForAdditionalDatasource(LaunchResult result) { void testReAugForAdditionalDatasource(LaunchResult result) {
CLIResult cliResult = (CLIResult) result; CLIResult cliResult = (CLIResult) result;
@ -79,7 +79,7 @@ public class QuarkusPropertiesAutoBuildDistTest {
@Test @Test
@BeforeStartDistribution(ChangeAdditionalDatasourceUsername.class) @BeforeStartDistribution(ChangeAdditionalDatasourceUsername.class)
@Launch({ "start", "--auto-build", "--http-enabled=true", "--hostname-strict=false", "--cache=local" }) @Launch({ "start", "--http-enabled=true", "--hostname-strict=false", "--cache=local" })
@Order(5) @Order(5)
void testNoReAugForAdditionalDatasourceRuntimeProperty(LaunchResult result) { void testNoReAugForAdditionalDatasourceRuntimeProperty(LaunchResult result) {
CLIResult cliResult = (CLIResult) result; CLIResult cliResult = (CLIResult) result;
@ -88,7 +88,7 @@ public class QuarkusPropertiesAutoBuildDistTest {
@Test @Test
@BeforeStartDistribution(ChangeAdditionalDatasourceDbKind.class) @BeforeStartDistribution(ChangeAdditionalDatasourceDbKind.class)
@Launch({ "start", "--auto-build", "--http-enabled=true", "--hostname-strict=false", "--cache=local" }) @Launch({ "start", "--http-enabled=true", "--hostname-strict=false", "--cache=local" })
@Order(6) @Order(6)
void testNoReAugWhenBuildTimePropertiesAreTheSame(LaunchResult result) { void testNoReAugWhenBuildTimePropertiesAreTheSame(LaunchResult result) {
CLIResult cliResult = (CLIResult) result; CLIResult cliResult = (CLIResult) result;
@ -97,7 +97,7 @@ public class QuarkusPropertiesAutoBuildDistTest {
@Test @Test
@BeforeStartDistribution(AddAdditionalDatasource2.class) @BeforeStartDistribution(AddAdditionalDatasource2.class)
@Launch({ "start", "--auto-build", "--http-enabled=true", "--hostname-strict=false", "--cache=local" }) @Launch({ "start", "--http-enabled=true", "--hostname-strict=false", "--cache=local" })
@Order(7) @Order(7)
void testReAugWhenAnotherDatasourceAdded(LaunchResult result) { void testReAugWhenAnotherDatasourceAdded(LaunchResult result) {
CLIResult cliResult = (CLIResult) result; CLIResult cliResult = (CLIResult) result;
@ -106,7 +106,7 @@ public class QuarkusPropertiesAutoBuildDistTest {
@Test @Test
@BeforeStartDistribution(EnableDatasourceMetrics.class) @BeforeStartDistribution(EnableDatasourceMetrics.class)
@Launch({ "start", "--auto-build", "--http-enabled=true", "--hostname-strict=false", "--cache=local" }) @Launch({ "start", "--http-enabled=true", "--hostname-strict=false", "--cache=local" })
@Order(8) @Order(8)
void testWrappedBuildPropertyTriggersBuildButGetsIgnoredWhenSetByQuarkus(LaunchResult result) { void testWrappedBuildPropertyTriggersBuildButGetsIgnoredWhenSetByQuarkus(LaunchResult result) {
CLIResult cliResult = (CLIResult) result; CLIResult cliResult = (CLIResult) result;

View file

@ -21,6 +21,7 @@ import static io.restassured.RestAssured.when;
import static org.hamcrest.Matchers.containsString; 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;
import static org.keycloak.quarkus.runtime.cli.command.AbstractStartCommand.OPTIMISED_BUILD_OPTION_LONG;
import java.util.function.Consumer; import java.util.function.Consumer;
import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
@ -119,7 +120,7 @@ public class QuarkusPropertiesDistTest {
@Test @Test
@KeepServerAlive @KeepServerAlive
@Launch({ "start", "--http-enabled=true", "--hostname-strict=false" }) @Launch({ "start", "--http-enabled=true", "--hostname-strict=false", OPTIMISED_BUILD_OPTION_LONG})
@Order(9) @Order(9)
void testUnknownQuarkusBuildTimePropertyApplied(LaunchResult result) { void testUnknownQuarkusBuildTimePropertyApplied(LaunchResult result) {
CLIResult cliResult = (CLIResult) result; CLIResult cliResult = (CLIResult) result;

View file

@ -17,9 +17,8 @@
package org.keycloak.it.cli.dist; package org.keycloak.it.cli.dist;
import static org.junit.jupiter.api.Assertions.assertFalse; import io.quarkus.test.junit.main.Launch;
import static org.junit.jupiter.api.Assertions.assertTrue; import io.quarkus.test.junit.main.LaunchResult;
import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -27,10 +26,13 @@ import org.junit.jupiter.api.TestMethodOrder;
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.RawDistOnly; import org.keycloak.it.junit5.extension.RawDistOnly;
import io.quarkus.test.junit.main.Launch;
import io.quarkus.test.junit.main.LaunchResult;
import org.keycloak.it.utils.KeycloakDistribution; import org.keycloak.it.utils.KeycloakDistribution;
import org.keycloak.quarkus.runtime.cli.command.AbstractStartCommand;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.keycloak.quarkus.runtime.cli.command.AbstractStartCommand.DEFAULT_WARN_MESSAGE_REPEATED_AUTO_BUILD_OPTION;
import static org.keycloak.quarkus.runtime.cli.command.AbstractStartCommand.OPTIMISED_BUILD_OPTION_LONG;
@DistributionTest(reInstall = DistributionTest.ReInstall.NEVER) @DistributionTest(reInstall = DistributionTest.ReInstall.NEVER)
@RawDistOnly(reason = "Containers are immutable") @RawDistOnly(reason = "Containers are immutable")
@ -38,7 +40,7 @@ import org.keycloak.it.utils.KeycloakDistribution;
public class StartAutoBuildDistTest { public class StartAutoBuildDistTest {
@Test @Test
@Launch({ "start", "--auto-build", "--http-enabled=true", "--hostname-strict=false", "--cache=local" }) @Launch({ "start", AbstractStartCommand.AUTO_BUILD_OPTION_LONG, "--http-enabled=true", "--hostname-strict=false", "--cache=local" })
@Order(1) @Order(1)
void testStartAutoBuild(LaunchResult result) { void testStartAutoBuild(LaunchResult result) {
CLIResult cliResult = (CLIResult) result; CLIResult cliResult = (CLIResult) result;
@ -47,13 +49,14 @@ public class StartAutoBuildDistTest {
cliResult.assertMessage("Server configuration updated and persisted. Run the following command to review the configuration:"); cliResult.assertMessage("Server configuration updated and persisted. Run the following command to review the configuration:");
cliResult.assertMessage(KeycloakDistribution.SCRIPT_CMD + " show-config"); cliResult.assertMessage(KeycloakDistribution.SCRIPT_CMD + " show-config");
cliResult.assertMessage("Next time you run the server, just run:"); cliResult.assertMessage("Next time you run the server, just run:");
cliResult.assertMessage(KeycloakDistribution.SCRIPT_CMD + " start --http-enabled=true --hostname-strict=false"); cliResult.assertMessage(KeycloakDistribution.SCRIPT_CMD + " start " + OPTIMISED_BUILD_OPTION_LONG + " --http-enabled=true --hostname-strict=false");
cliResult.assertMessage(DEFAULT_WARN_MESSAGE_REPEATED_AUTO_BUILD_OPTION);
assertFalse(cliResult.getOutput().contains("--cache")); assertFalse(cliResult.getOutput().contains("--cache"));
cliResult.assertStarted(); cliResult.assertStarted();
} }
@Test @Test
@Launch({ "start", "--auto-build", "--http-enabled=true", "--hostname-strict=false", "--cache=local" }) @Launch({ "start", "--http-enabled=true", "--hostname-strict=false", "--cache=local" })
@Order(2) @Order(2)
void testShouldNotReAugIfConfigIsSame(LaunchResult result) { void testShouldNotReAugIfConfigIsSame(LaunchResult result) {
CLIResult cliResult = (CLIResult) result; CLIResult cliResult = (CLIResult) result;
@ -62,7 +65,7 @@ public class StartAutoBuildDistTest {
} }
@Test @Test
@Launch({ "start", "--auto-build", "--db=dev-mem", "--http-enabled=true", "--hostname-strict=false", "--cache=local" }) @Launch({ "start", "--db=dev-mem", "--http-enabled=true", "--hostname-strict=false", "--cache=local" })
@Order(3) @Order(3)
void testShouldReAugIfConfigChanged(LaunchResult result) { void testShouldReAugIfConfigChanged(LaunchResult result) {
CLIResult cliResult = (CLIResult) result; CLIResult cliResult = (CLIResult) result;
@ -71,7 +74,7 @@ public class StartAutoBuildDistTest {
} }
@Test @Test
@Launch({ "start", "--auto-build", "--db=dev-mem", "--http-enabled=true", "--hostname-strict=false", "--cache=local" }) @Launch({ "start", "--db=dev-mem", "--http-enabled=true", "--hostname-strict=false", "--cache=local" })
@Order(4) @Order(4)
void testShouldNotReAugIfSameDatabase(LaunchResult result) { void testShouldNotReAugIfSameDatabase(LaunchResult result) {
CLIResult cliResult = (CLIResult) result; CLIResult cliResult = (CLIResult) result;
@ -88,7 +91,7 @@ public class StartAutoBuildDistTest {
} }
@Test @Test
@Launch({ "start", "--auto-build", "--http-enabled=true", "--hostname-strict=false", "--cache=local" }) @Launch({ "start", "--http-enabled=true", "--hostname-strict=false", "--cache=local" })
@Order(6) @Order(6)
void testReAugWhenNoOptionAfterBuild(LaunchResult result) { void testReAugWhenNoOptionAfterBuild(LaunchResult result) {
CLIResult cliResult = (CLIResult) result; CLIResult cliResult = (CLIResult) result;
@ -97,8 +100,25 @@ public class StartAutoBuildDistTest {
} }
@Test @Test
@Launch({ "start-dev" }) @Launch({ "start", "--db=postgres", "--http-enabled=true", "--hostname-strict=false", "--cache=local" })
@Order(7) @Order(7)
void testShouldReAugWithoutAutoBuildOptionAfterDatabaseChange(LaunchResult result) {
CLIResult cliResult = (CLIResult) result;
cliResult.assertNoMessage(DEFAULT_WARN_MESSAGE_REPEATED_AUTO_BUILD_OPTION);
cliResult.assertBuild();
}
@Test
@Launch({ "start", "--db=dev-file", "--http-enabled=true", "--hostname-strict=false", "--cache=local", OPTIMISED_BUILD_OPTION_LONG})
@Order(8)
void testShouldReAugAndNeedsAutoBuildOptionBecauseHasNoAutoBuildOption(LaunchResult result) {
CLIResult cliResult = (CLIResult) result;
cliResult.assertNoBuild();
}
@Test
@Launch({ "start-dev" })
@Order(8)
void testStartDevFirstTime(LaunchResult result) { void testStartDevFirstTime(LaunchResult result) {
CLIResult cliResult = (CLIResult) result; CLIResult cliResult = (CLIResult) result;
assertTrue(cliResult.getOutput().contains("Updating the configuration and installing your custom providers, if any. Please wait.")); assertTrue(cliResult.getOutput().contains("Updating the configuration and installing your custom providers, if any. Please wait."));
@ -107,7 +127,7 @@ public class StartAutoBuildDistTest {
@Test @Test
@Launch({ "start-dev" }) @Launch({ "start-dev" })
@Order(8) @Order(9)
void testShouldNotReAugStartDevIfConfigIsSame(LaunchResult result) { void testShouldNotReAugStartDevIfConfigIsSame(LaunchResult result) {
CLIResult cliResult = (CLIResult) result; CLIResult cliResult = (CLIResult) result;
assertFalse(cliResult.getOutput().contains("Updating the configuration and installing your custom providers, if any. Please wait.")); assertFalse(cliResult.getOutput().contains("Updating the configuration and installing your custom providers, if any. Please wait."));

View file

@ -22,6 +22,7 @@ import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
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;
import static org.keycloak.quarkus.runtime.cli.command.AbstractStartCommand.OPTIMISED_BUILD_OPTION_LONG;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.keycloak.it.cli.StartCommandTest; import org.keycloak.it.cli.StartCommandTest;
@ -36,7 +37,7 @@ import org.keycloak.it.utils.KeycloakDistribution;
public class StartCommandDistTest extends StartCommandTest { public class StartCommandDistTest extends StartCommandTest {
@Test @Test
@Launch({ "--profile=dev", "start", "--auto-build", "--http-enabled=true", "--hostname-strict=false" }) @Launch({ "--profile=dev", "start", "--http-enabled=true", "--hostname-strict=false" })
void failIfAutoBuildUsingDevProfile(LaunchResult result) { void failIfAutoBuildUsingDevProfile(LaunchResult result) {
CLIResult cliResult = (CLIResult) result; CLIResult cliResult = (CLIResult) result;
assertThat(cliResult.getErrorOutput(), containsString("You can not 'start' the server in development mode. Please re-build the server first, using 'kc.sh build' for the default production mode.")); assertThat(cliResult.getErrorOutput(), containsString("You can not 'start' the server in development mode. Please re-build the server first, using 'kc.sh build' for the default production mode."));
@ -51,7 +52,7 @@ public class StartCommandDistTest extends StartCommandTest {
} }
@Test @Test
@Launch({ "start", "--auto-build", "--http-enabled=true", "--hostname-strict=false", "--cache=local" }) @Launch({ "start", "--http-enabled=true", "--hostname-strict=false", "--cache=local" })
void testStartUsingAutoBuild(LaunchResult result) { void testStartUsingAutoBuild(LaunchResult result) {
CLIResult cliResult = (CLIResult) result; CLIResult cliResult = (CLIResult) result;
cliResult.assertMessage("Changes detected in configuration. Updating the server image."); cliResult.assertMessage("Changes detected in configuration. Updating the server image.");
@ -59,7 +60,7 @@ public class StartCommandDistTest extends StartCommandTest {
cliResult.assertMessage("Server configuration updated and persisted. Run the following command to review the configuration:"); cliResult.assertMessage("Server configuration updated and persisted. Run the following command to review the configuration:");
cliResult.assertMessage(KeycloakDistribution.SCRIPT_CMD + " show-config"); cliResult.assertMessage(KeycloakDistribution.SCRIPT_CMD + " show-config");
cliResult.assertMessage("Next time you run the server, just run:"); cliResult.assertMessage("Next time you run the server, just run:");
cliResult.assertMessage(KeycloakDistribution.SCRIPT_CMD + " start --http-enabled=true --hostname-strict=false"); cliResult.assertMessage(KeycloakDistribution.SCRIPT_CMD + " start " + OPTIMISED_BUILD_OPTION_LONG + " --http-enabled=true --hostname-strict=false");
assertFalse(cliResult.getOutput().contains("--cache")); assertFalse(cliResult.getOutput().contains("--cache"));
cliResult.assertStarted(); cliResult.assertStarted();
} }

View file

@ -17,9 +17,43 @@
package org.keycloak.it.cli.dist; package org.keycloak.it.cli.dist;
import io.quarkus.test.junit.main.Launch;
import io.quarkus.test.junit.main.LaunchResult;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import org.keycloak.it.cli.StartDevCommandTest; import org.keycloak.it.cli.StartDevCommandTest;
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;
@DistributionTest import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
@DistributionTest(reInstall = DistributionTest.ReInstall.NEVER)
@RawDistOnly(reason = "Containers are immutable")
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class StartDevCommandDistTest extends StartDevCommandTest { public class StartDevCommandDistTest extends StartDevCommandTest {
@Test
@Launch({ "start-dev", "--debug" })
@Order(1)
void testStartDevShouldStartTwoJVMs(LaunchResult result) {
CLIResult cliResult = (CLIResult) result;
cliResult.assertMessage("Updating the configuration and installing your custom providers, if any. Please wait.");
cliResult.assertMessageWasShownExactlyNumberOfTimes("Listening for transport dt_socket at address:", 2);
cliResult.assertStartedDevMode();
}
@Test
@Launch({ "build", "--debug" })
@Order(2)
void testBuildMustNotRunTwoJVMs(LaunchResult result) {
CLIResult cliResult = (CLIResult) result;
cliResult.assertMessage("Updating the configuration and installing your custom providers, if any. Please wait.");
cliResult.assertMessageWasShownExactlyNumberOfTimes("Listening for transport dt_socket at address:", 1);
cliResult.assertBuild();
}
} }

View file

@ -22,6 +22,7 @@ import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder; import org.junit.jupiter.api.TestMethodOrder;
import org.keycloak.it.junit5.extension.CLIResult; import org.keycloak.it.junit5.extension.CLIResult;
import org.keycloak.quarkus.runtime.cli.command.AbstractStartCommand;
import io.quarkus.test.junit.main.Launch; import io.quarkus.test.junit.main.Launch;
import io.quarkus.test.junit.main.LaunchResult; import io.quarkus.test.junit.main.LaunchResult;
@ -30,14 +31,14 @@ import io.quarkus.test.junit.main.LaunchResult;
public abstract class BasicDatabaseTest { public abstract class BasicDatabaseTest {
@Test @Test
@Launch({ "start", "--http-enabled=true", "--hostname-strict=false" }) @Launch({ "start", AbstractStartCommand.OPTIMISED_BUILD_OPTION_LONG, "--http-enabled=true", "--hostname-strict=false" })
void testSuccessful(LaunchResult result) { void testSuccessful(LaunchResult result) {
CLIResult cliResult = (CLIResult) result; CLIResult cliResult = (CLIResult) result;
cliResult.assertStarted(); cliResult.assertStarted();
} }
@Test @Test
@Launch({ "start", "--http-enabled=true", "--hostname-strict=false", "--db-username=wrong" }) @Launch({ "start", AbstractStartCommand.OPTIMISED_BUILD_OPTION_LONG,"--http-enabled=true", "--hostname-strict=false", "--db-username=wrong" })
void testWrongUsername(LaunchResult result) { void testWrongUsername(LaunchResult result) {
CLIResult cliResult = (CLIResult) result; CLIResult cliResult = (CLIResult) result;
cliResult.assertMessage("ERROR: Failed to obtain JDBC connection"); cliResult.assertMessage("ERROR: Failed to obtain JDBC connection");
@ -47,7 +48,7 @@ public abstract class BasicDatabaseTest {
protected abstract void assertWrongUsername(CLIResult cliResult); protected abstract void assertWrongUsername(CLIResult cliResult);
@Test @Test
@Launch({ "start", "--http-enabled=true", "--hostname-strict=false", "--db-password=wrong" }) @Launch({ "start", AbstractStartCommand.OPTIMISED_BUILD_OPTION_LONG,"--http-enabled=true", "--hostname-strict=false", "--db-password=wrong" })
void testWrongPassword(LaunchResult result) { void testWrongPassword(LaunchResult result) {
CLIResult cliResult = (CLIResult) result; CLIResult cliResult = (CLIResult) result;
cliResult.assertMessage("ERROR: Failed to obtain JDBC connection"); cliResult.assertMessage("ERROR: Failed to obtain JDBC connection");

View file

@ -32,7 +32,7 @@ import io.quarkus.test.junit.main.LaunchResult;
public class ChmStorageDistTest { public class ChmStorageDistTest {
@Test @Test
@Launch({ "start", "--auto-build", "--http-enabled=true", "--hostname-strict=false", "--storage=chm" }) @Launch({ "start", "--http-enabled=true", "--hostname-strict=false", "--storage=chm" })
void testStartUsingChmsStorage(LaunchResult result, RawDistRootPath distPath) { void testStartUsingChmsStorage(LaunchResult result, RawDistRootPath distPath) {
CLIResult cliResult = (CLIResult) result; CLIResult cliResult = (CLIResult) result;
assertExpectedMessages(cliResult, distPath); assertExpectedMessages(cliResult, distPath);

View file

@ -30,7 +30,7 @@ import io.quarkus.test.junit.main.LaunchResult;
public class JPAStoreDistTest { public class JPAStoreDistTest {
@Test @Test
@Launch({ "start", "--http-enabled=true", "--hostname-strict=false" }) @Launch({ "start", "--optimised", "--http-enabled=true", "--hostname-strict=false" })
void testSuccessful(LaunchResult result) { void testSuccessful(LaunchResult result) {
CLIResult cliResult = (CLIResult) result; CLIResult cliResult = (CLIResult) result;
cliResult.assertMessage("Experimental feature enabled: map_storage"); cliResult.assertMessage("Experimental feature enabled: map_storage");

View file

@ -102,11 +102,4 @@ Examples:
Change the relative path: Change the relative path:
$ kc.sh build --http-relative-path=/auth $ kc.sh build --http-relative-path=/auth
You can also use the "--auto-build" option when starting the server to avoid
running this command every time you change a configuration:
$ kc.sh start --auto-build <OPTIONS>
By doing that you have an additional overhead when the server is starting.

View file

@ -1,159 +1,160 @@
Start the server in development mode. WARNING: The '--auto-build' option for 'start' command is DEPRECATED and no longer needed. When executing the 'start' command, a new server image is automatically built based on the configuration. If you want to disable this behavior and achieve an optimal startup time, use the '--optimised' option instead.
Start the server in development mode.
Usage:
Usage:
kc.bat start-dev [OPTIONS]
kc.bat start-dev [OPTIONS]
Use this command if you want to run the server locally for development or
testing purposes. Use this command if you want to run the server locally for development or
testing purposes.
Options:
Options:
-h, --help This help message.
--help-all This same help message but with additional options. -h, --help This help message.
--import-realm Import realms during startup by reading any realm configuration file from the --help-all This same help message but with additional options.
'data/import' directory. --import-realm Import realms during startup by reading any realm configuration file from the
'data/import' directory.
Database:
Database:
--db-password <password>
The password of the database user. --db-password <password>
--db-pool-initial-size <size> The password of the database user.
The initial size of the connection pool. --db-pool-initial-size <size>
--db-pool-max-size <size> The initial size of the connection pool.
The maximum size of the connection pool. Default: 100. --db-pool-max-size <size>
--db-pool-min-size <size> The maximum size of the connection pool. Default: 100.
The minimal size of the connection pool. --db-pool-min-size <size>
--db-schema <schema> The database schema to be used. The minimal size of the connection pool.
--db-url <jdbc-url> The full database JDBC URL. If not provided, a default URL is set based on the --db-schema <schema> The database schema to be used.
selected database vendor. For instance, if using 'postgres', the default --db-url <jdbc-url> The full database JDBC URL. If not provided, a default URL is set based on the
JDBC URL would be 'jdbc:postgresql://localhost/keycloak'. selected database vendor. For instance, if using 'postgres', the default
--db-url-database <dbname> JDBC URL would be 'jdbc:postgresql://localhost/keycloak'.
Sets the database name of the default JDBC URL of the chosen vendor. If the --db-url-database <dbname>
`db-url` option is set, this option is ignored. Sets the database name of the default JDBC URL of the chosen vendor. If the
--db-url-host <hostname> `db-url` option is set, this option is ignored.
Sets the hostname of the default JDBC URL of the chosen vendor. If the --db-url-host <hostname>
`db-url` option is set, this option is ignored. Sets the hostname of the default JDBC URL of the chosen vendor. If the
--db-url-port <port> Sets the port of the default JDBC URL of the chosen vendor. If the `db-url` `db-url` option is set, this option is ignored.
option is set, this option is ignored. --db-url-port <port> Sets the port of the default JDBC URL of the chosen vendor. If the `db-url`
--db-url-properties <properties> option is set, this option is ignored.
Sets the properties of the default JDBC URL of the chosen vendor. If the --db-url-properties <properties>
`db-url` option is set, this option is ignored. Sets the properties of the default JDBC URL of the chosen vendor. If the
--db-username <username> `db-url` option is set, this option is ignored.
The username of the database user. --db-username <username>
The username of the database user.
Hostname:
Hostname:
--hostname <hostname>
Hostname for the Keycloak server. --hostname <hostname>
--hostname-admin <hostname> Hostname for the Keycloak server.
The hostname for accessing the administration console. Use this option if you --hostname-admin <hostname>
are exposing the administration console using a hostname other than the The hostname for accessing the administration console. Use this option if you
value set to the 'hostname' option. are exposing the administration console using a hostname other than the
--hostname-path <path> value set to the 'hostname' option.
This should be set if proxy uses a different context-path for Keycloak. --hostname-path <path>
--hostname-port <port> This should be set if proxy uses a different context-path for Keycloak.
The port used by the proxy when exposing the hostname. Set this option if the --hostname-port <port>
proxy uses a port other than the default HTTP and HTTPS ports. Default: -1. The port used by the proxy when exposing the hostname. Set this option if the
--hostname-strict <true|false> proxy uses a port other than the default HTTP and HTTPS ports. Default: -1.
Disables dynamically resolving the hostname from request headers. Should --hostname-strict <true|false>
always be set to true in production, unless proxy verifies the Host header. Disables dynamically resolving the hostname from request headers. Should
Default: true. always be set to true in production, unless proxy verifies the Host header.
--hostname-strict-backchannel <true|false> Default: true.
By default backchannel URLs are dynamically resolved from request headers to --hostname-strict-backchannel <true|false>
allow internal and external applications. If all applications use the public By default backchannel URLs are dynamically resolved from request headers to
URL this option should be enabled. Default: false. allow internal and external applications. If all applications use the public
URL this option should be enabled. Default: false.
HTTP/TLS:
HTTP/TLS:
--http-enabled <true|false>
Enables the HTTP listener. Default: false. --http-enabled <true|false>
--http-host <host> The used HTTP Host. Default: 0.0.0.0. Enables the HTTP listener. Default: false.
--http-port <port> The used HTTP port. Default: 8080. --http-host <host> The used HTTP Host. Default: 0.0.0.0.
--https-certificate-file <file> --http-port <port> The used HTTP port. Default: 8080.
The file path to a server certificate or certificate chain in PEM format. --https-certificate-file <file>
--https-certificate-key-file <file> The file path to a server certificate or certificate chain in PEM format.
The file path to a private key in PEM format. --https-certificate-key-file <file>
--https-cipher-suites <ciphers> The file path to a private key in PEM format.
The cipher suites to use. If none is given, a reasonable default is selected. --https-cipher-suites <ciphers>
--https-client-auth <auth> The cipher suites to use. If none is given, a reasonable default is selected.
Configures the server to require/request client authentication. Possible --https-client-auth <auth>
Values: none, request, required. Default: none. Configures the server to require/request client authentication. Possible
--https-key-store-file <file> Values: none, request, required. Default: none.
The key store which holds the certificate information instead of specifying --https-key-store-file <file>
separate files. The key store which holds the certificate information instead of specifying
--https-key-store-password <password> separate files.
The password of the key store file. Default: password. --https-key-store-password <password>
--https-key-store-type <type> The password of the key store file. Default: password.
The type of the key store file. If not given, the type is automatically --https-key-store-type <type>
detected based on the file name. The type of the key store file. If not given, the type is automatically
--https-port <port> The used HTTPS port. Default: 8443. detected based on the file name.
--https-protocols <protocols> --https-port <port> The used HTTPS port. Default: 8443.
The list of protocols to explicitly enable. Default: TLSv1.3. --https-protocols <protocols>
--https-trust-store-file <file> The list of protocols to explicitly enable. Default: TLSv1.3.
The trust store which holds the certificate information of the certificates to --https-trust-store-file <file>
trust. The trust store which holds the certificate information of the certificates to
--https-trust-store-password <password> trust.
The password of the trust store file. --https-trust-store-password <password>
--https-trust-store-type <type> The password of the trust store file.
The type of the trust store file. If not given, the type is automatically --https-trust-store-type <type>
detected based on the file name. The type of the trust store file. If not given, the type is automatically
detected based on the file name.
Proxy:
Proxy:
--proxy <mode> The proxy address forwarding mode if the server is behind a reverse proxy.
Possible values are: edge,reencrypt,passthrough Default: none. --proxy <mode> The proxy address forwarding mode if the server is behind a reverse proxy.
Possible values are: edge,reencrypt,passthrough Default: none.
Vault:
Vault:
--vault-dir <dir> If set, secrets can be obtained by reading the content of files within the
given directory. --vault-dir <dir> If set, secrets can be obtained by reading the content of files within the
given directory.
Logging:
Logging:
--log <handler> Enable one or more log handlers in a comma-separated list. Available log
handlers are: console,file,gelf Default: console. --log <handler> Enable one or more log handlers in a comma-separated list. Available log
--log-console-color <true|false> handlers are: console,file,gelf Default: console.
Enable or disable colors when logging to console. Default: false. --log-console-color <true|false>
--log-console-format <format> Enable or disable colors when logging to console. Default: false.
The format of unstructured console log entries. If the format has spaces in --log-console-format <format>
it, escape the value using "<format>". Default: %d{yyyy-MM-dd HH:mm:ss,SSS} % The format of unstructured console log entries. If the format has spaces in
-5p [%c] (%t) %s%e%n. it, escape the value using "<format>". Default: %d{yyyy-MM-dd HH:mm:ss,SSS} %
--log-console-output <default|json> -5p [%c] (%t) %s%e%n.
Set the log output to JSON or default (plain) unstructured logging. Default: --log-console-output <default|json>
default. Set the log output to JSON or default (plain) unstructured logging. Default:
--log-file <path>/<file-name>.log default.
Set the log file path and filename. Default: data\log\keycloak.log. --log-file <path>/<file-name>.log
--log-file-format <format> Set the log file path and filename. Default: data\log\keycloak.log.
Set a format specific to file log entries. Default: %d{yyyy-MM-dd HH:mm:ss, --log-file-format <format>
SSS} %-5p [%c] (%t) %s%e%n. Set a format specific to file log entries. Default: %d{yyyy-MM-dd HH:mm:ss,
--log-gelf-facility <name> SSS} %-5p [%c] (%t) %s%e%n.
The facility (name of the process) that sends the message. Default: keycloak. --log-gelf-facility <name>
--log-gelf-host <hostname> The facility (name of the process) that sends the message. Default: keycloak.
Hostname of the Logstash or Graylog Host. By default UDP is used, prefix the --log-gelf-host <hostname>
host with 'tcp:' to switch to TCP. Example: 'tcp:localhost' Default: Hostname of the Logstash or Graylog Host. By default UDP is used, prefix the
localhost. host with 'tcp:' to switch to TCP. Example: 'tcp:localhost' Default:
--log-gelf-include-location <true|false> localhost.
Include source code location. Default: true. --log-gelf-include-location <true|false>
--log-gelf-include-message-parameters <true|false> Include source code location. Default: true.
Include message parameters from the log event. Default: true. --log-gelf-include-message-parameters <true|false>
--log-gelf-include-stack-trace <true|false> Include message parameters from the log event. Default: true.
If set to true, occuring stack traces are included in the 'StackTrace' field --log-gelf-include-stack-trace <true|false>
in the gelf output. Default: true. If set to true, occuring stack traces are included in the 'StackTrace' field
--log-gelf-max-message-size <size> in the gelf output. Default: true.
Maximum message size (in bytes). If the message size is exceeded, gelf will --log-gelf-max-message-size <size>
submit the message in multiple chunks. Default: 8192. Maximum message size (in bytes). If the message size is exceeded, gelf will
--log-gelf-port <port> submit the message in multiple chunks. Default: 8192.
The port the Logstash or Graylog Host is called on. Default: 12201. --log-gelf-port <port>
--log-gelf-timestamp-format <pattern> The port the Logstash or Graylog Host is called on. Default: 12201.
Set the format for the gelf timestamp field. Uses Java SimpleDateFormat --log-gelf-timestamp-format <pattern>
pattern. Default: yyyy-MM-dd HH:mm:ss,SSS. Set the format for the gelf timestamp field. Uses Java SimpleDateFormat
--log-level <category:level> pattern. Default: yyyy-MM-dd HH:mm:ss,SSS.
The log level of the root category or a comma-separated list of individual --log-level <category:level>
categories and their levels. For the root category, you don't need to The log level of the root category or a comma-separated list of individual
specify a category. Default: info. categories and their levels. For the root category, you don't need to
specify a category. Default: info.
Do NOT start the server using this command when deploying to production.
Do NOT start the server using this command when deploying to production.
Use 'kc.bat start-dev --help-all' to list all available options, including
build options. Use 'kc.bat start-dev --help-all' to list all available options, including
build options.

View file

@ -8,14 +8,16 @@ Use this command to run the server in production.
Options: Options:
-b, --auto-build Automatically detects whether the server configuration changed and a new -b, --auto-build (Deprecated) Automatically detects whether the server configuration changed
server image must be built prior to starting the server. This option and a new server image must be built prior to starting the server. This
provides an alternative to manually running the 'build' prior to starting option provides an alternative to manually running the 'build' prior to
the server. Use this configuration carefully in production as it might starting the server. Use this configuration carefully in production as it
impact the startup time. might impact the startup time.
-h, --help This help message. -h, --help This help message.
--import-realm Import realms during startup by reading any realm configuration file from the --import-realm Import realms during startup by reading any realm configuration file from the
'data/import' directory. 'data/import' directory.
--optimised Use this option to achieve an optional startup time if you have previously
built a server image using the 'build' command.
Database: Database:
@ -156,10 +158,11 @@ Logging:
categories and their levels. For the root category, you don't need to categories and their levels. For the root category, you don't need to
specify a category. Default: info. specify a category. Default: info.
You may use the "--auto-build" option when starting the server to avoid running By default, this command tries to update the server configuration by running a
the "build" command everytime you need to change a static property: 'build' before starting the server. You can disable this behavior by using the
'--optimised' option:
$ kc.sh start --auto-build <OPTIONS> $ kc.sh start '--optimised'
By doing that you have an additional overhead when the server is starting. Run By doing that, the server should start faster based on any previous
"kc.sh build -h" for more details. configuration you have set when manually running the 'build' command.

View file

@ -1,165 +1,168 @@
Start the server. Start the server.
Usage: Usage:
kc.bat start [OPTIONS] kc.bat start [OPTIONS]
Use this command to run the server in production. Use this command to run the server in production.
Options: Options:
-b, --auto-build Automatically detects whether the server configuration changed and a new -b, --auto-build (Deprecated) Automatically detects whether the server configuration changed
server image must be built prior to starting the server. This option and a new server image must be built prior to starting the server. This
provides an alternative to manually running the 'build' prior to starting option provides an alternative to manually running the 'build' prior to
the server. Use this configuration carefully in production as it might starting the server. Use this configuration carefully in production as it
impact the startup time. might impact the startup time.
-h, --help This help message. -h, --help This help message.
--import-realm Import realms during startup by reading any realm configuration file from the --import-realm Import realms during startup by reading any realm configuration file from the
'data/import' directory. 'data/import' directory.
--optimised Use this option to achieve an optional startup time if you have previously
Database: built a server image using the 'build' command.
--db-password <password> Database:
The password of the database user.
--db-pool-initial-size <size> --db-password <password>
The initial size of the connection pool. The password of the database user.
--db-pool-max-size <size> --db-pool-initial-size <size>
The maximum size of the connection pool. Default: 100. The initial size of the connection pool.
--db-pool-min-size <size> --db-pool-max-size <size>
The minimal size of the connection pool. The maximum size of the connection pool. Default: 100.
--db-schema <schema> The database schema to be used. --db-pool-min-size <size>
--db-url <jdbc-url> The full database JDBC URL. If not provided, a default URL is set based on the The minimal size of the connection pool.
selected database vendor. For instance, if using 'postgres', the default --db-schema <schema> The database schema to be used.
JDBC URL would be 'jdbc:postgresql://localhost/keycloak'. --db-url <jdbc-url> The full database JDBC URL. If not provided, a default URL is set based on the
--db-url-database <dbname> selected database vendor. For instance, if using 'postgres', the default
Sets the database name of the default JDBC URL of the chosen vendor. If the JDBC URL would be 'jdbc:postgresql://localhost/keycloak'.
`db-url` option is set, this option is ignored. --db-url-database <dbname>
--db-url-host <hostname> Sets the database name of the default JDBC URL of the chosen vendor. If the
Sets the hostname of the default JDBC URL of the chosen vendor. If the `db-url` option is set, this option is ignored.
`db-url` option is set, this option is ignored. --db-url-host <hostname>
--db-url-port <port> Sets the port of the default JDBC URL of the chosen vendor. If the `db-url` Sets the hostname of the default JDBC URL of the chosen vendor. If the
option is set, this option is ignored. `db-url` option is set, this option is ignored.
--db-url-properties <properties> --db-url-port <port> Sets the port of the default JDBC URL of the chosen vendor. If the `db-url`
Sets the properties of the default JDBC URL of the chosen vendor. If the option is set, this option is ignored.
`db-url` option is set, this option is ignored. --db-url-properties <properties>
--db-username <username> Sets the properties of the default JDBC URL of the chosen vendor. If the
The username of the database user. `db-url` option is set, this option is ignored.
--db-username <username>
Hostname: The username of the database user.
--hostname <hostname> Hostname:
Hostname for the Keycloak server.
--hostname-admin <hostname> --hostname <hostname>
The hostname for accessing the administration console. Use this option if you Hostname for the Keycloak server.
are exposing the administration console using a hostname other than the --hostname-admin <hostname>
value set to the 'hostname' option. The hostname for accessing the administration console. Use this option if you
--hostname-path <path> are exposing the administration console using a hostname other than the
This should be set if proxy uses a different context-path for Keycloak. value set to the 'hostname' option.
--hostname-port <port> --hostname-path <path>
The port used by the proxy when exposing the hostname. Set this option if the This should be set if proxy uses a different context-path for Keycloak.
proxy uses a port other than the default HTTP and HTTPS ports. Default: -1. --hostname-port <port>
--hostname-strict <true|false> The port used by the proxy when exposing the hostname. Set this option if the
Disables dynamically resolving the hostname from request headers. Should proxy uses a port other than the default HTTP and HTTPS ports. Default: -1.
always be set to true in production, unless proxy verifies the Host header. --hostname-strict <true|false>
Default: true. Disables dynamically resolving the hostname from request headers. Should
--hostname-strict-backchannel <true|false> always be set to true in production, unless proxy verifies the Host header.
By default backchannel URLs are dynamically resolved from request headers to Default: true.
allow internal and external applications. If all applications use the public --hostname-strict-backchannel <true|false>
URL this option should be enabled. Default: false. By default backchannel URLs are dynamically resolved from request headers to
allow internal and external applications. If all applications use the public
HTTP/TLS: URL this option should be enabled. Default: false.
--http-enabled <true|false> HTTP/TLS:
Enables the HTTP listener. Default: false.
--http-host <host> The used HTTP Host. Default: 0.0.0.0. --http-enabled <true|false>
--http-port <port> The used HTTP port. Default: 8080. Enables the HTTP listener. Default: false.
--https-certificate-file <file> --http-host <host> The used HTTP Host. Default: 0.0.0.0.
The file path to a server certificate or certificate chain in PEM format. --http-port <port> The used HTTP port. Default: 8080.
--https-certificate-key-file <file> --https-certificate-file <file>
The file path to a private key in PEM format. The file path to a server certificate or certificate chain in PEM format.
--https-cipher-suites <ciphers> --https-certificate-key-file <file>
The cipher suites to use. If none is given, a reasonable default is selected. The file path to a private key in PEM format.
--https-client-auth <auth> --https-cipher-suites <ciphers>
Configures the server to require/request client authentication. Possible The cipher suites to use. If none is given, a reasonable default is selected.
Values: none, request, required. Default: none. --https-client-auth <auth>
--https-key-store-file <file> Configures the server to require/request client authentication. Possible
The key store which holds the certificate information instead of specifying Values: none, request, required. Default: none.
separate files. --https-key-store-file <file>
--https-key-store-password <password> The key store which holds the certificate information instead of specifying
The password of the key store file. Default: password. separate files.
--https-key-store-type <type> --https-key-store-password <password>
The type of the key store file. If not given, the type is automatically The password of the key store file. Default: password.
detected based on the file name. --https-key-store-type <type>
--https-port <port> The used HTTPS port. Default: 8443. The type of the key store file. If not given, the type is automatically
--https-protocols <protocols> detected based on the file name.
The list of protocols to explicitly enable. Default: TLSv1.3. --https-port <port> The used HTTPS port. Default: 8443.
--https-trust-store-file <file> --https-protocols <protocols>
The trust store which holds the certificate information of the certificates to The list of protocols to explicitly enable. Default: TLSv1.3.
trust. --https-trust-store-file <file>
--https-trust-store-password <password> The trust store which holds the certificate information of the certificates to
The password of the trust store file. trust.
--https-trust-store-type <type> --https-trust-store-password <password>
The type of the trust store file. If not given, the type is automatically The password of the trust store file.
detected based on the file name. --https-trust-store-type <type>
The type of the trust store file. If not given, the type is automatically
Proxy: detected based on the file name.
--proxy <mode> The proxy address forwarding mode if the server is behind a reverse proxy. Proxy:
Possible values are: edge,reencrypt,passthrough Default: none.
--proxy <mode> The proxy address forwarding mode if the server is behind a reverse proxy.
Vault: Possible values are: edge,reencrypt,passthrough Default: none.
--vault-dir <dir> If set, secrets can be obtained by reading the content of files within the Vault:
given directory.
--vault-dir <dir> If set, secrets can be obtained by reading the content of files within the
Logging: given directory.
--log <handler> Enable one or more log handlers in a comma-separated list. Available log Logging:
handlers are: console,file,gelf Default: console.
--log-console-color <true|false> --log <handler> Enable one or more log handlers in a comma-separated list. Available log
Enable or disable colors when logging to console. Default: false. handlers are: console,file,gelf Default: console.
--log-console-format <format> --log-console-color <true|false>
The format of unstructured console log entries. If the format has spaces in Enable or disable colors when logging to console. Default: false.
it, escape the value using "<format>". Default: %d{yyyy-MM-dd HH:mm:ss,SSS} % --log-console-format <format>
-5p [%c] (%t) %s%e%n. The format of unstructured console log entries. If the format has spaces in
--log-console-output <default|json> it, escape the value using "<format>". Default: %d{yyyy-MM-dd HH:mm:ss,SSS} %
Set the log output to JSON or default (plain) unstructured logging. Default: -5p [%c] (%t) %s%e%n.
default. --log-console-output <default|json>
--log-file <path>/<file-name>.log Set the log output to JSON or default (plain) unstructured logging. Default:
Set the log file path and filename. Default: data\log\keycloak.log. default.
--log-file-format <format> --log-file <path>/<file-name>.log
Set a format specific to file log entries. Default: %d{yyyy-MM-dd HH:mm:ss, Set the log file path and filename. Default: data\log\keycloak.log.
SSS} %-5p [%c] (%t) %s%e%n. --log-file-format <format>
--log-gelf-facility <name> Set a format specific to file log entries. Default: %d{yyyy-MM-dd HH:mm:ss,
The facility (name of the process) that sends the message. Default: keycloak. SSS} %-5p [%c] (%t) %s%e%n.
--log-gelf-host <hostname> --log-gelf-facility <name>
Hostname of the Logstash or Graylog Host. By default UDP is used, prefix the The facility (name of the process) that sends the message. Default: keycloak.
host with 'tcp:' to switch to TCP. Example: 'tcp:localhost' Default: --log-gelf-host <hostname>
localhost. Hostname of the Logstash or Graylog Host. By default UDP is used, prefix the
--log-gelf-include-location <true|false> host with 'tcp:' to switch to TCP. Example: 'tcp:localhost' Default:
Include source code location. Default: true. localhost.
--log-gelf-include-message-parameters <true|false> --log-gelf-include-location <true|false>
Include message parameters from the log event. Default: true. Include source code location. Default: true.
--log-gelf-include-stack-trace <true|false> --log-gelf-include-message-parameters <true|false>
If set to true, occuring stack traces are included in the 'StackTrace' field Include message parameters from the log event. Default: true.
in the gelf output. Default: true. --log-gelf-include-stack-trace <true|false>
--log-gelf-max-message-size <size> If set to true, occuring stack traces are included in the 'StackTrace' field
Maximum message size (in bytes). If the message size is exceeded, gelf will in the gelf output. Default: true.
submit the message in multiple chunks. Default: 8192. --log-gelf-max-message-size <size>
--log-gelf-port <port> Maximum message size (in bytes). If the message size is exceeded, gelf will
The port the Logstash or Graylog Host is called on. Default: 12201. submit the message in multiple chunks. Default: 8192.
--log-gelf-timestamp-format <pattern> --log-gelf-port <port>
Set the format for the gelf timestamp field. Uses Java SimpleDateFormat The port the Logstash or Graylog Host is called on. Default: 12201.
pattern. Default: yyyy-MM-dd HH:mm:ss,SSS. --log-gelf-timestamp-format <pattern>
--log-level <category:level> Set the format for the gelf timestamp field. Uses Java SimpleDateFormat
The log level of the root category or a comma-separated list of individual pattern. Default: yyyy-MM-dd HH:mm:ss,SSS.
categories and their levels. For the root category, you don't need to --log-level <category:level>
specify a category. Default: info. The log level of the root category or a comma-separated list of individual
categories and their levels. For the root category, you don't need to
You may use the "--auto-build" option when starting the server to avoid running specify a category. Default: info.
the "build" command everytime you need to change a static property:
By default, this command tries to update the server configuration by running a
$ kc.bat start --auto-build <OPTIONS> 'build' before starting the server. You can disable this behavior by using the
'--optimised' option:
By doing that you have an additional overhead when the server is starting. Run
"kc.bat build -h" for more details. $ kc.bat start '--optimised'
By doing that, the server should start faster based on any previous
configuration you have set when manually running the 'build' command.

View file

@ -177,6 +177,7 @@ public class KeycloakQuarkusServerDeployableContainer implements DeployableConta
commands.add(getCommand()); commands.add(getCommand());
commands.add("-v"); commands.add("-v");
commands.add("start"); commands.add("start");
commands.add("--optimised");
commands.add("--http-enabled=true"); commands.add("--http-enabled=true");
if (Boolean.parseBoolean(System.getProperty("auth.server.debug", "false"))) { if (Boolean.parseBoolean(System.getProperty("auth.server.debug", "false"))) {
@ -195,9 +196,9 @@ public class KeycloakQuarkusServerDeployableContainer implements DeployableConta
commands.add("-Djboss.node.name=" + configuration.getRoute()); commands.add("-Djboss.node.name=" + configuration.getRoute());
} }
// only run auto-build during restarts or when running cluster tests // only run build during restarts or when running cluster tests
if (restart.get() || "ha".equals(System.getProperty("auth.server.quarkus.cluster.config"))) { if (restart.get() || "ha".equals(System.getProperty("auth.server.quarkus.cluster.config"))) {
commands.add("--auto-build"); commands.removeIf("--optimised"::equals);
commands.add("--http-relative-path=/auth"); commands.add("--http-relative-path=/auth");
String cacheMode = System.getProperty("auth.server.quarkus.cluster.config", "local"); String cacheMode = System.getProperty("auth.server.quarkus.cluster.config", "local");