mirror of
https://github.com/keycloak/keycloak.git
synced 2026-01-25 16:42:34 +00:00
fix: refining the usage of distribution tests (#34272)
* fix: refining distribution tests allows for the capturing of dry run build values for subsequent commands closes: #34058 Signed-off-by: Steve Hawkins <shawkins@redhat.com> * converting a few more tests to dry run and several other cleanups also splitting the stdout and stderr collection for docker Signed-off-by: Steve Hawkins <shawkins@redhat.com> --------- Signed-off-by: Steve Hawkins <shawkins@redhat.com>
This commit is contained in:
@@ -392,6 +392,10 @@ public class Profile {
|
||||
return CURRENT;
|
||||
}
|
||||
|
||||
public static void reset() {
|
||||
CURRENT = null;
|
||||
}
|
||||
|
||||
public static boolean isFeatureEnabled(Feature feature) {
|
||||
return getInstance().features.get(feature);
|
||||
}
|
||||
|
||||
@@ -216,7 +216,7 @@ public final class Environment {
|
||||
public static boolean isRebuildCheck() {
|
||||
return Boolean.getBoolean("kc.config.build-and-exit");
|
||||
}
|
||||
|
||||
|
||||
public static void setRebuildCheck() {
|
||||
System.setProperty("kc.config.build-and-exit", "true");
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
package org.keycloak.quarkus.runtime;
|
||||
|
||||
import static org.keycloak.quarkus.runtime.Environment.getKeycloakModeFromProfile;
|
||||
import static org.keycloak.quarkus.runtime.Environment.isDevProfile;
|
||||
import static org.keycloak.quarkus.runtime.Environment.isNonServerMode;
|
||||
import static org.keycloak.quarkus.runtime.Environment.isTestLaunchMode;
|
||||
import static org.keycloak.quarkus.runtime.cli.command.AbstractStartCommand.OPTIMIZED_BUILD_OPTION_LONG;
|
||||
@@ -29,9 +28,7 @@ import java.util.List;
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import org.keycloak.common.profile.ProfileException;
|
||||
import org.keycloak.quarkus.runtime.configuration.mappers.PropertyMappers;
|
||||
import picocli.CommandLine.ExitCode;
|
||||
import org.keycloak.quarkus.runtime.configuration.PersistedConfigSource;
|
||||
|
||||
import io.quarkus.runtime.ApplicationLifecycleManager;
|
||||
import io.quarkus.runtime.Quarkus;
|
||||
@@ -41,6 +38,7 @@ import org.keycloak.quarkus.runtime.cli.ExecutionExceptionHandler;
|
||||
import org.keycloak.quarkus.runtime.cli.PropertyException;
|
||||
import org.keycloak.quarkus.runtime.cli.Picocli;
|
||||
import org.keycloak.common.Version;
|
||||
import org.keycloak.quarkus.runtime.cli.command.DryRunMixin;
|
||||
import org.keycloak.quarkus.runtime.cli.command.Start;
|
||||
|
||||
import io.quarkus.runtime.QuarkusApplication;
|
||||
@@ -66,47 +64,34 @@ public class KeycloakMain implements QuarkusApplication {
|
||||
ensureForkJoinPoolThreadFactoryHasBeenSetToQuarkus();
|
||||
|
||||
System.setProperty("kc.version", Version.VERSION);
|
||||
|
||||
main(args, new Picocli());
|
||||
}
|
||||
|
||||
public static void main(String[] args, Picocli picocli) {
|
||||
List<String> cliArgs = null;
|
||||
try {
|
||||
cliArgs = Picocli.parseArgs(args);
|
||||
} catch (PropertyException e) {
|
||||
handleUsageError(e.getMessage());
|
||||
picocli.usageException(e.getMessage(), e.getCause());
|
||||
return;
|
||||
}
|
||||
|
||||
if (DryRunMixin.isDryRunBuild() && (cliArgs.contains(DryRunMixin.DRYRUN_OPTION_LONG) || Boolean.valueOf(System.getenv().get(DryRunMixin.KC_DRY_RUN_ENV)))) {
|
||||
PersistedConfigSource.getInstance().useDryRunProperties();
|
||||
}
|
||||
|
||||
if (cliArgs.isEmpty()) {
|
||||
cliArgs = new ArrayList<>(cliArgs);
|
||||
// default to show help message
|
||||
cliArgs.add("-h");
|
||||
} else if (isFastStart(cliArgs)) { // fast path for starting the server without bootstrapping CLI
|
||||
|
||||
Environment.updateProfile(true);
|
||||
if (Environment.isDevProfile()) {
|
||||
handleUsageError(Messages.devProfileNotAllowedError(Start.NAME));
|
||||
return;
|
||||
}
|
||||
|
||||
Environment.setParsedCommand(new Start());
|
||||
|
||||
try {
|
||||
PropertyMappers.sanitizeDisabledMappers();
|
||||
PrintWriter outStream = new PrintWriter(System.out, true);
|
||||
Picocli.validateConfig(cliArgs, new Start(), outStream);
|
||||
} catch (PropertyException | ProfileException e) {
|
||||
handleUsageError(e.getMessage(), e.getCause());
|
||||
return;
|
||||
}
|
||||
|
||||
ExecutionExceptionHandler errorHandler = new ExecutionExceptionHandler();
|
||||
PrintWriter errStream = new PrintWriter(System.err, true);
|
||||
|
||||
start(errorHandler, errStream, args);
|
||||
|
||||
Start.fastStart(picocli, Boolean.valueOf(System.getenv().get(DryRunMixin.KC_DRY_RUN_ENV)));
|
||||
return;
|
||||
}
|
||||
|
||||
// parse arguments and execute any of the configured commands
|
||||
new Picocli().parseAndRun(cliArgs);
|
||||
picocli.parseAndRun(cliArgs);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -128,23 +113,12 @@ public class KeycloakMain implements QuarkusApplication {
|
||||
}
|
||||
}
|
||||
|
||||
private static void handleUsageError(String message) {
|
||||
handleUsageError(message, null);
|
||||
}
|
||||
|
||||
private static void handleUsageError(String message, Throwable cause) {
|
||||
ExecutionExceptionHandler errorHandler = new ExecutionExceptionHandler();
|
||||
PrintWriter errStream = new PrintWriter(System.err, true);
|
||||
errorHandler.error(errStream, message, cause);
|
||||
System.exit(ExitCode.USAGE);
|
||||
}
|
||||
|
||||
private static boolean isFastStart(List<String> cliArgs) {
|
||||
// 'start --optimized' should start the server without parsing CLI
|
||||
return cliArgs.size() == 2 && cliArgs.get(0).equals(Start.NAME) && cliArgs.stream().anyMatch(OPTIMIZED_BUILD_OPTION_LONG::equals);
|
||||
}
|
||||
|
||||
public static void start(ExecutionExceptionHandler errorHandler, PrintWriter errStream, String[] args) {
|
||||
public static void start(ExecutionExceptionHandler errorHandler, PrintWriter errStream) {
|
||||
try {
|
||||
Quarkus.run(KeycloakMain.class, (exitCode, cause) -> {
|
||||
if (cause != null) {
|
||||
@@ -158,7 +132,7 @@ public class KeycloakMain implements QuarkusApplication {
|
||||
// as we are replacing the default exit handler, we need to force exit
|
||||
System.exit(exitCode);
|
||||
}
|
||||
}, args);
|
||||
});
|
||||
} catch (Throwable cause) {
|
||||
errorHandler.error(errStream,
|
||||
String.format("Unexpected error when starting the server in (%s) mode", getKeycloakModeFromProfile(org.keycloak.common.util.Environment.getProfile())),
|
||||
@@ -172,10 +146,6 @@ public class KeycloakMain implements QuarkusApplication {
|
||||
*/
|
||||
@Override
|
||||
public int run(String... args) throws Exception {
|
||||
if (isDevProfile()) {
|
||||
Logger.getLogger(KeycloakMain.class).warnf("Running the server in development mode. DO NOT use this configuration in production.");
|
||||
}
|
||||
|
||||
int exitCode = ApplicationLifecycleManager.getExitCode();
|
||||
|
||||
if (isTestLaunchMode() || isNonServerMode()) {
|
||||
|
||||
@@ -100,6 +100,8 @@ public class Picocli {
|
||||
boolean includeBuildTime;
|
||||
}
|
||||
|
||||
private ExecutionExceptionHandler errorHandler = new ExecutionExceptionHandler();
|
||||
|
||||
public void parseAndRun(List<String> cliArgs) {
|
||||
// perform two passes over the cli args. First without option validation to determine the current command, then with option validation enabled
|
||||
CommandLine cmd = createCommandLine(spec -> spec
|
||||
@@ -127,21 +129,17 @@ public class Picocli {
|
||||
exitCode = runReAugmentationIfNeeded(cliArgs, cmd, currentCommand);
|
||||
} else {
|
||||
PropertyMappers.sanitizeDisabledMappers();
|
||||
exitCode = run(cmd, argArray);
|
||||
exitCode = cmd.execute(argArray);
|
||||
}
|
||||
|
||||
exitOnFailure(exitCode, cmd);
|
||||
exit(exitCode);
|
||||
} catch (ParameterException parEx) {
|
||||
catchParameterException(parEx, cmd, argArray);
|
||||
} catch (ProfileException | PropertyException proEx) {
|
||||
catchProfileException(proEx.getMessage(), proEx.getCause(), cmd);
|
||||
usageException(proEx.getMessage(), proEx.getCause());
|
||||
}
|
||||
}
|
||||
|
||||
protected int run(CommandLine cmd, String[] argArray) {
|
||||
return cmd.execute(argArray);
|
||||
}
|
||||
|
||||
private CommandLine createCommandLineForCommand(List<String> cliArgs, List<CommandLine> commandLineList) {
|
||||
return createCommandLine(spec -> {
|
||||
// use the incoming commandLineList from the initial parsing to determine the current command
|
||||
@@ -184,24 +182,20 @@ public class Picocli {
|
||||
try {
|
||||
exitCode = cmd.getParameterExceptionHandler().handleParseException(parEx, args);
|
||||
} catch (Exception e) {
|
||||
ExecutionExceptionHandler errorHandler = new ExecutionExceptionHandler();
|
||||
errorHandler.error(cmd.getErr(), e.getMessage(), null);
|
||||
exitCode = parEx.getCommandLine().getCommandSpec().exitCodeOnInvalidInput();
|
||||
}
|
||||
exitOnFailure(exitCode, cmd);
|
||||
exit(exitCode);
|
||||
}
|
||||
|
||||
private void catchProfileException(String message, Throwable cause, CommandLine cmd) {
|
||||
ExecutionExceptionHandler errorHandler = new ExecutionExceptionHandler();
|
||||
errorHandler.error(cmd.getErr(), message, cause);
|
||||
exitOnFailure(CommandLine.ExitCode.USAGE, cmd);
|
||||
public void usageException(String message, Throwable cause) {
|
||||
errorHandler.error(getErrWriter(), message, cause);
|
||||
exit(CommandLine.ExitCode.USAGE);
|
||||
}
|
||||
|
||||
protected void exitOnFailure(int exitCode, CommandLine cmd) {
|
||||
if (exitCode != cmd.getCommandSpec().exitCodeOnSuccess() && !Environment.isTestLaunchMode() || isRebuildCheck()) {
|
||||
// hard exit wanted, as build failed and no subsequent command should be executed. no quarkus involved.
|
||||
System.exit(exitCode);
|
||||
}
|
||||
public void exit(int exitCode) {
|
||||
// hard exit wanted, as build failed and no subsequent command should be executed. no quarkus involved.
|
||||
System.exit(exitCode);
|
||||
}
|
||||
|
||||
private int runReAugmentationIfNeeded(List<String> cliArgs, CommandLine cmd, CommandLine currentCommand) {
|
||||
@@ -328,9 +322,8 @@ public class Picocli {
|
||||
*
|
||||
* @param cliArgs
|
||||
* @param abstractCommand
|
||||
* @param outWriter
|
||||
*/
|
||||
public static void validateConfig(List<String> cliArgs, AbstractCommand abstractCommand, PrintWriter outWriter) {
|
||||
public void validateConfig(List<String> cliArgs, AbstractCommand abstractCommand) {
|
||||
if (cliArgs.contains(OPTIMIZED_BUILD_OPTION_LONG) && !wasBuildEverRun()) {
|
||||
throw new PropertyException(Messages.optimizedUsedForFirstStartup());
|
||||
}
|
||||
@@ -390,7 +383,7 @@ public class Picocli {
|
||||
// only check build-time for a rebuild, we'll check the runtime later
|
||||
if (!mapper.isRunTime() || !isRebuild()) {
|
||||
if (PropertyMapper.isCliOption(configValue)) {
|
||||
throw new KcUnmatchedArgumentException(abstractCommand.getCommandLine(), List.of(mapper.getCliFormat()));
|
||||
throw new KcUnmatchedArgumentException(abstractCommand.getCommandLine().orElseThrow(), List.of(mapper.getCliFormat()));
|
||||
} else {
|
||||
handleDisabled(mapper.isRunTime() ? disabledRunTime : disabledBuildTime, mapper);
|
||||
}
|
||||
@@ -426,17 +419,17 @@ public class Picocli {
|
||||
String.join(", ", ignoredBuildTime)));
|
||||
} else if (!ignoredRunTime.isEmpty()) {
|
||||
warn(format("The following run time options were found, but will be ignored during build time: %s\n",
|
||||
String.join(", ", ignoredRunTime)), outWriter);
|
||||
String.join(", ", ignoredRunTime)), getOutWriter());
|
||||
}
|
||||
|
||||
if (!disabledBuildTime.isEmpty()) {
|
||||
outputDisabledProperties(disabledBuildTime, true, outWriter);
|
||||
outputDisabledProperties(disabledBuildTime, true, getOutWriter());
|
||||
} else if (!disabledRunTime.isEmpty()) {
|
||||
outputDisabledProperties(disabledRunTime, false, outWriter);
|
||||
outputDisabledProperties(disabledRunTime, false, getOutWriter());
|
||||
}
|
||||
|
||||
if (!deprecatedInUse.isEmpty()) {
|
||||
warn("The following used options or option values are DEPRECATED and will be removed or their behaviour changed in a future release:\n" + String.join("\n", deprecatedInUse) + "\nConsult the Release Notes for details.", outWriter);
|
||||
warn("The following used options or option values are DEPRECATED and will be removed or their behaviour changed in a future release:\n" + String.join("\n", deprecatedInUse) + "\nConsult the Release Notes for details.", getOutWriter());
|
||||
}
|
||||
} finally {
|
||||
DisabledMappersInterceptor.enable(disabledMappersInterceptorEnabled);
|
||||
@@ -563,7 +556,7 @@ public class Picocli {
|
||||
return;
|
||||
}
|
||||
} else if (name.startsWith(MicroProfileConfigProvider.NS_QUARKUS)) {
|
||||
// TODO: this is not correct - we are including runtime properties here, but at least they
|
||||
// TODO: this is not correct - we are including runtime properties here, but at least they
|
||||
// are already coming from a file
|
||||
quarkus = true;
|
||||
} else if (!PropertyMappers.isSpiBuildTimeProperty(name)) {
|
||||
@@ -618,7 +611,7 @@ public class Picocli {
|
||||
|
||||
CommandLine cmd = new CommandLine(spec);
|
||||
|
||||
cmd.setExecutionExceptionHandler(new ExecutionExceptionHandler());
|
||||
cmd.setExecutionExceptionHandler(this.errorHandler);
|
||||
cmd.setParameterExceptionHandler(new ShortErrorMessageHandler());
|
||||
cmd.setHelpFactory(new HelpFactory());
|
||||
cmd.getHelpSectionMap().put(SECTION_KEY_COMMAND_LIST, new SubCommandListRenderer());
|
||||
@@ -627,11 +620,11 @@ public class Picocli {
|
||||
return cmd;
|
||||
}
|
||||
|
||||
protected PrintWriter getErrWriter() {
|
||||
public PrintWriter getErrWriter() {
|
||||
return new PrintWriter(System.err, true);
|
||||
}
|
||||
|
||||
protected PrintWriter getOutWriter() {
|
||||
public PrintWriter getOutWriter() {
|
||||
return new PrintWriter(System.out, true);
|
||||
}
|
||||
|
||||
@@ -909,8 +902,8 @@ public class Picocli {
|
||||
}
|
||||
}
|
||||
|
||||
public void start(CommandLine cmd) {
|
||||
KeycloakMain.start((ExecutionExceptionHandler) cmd.getExecutionExceptionHandler(), cmd.getErr(), cmd.getParseResult().originalArgs().toArray(new String[0]));
|
||||
public void start() {
|
||||
KeycloakMain.start(errorHandler, getErrWriter());
|
||||
}
|
||||
|
||||
public void build() throws Throwable {
|
||||
|
||||
@@ -19,6 +19,10 @@ package org.keycloak.quarkus.runtime.cli.command;
|
||||
|
||||
import static org.keycloak.quarkus.runtime.Messages.cliExecutionError;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.keycloak.config.OptionCategory;
|
||||
import org.keycloak.quarkus.runtime.cli.Picocli;
|
||||
import org.keycloak.quarkus.runtime.configuration.ConfigArgsConfigSource;
|
||||
@@ -27,13 +31,10 @@ import picocli.CommandLine;
|
||||
import picocli.CommandLine.Model.CommandSpec;
|
||||
import picocli.CommandLine.Spec;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class AbstractCommand {
|
||||
|
||||
@Spec
|
||||
protected CommandSpec spec;
|
||||
protected CommandSpec spec; // will be null for "start --optimized"
|
||||
protected Picocli picocli;
|
||||
|
||||
protected void executionError(CommandLine cmd, String message) {
|
||||
@@ -66,15 +67,15 @@ public abstract class AbstractCommand {
|
||||
}
|
||||
|
||||
protected void validateConfig() {
|
||||
Picocli.validateConfig(ConfigArgsConfigSource.getAllCliArgs(), this, spec.commandLine().getOut());
|
||||
picocli.validateConfig(ConfigArgsConfigSource.getAllCliArgs(), this);
|
||||
}
|
||||
|
||||
public abstract String getName();
|
||||
|
||||
public CommandLine getCommandLine() {
|
||||
return spec.commandLine();
|
||||
public Optional<CommandLine> getCommandLine() {
|
||||
return Optional.ofNullable(spec).map(CommandSpec::commandLine);
|
||||
}
|
||||
|
||||
|
||||
public void setPicocli(Picocli picocli) {
|
||||
this.picocli = picocli;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
package org.keycloak.quarkus.runtime.cli.command;
|
||||
|
||||
import org.keycloak.config.OptionCategory;
|
||||
import org.keycloak.quarkus.runtime.Environment;
|
||||
import org.keycloak.quarkus.runtime.configuration.mappers.HostnameV2PropertyMappers;
|
||||
import org.keycloak.quarkus.runtime.configuration.mappers.HttpPropertyMappers;
|
||||
|
||||
@@ -27,32 +26,36 @@ import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import picocli.CommandLine;
|
||||
import picocli.CommandLine.Help.Ansi;
|
||||
|
||||
import static org.keycloak.quarkus.runtime.configuration.Configuration.getRawPersistedProperties;
|
||||
import static org.keycloak.quarkus.runtime.Environment.isDevProfile;
|
||||
|
||||
public abstract class AbstractStartCommand extends AbstractCommand implements Runnable {
|
||||
public static final String OPTIMIZED_BUILD_OPTION_LONG = "--optimized";
|
||||
|
||||
|
||||
@CommandLine.Mixin
|
||||
DryRunMixin dryRunMixin = new DryRunMixin();
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Environment.setParsedCommand(this);
|
||||
doBeforeRun();
|
||||
CommandLine cmd = spec.commandLine();
|
||||
HttpPropertyMappers.validateConfig();
|
||||
HostnameV2PropertyMappers.validateConfig();
|
||||
validateConfig();
|
||||
|
||||
picocli.start(cmd);
|
||||
if (isDevProfile()) {
|
||||
picocli.getOutWriter().println(Ansi.AUTO.string(
|
||||
"@|bold,red Running the server in development mode. DO NOT use this configuration in production.|@"));
|
||||
}
|
||||
if (!Boolean.TRUE.equals(dryRunMixin.dryRun)) {
|
||||
picocli.start();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected void doBeforeRun() {
|
||||
|
||||
}
|
||||
|
||||
public static boolean wasBuildEverRun() {
|
||||
return !getRawPersistedProperties().isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<OptionCategory> getOptionCategories() {
|
||||
EnumSet<OptionCategory> excludedCategories = excludedCategories();
|
||||
@@ -62,5 +65,5 @@ public abstract class AbstractStartCommand extends AbstractCommand implements Ru
|
||||
protected EnumSet<OptionCategory> excludedCategories() {
|
||||
return EnumSet.of(OptionCategory.IMPORT, OptionCategory.EXPORT);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import org.keycloak.config.OptionCategory;
|
||||
import org.keycloak.quarkus.runtime.Environment;
|
||||
import org.keycloak.quarkus.runtime.Messages;
|
||||
import org.keycloak.quarkus.runtime.configuration.Configuration;
|
||||
import org.keycloak.quarkus.runtime.configuration.PersistedConfigSource;
|
||||
|
||||
import io.quarkus.bootstrap.runner.RunnerClassLoader;
|
||||
|
||||
@@ -65,6 +66,9 @@ public final class Build extends AbstractCommand implements Runnable {
|
||||
@CommandLine.Mixin
|
||||
HelpAllMixin helpAllMixin;
|
||||
|
||||
@CommandLine.Mixin
|
||||
DryRunMixin dryRunMixin;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (org.keycloak.common.util.Environment.getProfile() == null) {
|
||||
@@ -81,7 +85,11 @@ public final class Build extends AbstractCommand implements Runnable {
|
||||
configureBuildClassLoader();
|
||||
|
||||
beforeReaugmentationOnWindows();
|
||||
picocli.build();
|
||||
if (!Boolean.TRUE.equals(dryRunMixin.dryRun)) {
|
||||
picocli.build();
|
||||
} else if (DryRunMixin.isDryRunBuild()) {
|
||||
PersistedConfigSource.getInstance().saveDryRunProperties();
|
||||
}
|
||||
|
||||
if (!isDevProfile()) {
|
||||
println(spec.commandLine(), "Server configuration updated and persisted. Run the following command to review the configuration:\n");
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2021 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.quarkus.runtime.cli.command;
|
||||
|
||||
import picocli.CommandLine;
|
||||
|
||||
import static org.keycloak.quarkus.runtime.cli.Picocli.NO_PARAM_LABEL;
|
||||
|
||||
public final class DryRunMixin {
|
||||
|
||||
public static final String DRYRUN_OPTION_LONG = "--dry-run";
|
||||
public static final String KC_DRY_RUN_ENV = "KC_DRY_RUN";
|
||||
public static final String KC_DRY_RUN_BUILD_ENV = "KC_DRY_RUN_BUILD";
|
||||
|
||||
@CommandLine.Option(names = {DRYRUN_OPTION_LONG},
|
||||
description = "Use this option to validate the command, but do nothing",
|
||||
paramLabel = NO_PARAM_LABEL,
|
||||
hidden = true,
|
||||
defaultValue = "${env:" + KC_DRY_RUN_ENV + "}")
|
||||
Boolean dryRun;
|
||||
|
||||
public static boolean isDryRunBuild() {
|
||||
return Boolean.valueOf(System.getenv().get(KC_DRY_RUN_BUILD_ENV));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -21,6 +21,10 @@ import static org.keycloak.quarkus.runtime.cli.command.AbstractStartCommand.OPTI
|
||||
|
||||
import org.keycloak.quarkus.runtime.Environment;
|
||||
import org.keycloak.quarkus.runtime.Messages;
|
||||
import org.keycloak.common.profile.ProfileException;
|
||||
import org.keycloak.quarkus.runtime.cli.Picocli;
|
||||
import org.keycloak.quarkus.runtime.cli.PropertyException;
|
||||
import org.keycloak.quarkus.runtime.configuration.mappers.PropertyMappers;
|
||||
|
||||
import picocli.CommandLine;
|
||||
import picocli.CommandLine.Command;
|
||||
@@ -38,7 +42,7 @@ public final class Start extends AbstractStartCommand implements Runnable {
|
||||
public static final String NAME = "start";
|
||||
|
||||
@CommandLine.Mixin
|
||||
OptimizedMixin optimizedMixin;
|
||||
OptimizedMixin optimizedMixin = new OptimizedMixin();
|
||||
|
||||
@CommandLine.Mixin
|
||||
ImportRealmMixin importRealmMixin;
|
||||
@@ -50,7 +54,7 @@ public final class Start extends AbstractStartCommand implements Runnable {
|
||||
protected void doBeforeRun() {
|
||||
Environment.updateProfile(true);
|
||||
if (Environment.isDevProfile()) {
|
||||
executionError(spec.commandLine(), Messages.devProfileNotAllowedError(NAME));
|
||||
throw new PropertyException(Messages.devProfileNotAllowedError(NAME));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,4 +67,18 @@ public final class Start extends AbstractStartCommand implements Runnable {
|
||||
public String getName() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
public static void fastStart(Picocli picocli, boolean dryRun) {
|
||||
try {
|
||||
Start start = new Start();
|
||||
Environment.setParsedCommand(start);
|
||||
PropertyMappers.sanitizeDisabledMappers();
|
||||
start.optimizedMixin.optimized = true;
|
||||
start.dryRunMixin.dryRun = dryRun;
|
||||
start.setPicocli(picocli);
|
||||
start.run();
|
||||
} catch (PropertyException | ProfileException e) {
|
||||
picocli.usageException(e.getMessage(), e.getCause());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,8 +20,11 @@ package org.keycloak.quarkus.runtime.configuration;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
@@ -33,8 +36,10 @@ import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
import io.smallrye.config.ConfigValue;
|
||||
import io.smallrye.config.ConfigValue.ConfigValueBuilder;
|
||||
import io.smallrye.config.PropertiesConfigSource;
|
||||
import org.keycloak.quarkus.runtime.Environment;
|
||||
import org.keycloak.quarkus.runtime.cli.Picocli;
|
||||
|
||||
/**
|
||||
* A {@link org.eclipse.microprofile.config.spi.ConfigSource} based on the configuration properties persisted into the server
|
||||
@@ -170,4 +175,29 @@ public final class PersistedConfigSource extends PropertiesConfigSource {
|
||||
enable();
|
||||
}
|
||||
}
|
||||
|
||||
public void saveDryRunProperties() throws FileNotFoundException, IOException {
|
||||
Path path = Environment.getHomePath().resolve("lib").resolve("dryRun.properties");
|
||||
var properties = Picocli.getNonPersistedBuildTimeOptions();
|
||||
try (FileOutputStream fos = new FileOutputStream(path.toFile())) {
|
||||
properties.store(fos, null);
|
||||
}
|
||||
}
|
||||
|
||||
public void useDryRunProperties() {
|
||||
Path path = Environment.getHomePath().resolve("lib").resolve("dryRun.properties");
|
||||
if (Files.exists(path)) {
|
||||
Properties properties = new Properties();
|
||||
try (FileInputStream fis = new FileInputStream(path.toFile())) {
|
||||
properties.load(fis);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
var config = this.getConfigValueProperties();
|
||||
config.clear();
|
||||
properties.forEach((k, v) -> config.put((String) k,
|
||||
new ConfigValueBuilder().withName((String) k).withValue((String) v).withRawValue((String) v)
|
||||
.withConfigSourceName(this.getName()).withConfigSourceOrdinal(this.getOrdinal()).build()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,7 +122,6 @@ public class TracingPropertyMappers {
|
||||
}
|
||||
|
||||
private static boolean isFeatureEnabled() {
|
||||
Environment.getCurrentOrCreateFeatureProfile();
|
||||
return Profile.isFeatureEnabled(Profile.Feature.OPENTELEMETRY);
|
||||
}
|
||||
|
||||
|
||||
@@ -27,13 +27,13 @@ import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.keycloak.quarkus.runtime.Environment;
|
||||
import org.keycloak.quarkus.runtime.KeycloakMain;
|
||||
import org.keycloak.quarkus.runtime.cli.Picocli;
|
||||
import org.keycloak.quarkus.runtime.configuration.ConfigArgsConfigSource;
|
||||
import org.keycloak.quarkus.runtime.configuration.test.AbstractConfigurationTest;
|
||||
@@ -57,44 +57,38 @@ public class PicocliTest extends AbstractConfigurationTest {
|
||||
String getErrString() {
|
||||
return normalize(err);
|
||||
}
|
||||
|
||||
|
||||
// normalize line endings - TODO: could also normalize non-printable chars
|
||||
// but for now those are part of the expected output
|
||||
String normalize(StringWriter writer) {
|
||||
return System.lineSeparator().equals("\n") ? writer.toString()
|
||||
: writer.toString().replace(System.lineSeparator(), "\n");
|
||||
}
|
||||
|
||||
|
||||
String getOutString() {
|
||||
return normalize(out);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PrintWriter getErrWriter() {
|
||||
public PrintWriter getErrWriter() {
|
||||
return new PrintWriter(err, true);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected PrintWriter getOutWriter() {
|
||||
public PrintWriter getOutWriter() {
|
||||
return new PrintWriter(out, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void exitOnFailure(int exitCode, CommandLine cmd) {
|
||||
public void exit(int exitCode) {
|
||||
this.exitCode = exitCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parseAndRun(List<String> cliArgs) {
|
||||
config = createConfig();
|
||||
super.parseAndRun(cliArgs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(CommandLine cmd) {
|
||||
public void start() {
|
||||
// skip
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void build() throws Throwable {
|
||||
reaug = true;
|
||||
@@ -106,8 +100,9 @@ public class PicocliTest extends AbstractConfigurationTest {
|
||||
NonRunningPicocli pseudoLaunch(String... args) {
|
||||
NonRunningPicocli nonRunningPicocli = new NonRunningPicocli();
|
||||
ConfigArgsConfigSource.setCliArgs(args);
|
||||
var cliArgs = Picocli.parseArgs(args);
|
||||
nonRunningPicocli.parseAndRun(cliArgs);
|
||||
// TODO: this needs refined, otherwise profile handling will not be correct
|
||||
nonRunningPicocli.config = createConfig();
|
||||
KeycloakMain.main(args, nonRunningPicocli);
|
||||
return nonRunningPicocli;
|
||||
}
|
||||
|
||||
@@ -211,22 +206,22 @@ public class PicocliTest extends AbstractConfigurationTest {
|
||||
.errorText("Unknown option: '--db-pasword'")
|
||||
+ "\nPossible solutions: --db-url, --db-url-host, --db-url-database, --db-url-port, --db-url-properties, --db-username, --db-password, --db-schema, --db-pool-initial-size, --db-pool-min-size, --db-pool-max-size, --db-driver, --db"));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void httpStoreTypeValidation() {
|
||||
NonRunningPicocli nonRunningPicocli = pseudoLaunch("start", "--https-key-store-file=not-there.ks", "--hostname-strict=false");
|
||||
assertEquals(CommandLine.ExitCode.USAGE, nonRunningPicocli.exitCode);
|
||||
assertThat(nonRunningPicocli.getErrString(), containsString("Unable to determine 'https-key-store-type' automatically. Adjust the file extension or specify the property"));
|
||||
|
||||
|
||||
nonRunningPicocli = pseudoLaunch("start", "--https-key-store-file=not-there.ks", "--hostname-strict=false", "--https-key-store-type=jdk");
|
||||
assertEquals(CommandLine.ExitCode.USAGE, nonRunningPicocli.exitCode);
|
||||
assertThat(nonRunningPicocli.getErrString(), containsString("Failed to load 'https-key-' material: NoSuchFileException not-there.ks"));
|
||||
|
||||
|
||||
nonRunningPicocli = pseudoLaunch("start", "--https-trust-store-file=not-there.jks", "--https-key-store-file=not-there.ks", "--hostname-strict=false", "--https-key-store-type=jdk");
|
||||
assertEquals(CommandLine.ExitCode.USAGE, nonRunningPicocli.exitCode);
|
||||
assertThat(nonRunningPicocli.getErrString(), containsString("No trust store password provided"));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testShowConfigHidesSystemProperties() {
|
||||
setSystemProperty("kc.something", "password", () -> {
|
||||
@@ -245,42 +240,42 @@ public class PicocliTest extends AbstractConfigurationTest {
|
||||
assertThat(nonRunningPicocli.getErrString(), containsString(
|
||||
"Option: '--db postgres' is not expected to contain whitespace, please remove any unnecessary quoting/escaping"));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void spiRuntimeAllowedWithStart() {
|
||||
NonRunningPicocli nonRunningPicocli = pseudoLaunch("start", "--http-enabled=true", "--spi-something-pass=changeme");
|
||||
assertEquals(CommandLine.ExitCode.OK, nonRunningPicocli.exitCode);
|
||||
assertThat(nonRunningPicocli.getOutString(), not(containsString("kc.spi-something-pass")));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void spiRuntimeWarnWithBuild() {
|
||||
NonRunningPicocli nonRunningPicocli = pseudoLaunch("build", "--spi-something-pass=changeme");
|
||||
assertEquals(CommandLine.ExitCode.OK, nonRunningPicocli.exitCode);
|
||||
assertThat(nonRunningPicocli.getOutString(), containsString("The following run time options were found, but will be ignored during build time: kc.spi-something-pass"));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void failBuildDev() {
|
||||
NonRunningPicocli nonRunningPicocli = pseudoLaunch("--profile=dev", "build");
|
||||
assertThat(nonRunningPicocli.getErrString(), containsString("You can not 'build' the server in development mode."));
|
||||
assertEquals(CommandLine.ExitCode.SOFTWARE, nonRunningPicocli.exitCode);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void failStartBuildDev() {
|
||||
NonRunningPicocli nonRunningPicocli = pseudoLaunch("--profile=dev", "start");
|
||||
assertThat(nonRunningPicocli.getErrString(), containsString("You can not 'start' the server in development mode."));
|
||||
assertEquals(CommandLine.ExitCode.SOFTWARE, nonRunningPicocli.exitCode);
|
||||
assertEquals(CommandLine.ExitCode.USAGE, nonRunningPicocli.exitCode);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void failIfOptimizedUsedForFirstStartupExport() {
|
||||
NonRunningPicocli nonRunningPicocli = pseudoLaunch("export", "--optimized", "--dir=data");
|
||||
assertEquals(CommandLine.ExitCode.USAGE, nonRunningPicocli.exitCode);
|
||||
assertThat(nonRunningPicocli.getErrString(), containsString("The '--optimized' flag was used for first ever server start."));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testReaugFromProdToDev() {
|
||||
build("build");
|
||||
@@ -305,37 +300,37 @@ public class PicocliTest extends AbstractConfigurationTest {
|
||||
onAfter();
|
||||
addPersistedConfigValues((Map)nonRunningPicocli.buildProps);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testReaugFromProdToDevExport() {
|
||||
build("build");
|
||||
|
||||
|
||||
Environment.setRebuildCheck(); // will be reset by the system properties logic
|
||||
NonRunningPicocli nonRunningPicocli = pseudoLaunch("--profile=dev", "export", "--file=file");
|
||||
assertEquals(CommandLine.ExitCode.OK, nonRunningPicocli.exitCode);
|
||||
assertTrue(nonRunningPicocli.reaug);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testNoReaugFromProdToExport() {
|
||||
build("build");
|
||||
|
||||
|
||||
Environment.setRebuildCheck(); // will be reset by the system properties logic
|
||||
NonRunningPicocli nonRunningPicocli = pseudoLaunch("export", "--file=file");
|
||||
assertEquals(CommandLine.ExitCode.OK, nonRunningPicocli.exitCode);
|
||||
assertFalse(nonRunningPicocli.reaug);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testReaugFromDevToProd() {
|
||||
build("start-dev");
|
||||
|
||||
|
||||
Environment.setRebuildCheck(); // will be reset by the system properties logic
|
||||
NonRunningPicocli nonRunningPicocli = pseudoLaunch("start", "--hostname=name", "--http-enabled=true");
|
||||
assertEquals(CommandLine.ExitCode.OK, nonRunningPicocli.exitCode);
|
||||
assertTrue(nonRunningPicocli.reaug);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testNoReaugFromDevToDevExport() {
|
||||
build("start-dev");
|
||||
@@ -345,22 +340,22 @@ public class PicocliTest extends AbstractConfigurationTest {
|
||||
assertEquals(CommandLine.ExitCode.OK, nonRunningPicocli.exitCode);
|
||||
assertFalse(nonRunningPicocli.reaug);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testReaugFromDevToProdExport() {
|
||||
build("start-dev");
|
||||
|
||||
|
||||
Environment.setRebuildCheck(); // will be reset by the system properties logic
|
||||
NonRunningPicocli nonRunningPicocli = pseudoLaunch("export", "--file=file");
|
||||
assertEquals(CommandLine.ExitCode.OK, nonRunningPicocli.exitCode);
|
||||
assertTrue(nonRunningPicocli.reaug);
|
||||
assertEquals("prod", nonRunningPicocli.buildProps.getProperty(org.keycloak.common.util.Environment.PROFILE));;
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testOptimizedReaugmentationMessage() {
|
||||
build("build");
|
||||
|
||||
|
||||
Environment.setRebuildCheck(); // will be reset by the system properties logic
|
||||
NonRunningPicocli nonRunningPicocli = pseudoLaunch("start", "--features=docker", "--hostname=name", "--http-enabled=true");
|
||||
assertEquals(CommandLine.ExitCode.OK, nonRunningPicocli.exitCode);
|
||||
@@ -368,4 +363,15 @@ public class PicocliTest extends AbstractConfigurationTest {
|
||||
assertTrue(nonRunningPicocli.reaug);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fastStartOptimizedSucceeds() {
|
||||
build("build");
|
||||
|
||||
System.setProperty("kc.http-enabled", "true");
|
||||
System.setProperty("kc.hostname-strict", "false");
|
||||
|
||||
NonRunningPicocli nonRunningPicocli = pseudoLaunch("start", "--optimized");
|
||||
assertEquals(Integer.MAX_VALUE, nonRunningPicocli.exitCode); // "running" state
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.common.Profile;
|
||||
import org.keycloak.quarkus.runtime.configuration.ConfigArgsConfigSource;
|
||||
import org.keycloak.quarkus.runtime.configuration.Configuration;
|
||||
import org.keycloak.quarkus.runtime.configuration.KeycloakConfigSourceProvider;
|
||||
@@ -98,7 +99,7 @@ public abstract class AbstractConfigurationTest {
|
||||
System.clearProperty(key);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@AfterClass
|
||||
public static void resetConfigruation() {
|
||||
ConfigurationTest.createConfig(); // onAfter doesn't actually reset the config
|
||||
@@ -124,6 +125,7 @@ public abstract class AbstractConfigurationTest {
|
||||
PropertyMappers.reset();
|
||||
ConfigArgsConfigSource.setCliArgs();
|
||||
PersistedConfigSource.getInstance().getConfigValueProperties().clear();
|
||||
Profile.reset();
|
||||
}
|
||||
|
||||
protected Config.Scope initConfig(String... scope) {
|
||||
@@ -164,7 +166,7 @@ public abstract class AbstractConfigurationTest {
|
||||
protected void assertExternalConfig(Map<String, String> expectedValues) {
|
||||
expectedValues.forEach(this::assertExternalConfig);
|
||||
}
|
||||
|
||||
|
||||
protected static void addPersistedConfigValues(Map<String, String> values) {
|
||||
var configValueProps = PersistedConfigSource.getInstance().getConfigValueProperties();
|
||||
values.forEach((k, v) -> configValueProps.put(k, new ConfigValueBuilder().withName(k).withValue(v).build()));
|
||||
|
||||
@@ -24,6 +24,7 @@ import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestMethodOrder;
|
||||
import org.keycloak.it.junit5.extension.CLIResult;
|
||||
import org.keycloak.it.junit5.extension.DistributionTest;
|
||||
import org.keycloak.it.junit5.extension.DryRun;
|
||||
import org.keycloak.it.junit5.extension.RawDistOnly;
|
||||
import org.keycloak.it.junit5.extension.WithEnvVars;
|
||||
import org.keycloak.it.utils.KeycloakDistribution;
|
||||
@@ -38,6 +39,7 @@ import static org.keycloak.quarkus.runtime.cli.command.AbstractStartCommand.OPTI
|
||||
@TestMethodOrder(OrderAnnotation.class)
|
||||
public class BuildAndStartDistTest {
|
||||
|
||||
@DryRun
|
||||
@Test
|
||||
void testBuildAndStart(KeycloakDistribution dist) {
|
||||
RawKeycloakDistribution rawDist = dist.unwrap(RawKeycloakDistribution.class);
|
||||
@@ -46,7 +48,7 @@ public class BuildAndStartDistTest {
|
||||
cliResult.assertBuild();
|
||||
cliResult = rawDist.run("start", "--http-enabled=true", "--hostname-strict=false", OPTIMIZED_BUILD_OPTION_LONG);
|
||||
cliResult.assertNoBuild();
|
||||
cliResult.assertStarted();
|
||||
assertTrue(cliResult.getErrorOutput().isBlank());
|
||||
|
||||
// start using based on the build options set via conf file
|
||||
rawDist.setProperty("http-enabled", "true");
|
||||
@@ -56,17 +58,17 @@ public class BuildAndStartDistTest {
|
||||
cliResult.assertBuild();
|
||||
cliResult = rawDist.run("start", OPTIMIZED_BUILD_OPTION_LONG);
|
||||
cliResult.assertNoBuild();
|
||||
cliResult.assertStarted();
|
||||
assertTrue(cliResult.getErrorOutput().isBlank());
|
||||
// running start without optimized flag should not cause a build
|
||||
cliResult = rawDist.run("start");
|
||||
cliResult.assertNoBuild();
|
||||
cliResult.assertStarted();
|
||||
assertTrue(cliResult.getErrorOutput().isBlank());
|
||||
|
||||
// remove the build option from conf file to force a build during start
|
||||
rawDist.removeProperty("http-relative-path");
|
||||
cliResult = rawDist.run("start");
|
||||
cliResult.assertBuild();
|
||||
cliResult.assertStarted();
|
||||
assertTrue(cliResult.getErrorOutput().isBlank());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -82,7 +82,7 @@ public class LoggingDistTest {
|
||||
assertFalse(cliResult.getOutput().contains("INFO"));
|
||||
assertFalse(cliResult.getOutput().contains("DEBUG"));
|
||||
assertFalse(cliResult.getOutput().contains("Listening on:"));
|
||||
assertTrue(cliResult.getOutput().contains("WARN [org.keycloak"));
|
||||
assertTrue(cliResult.getOutput().contains("Running the server in development mode."));
|
||||
cliResult.assertStartedDevMode();
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestMethodOrder;
|
||||
import org.keycloak.it.junit5.extension.CLIResult;
|
||||
import org.keycloak.it.junit5.extension.DistributionTest;
|
||||
import org.keycloak.it.junit5.extension.DryRun;
|
||||
import org.keycloak.it.junit5.extension.RawDistOnly;
|
||||
import org.keycloak.it.junit5.extension.WithEnvVars;
|
||||
import org.keycloak.it.utils.KeycloakDistribution;
|
||||
@@ -40,6 +41,7 @@ import static org.keycloak.quarkus.runtime.cli.command.Main.CONFIG_FILE_LONG_NAM
|
||||
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||
public class OptionsDistTest {
|
||||
|
||||
@DryRun
|
||||
@Test
|
||||
@Order(1)
|
||||
@Launch({"build", "--db=invalid"})
|
||||
@@ -47,6 +49,7 @@ public class OptionsDistTest {
|
||||
Assertions.assertTrue(result.getErrorOutput().contains("Invalid value for option '--db': invalid. Expected values are: dev-file, dev-mem, mariadb, mssql, mysql, oracle, postgres"));
|
||||
}
|
||||
|
||||
@DryRun
|
||||
@Test
|
||||
@Order(2)
|
||||
@Launch({"start", "--test=invalid"})
|
||||
@@ -54,6 +57,7 @@ public class OptionsDistTest {
|
||||
assertEquals(1, result.getErrorStream().stream().filter(s -> s.contains("Unknown option: '--test'")).count());
|
||||
}
|
||||
|
||||
@DryRun
|
||||
@Test
|
||||
@Order(3)
|
||||
@Launch({"start", "--log=console", "--log-file-output=json", "--http-enabled=true", "--hostname-strict=false"})
|
||||
@@ -62,6 +66,7 @@ public class OptionsDistTest {
|
||||
assertEquals(1, result.getErrorStream().stream().filter(s -> s.contains("Possible solutions: --log, --log-console-output, --log-console-level, --log-console-format, --log-console-color, --log-level")).count());
|
||||
}
|
||||
|
||||
@DryRun
|
||||
@Test
|
||||
@Order(4)
|
||||
@Launch({"start", "--log=file", "--log-file-output=json", "--http-enabled=true", "--hostname-strict=false"})
|
||||
@@ -82,6 +87,7 @@ public class OptionsDistTest {
|
||||
cliResult.assertMessage("config property is deprecated and should not be used anymore");
|
||||
}
|
||||
|
||||
@DryRun
|
||||
@Test
|
||||
@Order(6)
|
||||
@RawDistOnly(reason = "Raw is enough and we avoid issues with including custom conf file in the container")
|
||||
@@ -106,6 +112,7 @@ public class OptionsDistTest {
|
||||
|
||||
// Start-dev should be executed as last tests - build is done for development mode
|
||||
|
||||
@DryRun
|
||||
@Test
|
||||
@Order(8)
|
||||
@Launch({"start-dev", "--test=invalid"})
|
||||
@@ -113,6 +120,7 @@ public class OptionsDistTest {
|
||||
assertEquals(1, result.getErrorStream().stream().filter(s -> s.contains("Unknown option: '--test'")).count());
|
||||
}
|
||||
|
||||
@DryRun
|
||||
@Test
|
||||
@Order(9)
|
||||
@Launch({"start-dev", "--log=console", "--log-file-output=json"})
|
||||
@@ -121,6 +129,7 @@ public class OptionsDistTest {
|
||||
assertEquals(1, result.getErrorStream().stream().filter(s -> s.contains("Possible solutions: --log, --log-console-output, --log-console-level, --log-console-format, --log-console-color, --log-level")).count());
|
||||
}
|
||||
|
||||
@DryRun
|
||||
@Test
|
||||
@Order(10)
|
||||
@Launch({"start-dev", "--log=file", "--log-file-output=json", "--log-console-color=true"})
|
||||
@@ -130,6 +139,7 @@ public class OptionsDistTest {
|
||||
assertEquals(1, result.getErrorStream().stream().filter(s -> s.contains("Possible solutions: --log, --log-file, --log-file-level, --log-file-format, --log-file-output, --log-level")).count());
|
||||
}
|
||||
|
||||
@DryRun
|
||||
@Test
|
||||
@Order(10)
|
||||
@Launch({"start-dev", "--cache-remote-host=localhost"})
|
||||
@@ -137,6 +147,7 @@ public class OptionsDistTest {
|
||||
assertErrorStreamContains(result, "cache-remote-host available only when feature 'multi-site', 'clusterless' or 'cache-embedded-remote-store' is set");
|
||||
}
|
||||
|
||||
@DryRun
|
||||
@Test
|
||||
@Order(11)
|
||||
@Launch({"start-dev", "--cache-remote-port=11222"})
|
||||
@@ -144,6 +155,7 @@ public class OptionsDistTest {
|
||||
assertDisabledDueToMissingRemoteHost(result, "--cache-remote-port");
|
||||
}
|
||||
|
||||
@DryRun
|
||||
@Test
|
||||
@Order(12)
|
||||
@Launch({"start-dev", "--cache-remote-username=user"})
|
||||
@@ -151,6 +163,7 @@ public class OptionsDistTest {
|
||||
assertDisabledDueToMissingRemoteHost(result, "--cache-remote-username");
|
||||
}
|
||||
|
||||
@DryRun
|
||||
@Test
|
||||
@Order(13)
|
||||
@Launch({"start-dev", "--cache-remote-password=pass"})
|
||||
@@ -158,6 +171,7 @@ public class OptionsDistTest {
|
||||
assertDisabledDueToMissingRemoteHost(result, "--cache-remote-password");
|
||||
}
|
||||
|
||||
@DryRun
|
||||
@Test
|
||||
@Order(14)
|
||||
@Launch({"start-dev", "--cache-remote-tls-enabled=false"})
|
||||
@@ -165,6 +179,7 @@ public class OptionsDistTest {
|
||||
assertDisabledDueToMissingRemoteHost(result, "--cache-remote-tls-enabled");
|
||||
}
|
||||
|
||||
@DryRun
|
||||
@Test
|
||||
@Order(15)
|
||||
@Launch({"start-dev", "--features=multi-site"})
|
||||
@@ -172,6 +187,7 @@ public class OptionsDistTest {
|
||||
assertErrorStreamContains(result, "- cache-remote-host: Required when feature 'multi-site' or 'clusterless' is set.");
|
||||
}
|
||||
|
||||
@DryRun
|
||||
@Test
|
||||
@Order(16)
|
||||
@Launch({"start-dev", "--features=multi-site", "--cache-remote-host=localhost", "--cache-remote-username=user"})
|
||||
@@ -179,6 +195,7 @@ public class OptionsDistTest {
|
||||
assertErrorStreamContains(result, "The option 'cache-remote-password' is required when 'cache-remote-username' is set.");
|
||||
}
|
||||
|
||||
@DryRun
|
||||
@Test
|
||||
@Order(17)
|
||||
@Launch({"start-dev", "--features=multi-site", "--cache-remote-host=localhost", "--cache-remote-password=secret"})
|
||||
|
||||
@@ -30,6 +30,7 @@ import org.junit.jupiter.api.condition.OS;
|
||||
import org.keycloak.it.junit5.extension.BeforeStartDistribution;
|
||||
import org.keycloak.it.junit5.extension.CLIResult;
|
||||
import org.keycloak.it.junit5.extension.DistributionTest;
|
||||
import org.keycloak.it.junit5.extension.DryRun;
|
||||
import org.keycloak.it.junit5.extension.KeepServerAlive;
|
||||
import org.keycloak.it.junit5.extension.RawDistOnly;
|
||||
import org.keycloak.it.utils.KeycloakDistribution;
|
||||
@@ -52,6 +53,7 @@ public class QuarkusPropertiesDistTest {
|
||||
private static final String QUARKUS_BUILDTIME_HIBERNATE_METRICS_KEY = "quarkus.datasource.metrics.enabled";
|
||||
private static final String QUARKUS_RUNTIME_CONSOLE_HANDLER_ENABLED_KEY = "quarkus.log.handler.console.\"console-2\".enable";
|
||||
|
||||
@DryRun
|
||||
@Test
|
||||
@Launch({"build"})
|
||||
@Order(1)
|
||||
@@ -86,6 +88,7 @@ public class QuarkusPropertiesDistTest {
|
||||
cliResult.assertBuild();
|
||||
}
|
||||
|
||||
@DryRun
|
||||
@Test
|
||||
@BeforeStartDistribution(UpdateConsoleHandlerFromKeycloakConf.class)
|
||||
@Launch({"build"})
|
||||
@@ -96,6 +99,7 @@ public class QuarkusPropertiesDistTest {
|
||||
cliResult.assertBuild();
|
||||
}
|
||||
|
||||
@DryRun
|
||||
@Test
|
||||
@BeforeStartDistribution(UpdateConsoleHandlerFromQuarkusProps.class)
|
||||
@Launch({"start", "--http-enabled=true", "--hostname-strict=false"})
|
||||
|
||||
@@ -6,6 +6,7 @@ import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.keycloak.it.junit5.extension.CLIResult;
|
||||
import org.keycloak.it.junit5.extension.DistributionTest;
|
||||
import org.keycloak.it.junit5.extension.DryRun;
|
||||
import org.keycloak.it.junit5.extension.RawDistOnly;
|
||||
import org.keycloak.it.junit5.extension.WithEnvVars;
|
||||
import org.keycloak.it.utils.KeycloakDistribution;
|
||||
@@ -22,6 +23,7 @@ import static org.keycloak.quarkus.runtime.cli.command.Main.CONFIG_FILE_LONG_NAM
|
||||
@DistributionTest
|
||||
public class ShowConfigCommandDistTest {
|
||||
|
||||
@DryRun
|
||||
@Test
|
||||
@RawDistOnly(reason = "Containers are immutable")
|
||||
void testShowConfigPicksUpRightConfigDependingOnCurrentMode(KeycloakDistribution distribution) {
|
||||
|
||||
@@ -25,6 +25,7 @@ import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestMethodOrder;
|
||||
import org.keycloak.it.junit5.extension.CLIResult;
|
||||
import org.keycloak.it.junit5.extension.DistributionTest;
|
||||
import org.keycloak.it.junit5.extension.DryRun;
|
||||
import org.keycloak.it.junit5.extension.RawDistOnly;
|
||||
import org.keycloak.it.junit5.extension.TestProvider;
|
||||
import org.keycloak.it.utils.KeycloakDistribution;
|
||||
@@ -40,6 +41,7 @@ import static org.keycloak.quarkus.runtime.cli.command.AbstractStartCommand.OPTI
|
||||
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||
public class StartAutoBuildDistTest {
|
||||
|
||||
@DryRun
|
||||
@Test
|
||||
@Launch({ "--verbose", "start", "--http-enabled=true", "--hostname-strict=false" })
|
||||
@Order(1)
|
||||
@@ -52,36 +54,40 @@ public class StartAutoBuildDistTest {
|
||||
cliResult.assertMessage("Next time you run the server, just run:");
|
||||
cliResult.assertMessage(KeycloakDistribution.SCRIPT_CMD + " --verbose start --http-enabled=true --hostname-strict=false " + OPTIMIZED_BUILD_OPTION_LONG);
|
||||
assertFalse(cliResult.getOutput().contains("--cache"));
|
||||
cliResult.assertStarted();
|
||||
assertTrue(cliResult.getErrorOutput().isBlank());
|
||||
}
|
||||
|
||||
@DryRun
|
||||
@Test
|
||||
@Launch({ "start", "--http-enabled=true", "--hostname-strict=false" })
|
||||
@Order(2)
|
||||
void testShouldNotReAugIfConfigIsSame(LaunchResult result) {
|
||||
CLIResult cliResult = (CLIResult) result;
|
||||
cliResult.assertNoBuild();
|
||||
cliResult.assertStarted();
|
||||
assertTrue(cliResult.getErrorOutput().isBlank());
|
||||
}
|
||||
|
||||
@DryRun
|
||||
@Test
|
||||
@Launch({ "start", "--db=dev-mem", "--http-enabled=true", "--hostname-strict=false" })
|
||||
@Order(3)
|
||||
void testShouldReAugIfConfigChanged(LaunchResult result) {
|
||||
CLIResult cliResult = (CLIResult) result;
|
||||
cliResult.assertBuild();
|
||||
cliResult.assertStarted();
|
||||
assertTrue(cliResult.getErrorOutput().isBlank());
|
||||
}
|
||||
|
||||
@DryRun
|
||||
@Test
|
||||
@Launch({ "start", "--db=dev-mem", "--http-enabled=true", "--hostname-strict=false" })
|
||||
@Order(4)
|
||||
void testShouldNotReAugIfSameDatabase(LaunchResult result) {
|
||||
CLIResult cliResult = (CLIResult) result;
|
||||
cliResult.assertNoBuild();
|
||||
cliResult.assertStarted();
|
||||
assertTrue(cliResult.getErrorOutput().isBlank());
|
||||
}
|
||||
|
||||
@DryRun
|
||||
@Test
|
||||
@Launch({ "build", "--db=postgres" })
|
||||
@Order(5)
|
||||
@@ -90,15 +96,17 @@ public class StartAutoBuildDistTest {
|
||||
cliResult.assertBuild();
|
||||
}
|
||||
|
||||
@DryRun
|
||||
@Test
|
||||
@Launch({ "start", "--http-enabled=true", "--hostname-strict=false" })
|
||||
@Order(6)
|
||||
void testReAugWhenNoOptionAfterBuild(LaunchResult result) {
|
||||
CLIResult cliResult = (CLIResult) result;
|
||||
cliResult.assertBuild();
|
||||
cliResult.assertStarted();
|
||||
assertTrue(cliResult.getErrorOutput().isBlank());
|
||||
}
|
||||
|
||||
@DryRun
|
||||
@Test
|
||||
@Launch({ "start", "--db=postgres", "--http-enabled=true", "--hostname-strict=false" })
|
||||
@Order(7)
|
||||
@@ -107,6 +115,7 @@ public class StartAutoBuildDistTest {
|
||||
cliResult.assertBuild();
|
||||
}
|
||||
|
||||
@DryRun
|
||||
@Test
|
||||
@Launch({ "start", "--db=dev-file", "--http-enabled=true", "--hostname-strict=false", OPTIMIZED_BUILD_OPTION_LONG})
|
||||
@Order(8)
|
||||
@@ -115,6 +124,7 @@ public class StartAutoBuildDistTest {
|
||||
cliResult.assertNoBuild();
|
||||
}
|
||||
|
||||
@DryRun
|
||||
@Test
|
||||
@Launch({ "start-dev" })
|
||||
@Order(8)
|
||||
@@ -124,6 +134,7 @@ public class StartAutoBuildDistTest {
|
||||
cliResult.assertStartedDevMode();
|
||||
}
|
||||
|
||||
@DryRun
|
||||
@Test
|
||||
@Launch({ "start-dev" })
|
||||
@Order(9)
|
||||
@@ -132,7 +143,8 @@ public class StartAutoBuildDistTest {
|
||||
assertFalse(cliResult.getOutput().contains("Updating the configuration and installing your custom providers, if any. Please wait."));
|
||||
cliResult.assertStartedDevMode();
|
||||
}
|
||||
|
||||
|
||||
@DryRun
|
||||
@Test
|
||||
@TestProvider(CustomUserProvider.class)
|
||||
@Order(10)
|
||||
@@ -141,11 +153,11 @@ public class StartAutoBuildDistTest {
|
||||
cliResult.assertMessage("Updating the configuration");
|
||||
cliResult.assertStartedDevMode();
|
||||
dist.stop();
|
||||
|
||||
|
||||
// we should persist the spi provider and know not to rebuild
|
||||
cliResult = dist.run("start-dev", "--spi-user-provider=custom_jpa", "--spi-user-jpa-enabled=false");
|
||||
cliResult.assertNoMessage("Updating the configuration");
|
||||
cliResult.assertStartedDevMode();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -31,22 +31,20 @@ import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestMethodOrder;
|
||||
import org.keycloak.it.junit5.extension.CLIResult;
|
||||
import org.keycloak.it.junit5.extension.DistributionTest;
|
||||
import org.keycloak.it.junit5.extension.DryRun;
|
||||
|
||||
import io.quarkus.test.junit.main.Launch;
|
||||
import io.quarkus.test.junit.main.LaunchResult;
|
||||
import org.keycloak.it.junit5.extension.RawDistOnly;
|
||||
import org.keycloak.it.junit5.extension.WithEnvVars;
|
||||
import org.keycloak.it.utils.KeycloakDistribution;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@WithEnvVars({"KC_CACHE", "local"}) // avoid flakey port conflicts
|
||||
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||
@DistributionTest
|
||||
public class StartCommandDistTest {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(StartCommandDistTest.class);
|
||||
|
||||
@DryRun
|
||||
@Test
|
||||
@Launch({ "start", "--hostname-strict=false" })
|
||||
void failNoTls(LaunchResult result) {
|
||||
@@ -54,6 +52,7 @@ public class StartCommandDistTest {
|
||||
() -> "The Output:\n" + result.getErrorOutput() + "doesn't contains the expected string.");
|
||||
}
|
||||
|
||||
@DryRun
|
||||
@Test
|
||||
@Launch({ "start", "--spi-events-listener-jboss-logging-success-level" })
|
||||
void failSpiArgMissingValue(LaunchResult result) {
|
||||
@@ -61,6 +60,7 @@ public class StartCommandDistTest {
|
||||
() -> "The Output:\n" + result.getErrorOutput() + "doesn't contains the expected string.");
|
||||
}
|
||||
|
||||
@DryRun
|
||||
@Test
|
||||
@Launch({ "build", "--spi-events-listener-jboss-logging-success-level=debug" })
|
||||
void warnSpiRuntimeAtBuildtime(LaunchResult result) {
|
||||
@@ -68,6 +68,7 @@ public class StartCommandDistTest {
|
||||
() -> "The Output:\n" + result.getOutput() + "doesn't contains the expected string.");
|
||||
}
|
||||
|
||||
@DryRun
|
||||
@Test
|
||||
@RawDistOnly(reason = "Containers are immutable")
|
||||
void errorSpiBuildtimeAtRuntime(KeycloakDistribution dist) {
|
||||
@@ -78,6 +79,7 @@ public class StartCommandDistTest {
|
||||
result.assertError("The following build time options have values that differ from what is persisted - the new values will NOT be used until another build is run: kc.spi-events-listener-jboss-logging-enabled");
|
||||
}
|
||||
|
||||
@DryRun
|
||||
@WithEnvVars({"KC_SPI_EVENTS_LISTENER_JBOSS_LOGGING_ENABLED", "false"})
|
||||
@Test
|
||||
@RawDistOnly(reason = "Containers are immutable")
|
||||
@@ -86,9 +88,10 @@ public class StartCommandDistTest {
|
||||
result.assertBuild();
|
||||
|
||||
result = dist.run("start", "--optimized", "--http-enabled=true", "--hostname-strict=false");
|
||||
result.assertStarted();
|
||||
result.assertNoError("The following build time options");
|
||||
}
|
||||
|
||||
@DryRun
|
||||
@Test
|
||||
@Launch({ "--profile=dev", "start" })
|
||||
void failUsingDevProfile(LaunchResult result) {
|
||||
@@ -103,6 +106,7 @@ public class StartCommandDistTest {
|
||||
cliResult.assertStarted();
|
||||
}
|
||||
|
||||
@DryRun
|
||||
@Test
|
||||
@Launch({ "-v", "start", "--db=dev-mem", OPTIMIZED_BUILD_OPTION_LONG})
|
||||
void failBuildPropertyNotAvailable(LaunchResult result) {
|
||||
@@ -110,6 +114,7 @@ public class StartCommandDistTest {
|
||||
cliResult.assertError("Build time option: '--db' not usable with pre-built image and --optimized");
|
||||
}
|
||||
|
||||
@DryRun
|
||||
@Test
|
||||
@Launch({ "--profile=dev", "start", "--http-enabled=true", "--hostname-strict=false" })
|
||||
void failIfAutoBuildUsingDevProfile(LaunchResult result) {
|
||||
@@ -118,6 +123,8 @@ public class StartCommandDistTest {
|
||||
assertEquals(4, cliResult.getErrorStream().size());
|
||||
}
|
||||
|
||||
@DryRun
|
||||
@WithEnvVars({"KC_HTTP_ENABLED", "true", "KC_HOSTNAME_STRICT", "false"})
|
||||
@Test
|
||||
@Launch({ "start", "--optimized" })
|
||||
@Order(1)
|
||||
@@ -126,6 +133,7 @@ public class StartCommandDistTest {
|
||||
cliResult.assertError("The '--optimized' flag was used for first ever server start.");
|
||||
}
|
||||
|
||||
@DryRun
|
||||
@Test
|
||||
@Launch({ "start", "--optimized", "--http-enabled=true", "--hostname-strict=false" })
|
||||
@Order(2)
|
||||
@@ -141,6 +149,7 @@ public class StartCommandDistTest {
|
||||
() -> "The Output:\n" + result.getOutput() + "doesn't contains the expected string.");
|
||||
}
|
||||
|
||||
@DryRun
|
||||
@Test
|
||||
@Launch({ "start", "--http-enabled=true", "--hostname-strict=false", "--metrics-enabled=true" })
|
||||
void testStartUsingAutoBuild(LaunchResult result) {
|
||||
@@ -153,9 +162,10 @@ public class StartCommandDistTest {
|
||||
cliResult.assertMessage("Next time you run the server, just run:");
|
||||
cliResult.assertMessage(KeycloakDistribution.SCRIPT_CMD + " start --http-enabled=true --hostname-strict=false " + OPTIMIZED_BUILD_OPTION_LONG);
|
||||
assertFalse(cliResult.getOutput().contains("--metrics-enabled"));
|
||||
cliResult.assertStarted();
|
||||
assertTrue(cliResult.getErrorOutput().isBlank(), cliResult.getErrorOutput());
|
||||
}
|
||||
|
||||
@DryRun
|
||||
@Test
|
||||
@Launch({ "start", "--optimized", "--http-enabled=true", "--hostname-strict=false", "--db=postgres" })
|
||||
void testStartUsingOptimizedDoesNotAllowBuildOptions(LaunchResult result) {
|
||||
@@ -163,6 +173,7 @@ public class StartCommandDistTest {
|
||||
cliResult.assertError("Build time option: '--db' not usable with pre-built image and --optimized");
|
||||
}
|
||||
|
||||
@DryRun
|
||||
@Test
|
||||
@Launch({ "start", "--http-enabled=true", "--cache-remote-host=localhost", "--hostname-strict=false", "--cache-remote-tls-enabled=false", "--transaction-xa-enabled=true" })
|
||||
void testStartNoWarningOnDisabledRuntimeOption(LaunchResult result) {
|
||||
@@ -170,14 +181,16 @@ public class StartCommandDistTest {
|
||||
cliResult.assertNoMessage("cache-remote-tls-enabled: Available only when remote host is set");
|
||||
}
|
||||
|
||||
@DryRun
|
||||
@Test
|
||||
@WithEnvVars({"KC_LOG", "invalid"})
|
||||
@Launch({ "start" })
|
||||
@Launch({ "start", "--http-enabled=false", "--hostname-strict=false" })
|
||||
void testStartUsingOptimizedInvalidEnvOption(LaunchResult result) {
|
||||
CLIResult cliResult = (CLIResult) result;
|
||||
cliResult.assertError("Invalid value for option 'KC_LOG': invalid. Expected values are: console, file, syslog");
|
||||
}
|
||||
|
||||
@DryRun
|
||||
@Test
|
||||
@RawDistOnly(reason = "Containers are immutable")
|
||||
void testWarningWhenOverridingBuildOptionsDuringStart(KeycloakDistribution dist) {
|
||||
@@ -188,30 +201,31 @@ public class StartCommandDistTest {
|
||||
cliResult.assertMessage("- db=postgres > db=dev-file"); // back to the default value
|
||||
cliResult.assertMessage("- features=preview > features=<unset>"); // no default value, the <unset> is shown
|
||||
cliResult.assertMessage("To avoid that, run the 'build' command again and then start the optimized server instance using the '--optimized' flag.");
|
||||
cliResult.assertStarted();
|
||||
assertTrue(cliResult.getErrorOutput().isBlank());
|
||||
// should not show warning if the re-augmentation did not happen through the build command
|
||||
// an optimized server image should ideally be created by running a build
|
||||
cliResult = dist.run("start", "--db=dev-mem", "--hostname=localhost", "--http-enabled=true");
|
||||
cliResult.assertNoMessage("The previous optimized build will be overridden with the following build options:");
|
||||
cliResult.assertStarted();
|
||||
assertTrue(cliResult.getErrorOutput().isBlank());
|
||||
dist.run("build", "--db=postgres");
|
||||
cliResult = dist.run("start", "--hostname=localhost", "--http-enabled=true");
|
||||
cliResult.assertMessage("- db=postgres > db=dev-file");
|
||||
cliResult.assertNoMessage("- features=preview > features=<unset>");
|
||||
cliResult.assertStarted();
|
||||
assertTrue(cliResult.getErrorOutput().isBlank());
|
||||
dist.run("build", "--db=postgres");
|
||||
cliResult = dist.run("start", "--db=dev-mem", "--hostname=localhost", "--http-enabled=true");
|
||||
cliResult.assertMessage("- db=postgres > db=dev-mem"); // option overridden during the start
|
||||
cliResult.assertStarted();
|
||||
assertTrue(cliResult.getErrorOutput().isBlank());
|
||||
dist.run("build", "--db=dev-mem");
|
||||
cliResult = dist.run("start", "--db=dev-mem", "--hostname=localhost", "--http-enabled=true");
|
||||
cliResult.assertNoMessage("- db=postgres > db=postgres"); // option did not change not need to show
|
||||
cliResult.assertStarted();
|
||||
assertTrue(cliResult.getErrorOutput().isBlank());
|
||||
dist.run("build", "--db=dev-mem");
|
||||
cliResult = dist.run("start", "--db=dev-mem", "--cache=local", "--hostname=localhost", "--http-enabled=true");
|
||||
cliResult.assertNoMessage("The previous optimized build will be overridden with the following build options:"); // no message, same values provided during auto-build
|
||||
}
|
||||
|
||||
@DryRun
|
||||
@Test
|
||||
@RawDistOnly(reason = "Containers are immutable")
|
||||
void testStartAfterStartDev(KeycloakDistribution dist) {
|
||||
@@ -219,9 +233,11 @@ public class StartCommandDistTest {
|
||||
cliResult.assertStartedDevMode();
|
||||
|
||||
cliResult = dist.run("start", "--http-enabled", "true", "--hostname-strict", "false");
|
||||
cliResult.assertStarted();
|
||||
cliResult.assertNotDevMode();
|
||||
assertTrue(cliResult.getErrorOutput().isBlank());
|
||||
}
|
||||
|
||||
@DryRun
|
||||
@Test
|
||||
@RawDistOnly(reason = "Containers are immutable")
|
||||
void testErrorWhenOverridingNonCliBuildOptionsDuringStart(KeycloakDistribution dist) {
|
||||
@@ -232,6 +248,7 @@ public class StartCommandDistTest {
|
||||
cliResult.assertError("The following build time options have values that differ from what is persisted - the new values will NOT be used until another build is run: kc.db");
|
||||
}
|
||||
|
||||
@DryRun
|
||||
@Test
|
||||
@Launch({CONFIG_FILE_LONG_NAME + "=src/test/resources/non-existing.conf", "start"})
|
||||
void testInvalidConfigFileOption(LaunchResult result) {
|
||||
|
||||
@@ -26,6 +26,7 @@ import org.junit.jupiter.api.condition.DisabledOnOs;
|
||||
import org.junit.jupiter.api.condition.OS;
|
||||
import org.keycloak.it.junit5.extension.CLIResult;
|
||||
import org.keycloak.it.junit5.extension.DistributionTest;
|
||||
import org.keycloak.it.junit5.extension.DryRun;
|
||||
import org.keycloak.it.junit5.extension.RawDistOnly;
|
||||
import org.keycloak.it.utils.KeycloakDistribution;
|
||||
import org.keycloak.it.utils.RawKeycloakDistribution;
|
||||
@@ -40,6 +41,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||
public class StartDevCommandDistTest {
|
||||
|
||||
@DryRun
|
||||
@Test
|
||||
@Launch({ "start-dev" })
|
||||
void testDevModeWarning(LaunchResult result) {
|
||||
@@ -47,6 +49,7 @@ public class StartDevCommandDistTest {
|
||||
cliResult.assertStartedDevMode();
|
||||
}
|
||||
|
||||
@DryRun
|
||||
@Test
|
||||
@Launch({ "start-dev", "--db=dev-mem" })
|
||||
void testBuildPropertyAvailable(LaunchResult result) {
|
||||
@@ -63,6 +66,7 @@ public class StartDevCommandDistTest {
|
||||
cliResult.assertMessage("passkeys");
|
||||
}
|
||||
|
||||
@DryRun
|
||||
@Test
|
||||
@Launch({ "build", "--debug" })
|
||||
void testBuildMustNotRunTwoJVMs(LaunchResult result) {
|
||||
@@ -71,6 +75,7 @@ public class StartDevCommandDistTest {
|
||||
cliResult.assertBuild();
|
||||
}
|
||||
|
||||
@DryRun
|
||||
@Test
|
||||
@Launch({ "start-dev", "--verbose" })
|
||||
void testVerboseAfterCommand(LaunchResult result) {
|
||||
@@ -88,7 +93,8 @@ public class StartDevCommandDistTest {
|
||||
assertTrue(cliResult.getOutput().contains("Listening on:"));
|
||||
cliResult.assertStartedDevMode();
|
||||
}
|
||||
|
||||
|
||||
@DryRun
|
||||
@Test
|
||||
void testStartDevThenImportRebuild(KeycloakDistribution dist) throws Exception {
|
||||
RawKeycloakDistribution rawDist = dist.unwrap(RawKeycloakDistribution.class);
|
||||
|
||||
@@ -23,9 +23,6 @@ import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
||||
/**
|
||||
* When used for in-vm tests, use {@link ConfigurationTestResource} to reset the configuration
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@ExtendWith({ CLITestExtension.class })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
|
||||
@@ -32,6 +32,7 @@ import org.keycloak.it.utils.KeycloakDistribution;
|
||||
import org.keycloak.it.utils.RawDistRootPath;
|
||||
import org.keycloak.it.utils.RawKeycloakDistribution;
|
||||
import org.keycloak.quarkus.runtime.Environment;
|
||||
import org.keycloak.quarkus.runtime.cli.command.DryRunMixin;
|
||||
import org.keycloak.quarkus.runtime.cli.command.Start;
|
||||
import org.keycloak.quarkus.runtime.cli.command.StartDev;
|
||||
import org.keycloak.quarkus.runtime.configuration.ConfigArgsConfigSource;
|
||||
@@ -101,6 +102,11 @@ public class CLITestExtension extends QuarkusMainTestExtension {
|
||||
|
||||
configureEnvVars(context.getRequiredTestClass().getAnnotation(WithEnvVars.class));
|
||||
configureEnvVars(context.getRequiredTestMethod().getAnnotation(WithEnvVars.class));
|
||||
boolean dryRun = context.getRequiredTestMethod().getAnnotation(DryRun.class) != null;
|
||||
if (dryRun && isRaw()) {
|
||||
dist.setEnvVar(DryRunMixin.KC_DRY_RUN_ENV, "true");
|
||||
dist.setEnvVar(DryRunMixin.KC_DRY_RUN_BUILD_ENV, "true");
|
||||
}
|
||||
|
||||
if (launch != null) {
|
||||
result = dist.run(Stream.concat(List.of(launch.value()).stream(), List.of(distConfig.defaultOptions()).stream()).collect(Collectors.toList()));
|
||||
@@ -125,7 +131,7 @@ public class CLITestExtension extends QuarkusMainTestExtension {
|
||||
return;
|
||||
}
|
||||
|
||||
if (RAW.equals(DistributionType.getCurrent().orElse(RAW))) {
|
||||
if (isRaw()) {
|
||||
try {
|
||||
dist.unwrap(RawKeycloakDistribution.class).copyProvider(provider.value().getDeclaredConstructor().newInstance());
|
||||
} catch (Exception cause) {
|
||||
@@ -134,6 +140,10 @@ public class CLITestExtension extends QuarkusMainTestExtension {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isRaw() {
|
||||
return RAW.equals(DistributionType.getCurrent().orElse(RAW));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void interceptTestMethod(Invocation<Void> invocation,
|
||||
ReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext) throws Throwable {
|
||||
@@ -171,6 +181,7 @@ public class CLITestExtension extends QuarkusMainTestExtension {
|
||||
if (dist != null) {
|
||||
onKeepServerAlive(context.getRequiredTestMethod().getAnnotation(KeepServerAlive.class), false);
|
||||
dist.stop();
|
||||
dist.clearEnv();
|
||||
|
||||
if (distConfig != null && DistributionTest.ReInstall.BEFORE_TEST.equals(distConfig.reInstall())) {
|
||||
dist = null;
|
||||
@@ -195,7 +206,7 @@ public class CLITestExtension extends QuarkusMainTestExtension {
|
||||
infinispanContainer.stop();
|
||||
}
|
||||
result = null;
|
||||
if (RAW.equals(DistributionType.getCurrent().orElse(RAW))) {
|
||||
if (isRaw()) {
|
||||
if (distConfig != null && !DistributionTest.ReInstall.NEVER.equals(distConfig.reInstall()) && dist != null) {
|
||||
try {
|
||||
FileUtil.deleteDirectory(getDistPath().getDistRootPath().resolve("conf"));
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright 2024 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.it.junit5.extension;
|
||||
|
||||
import io.quarkus.runtime.LaunchMode;
|
||||
import io.quarkus.runtime.configuration.ConfigUtils;
|
||||
import io.quarkus.test.common.QuarkusTestResourceLifecycleManager;
|
||||
import io.smallrye.config.SmallRyeConfig;
|
||||
import io.smallrye.config.SmallRyeConfigProviderResolver;
|
||||
|
||||
import org.eclipse.microprofile.config.spi.ConfigProviderResolver;
|
||||
import org.keycloak.quarkus.runtime.configuration.KeycloakConfigSourceProvider;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Used to reset the configuration for {@link CLITest}s
|
||||
*/
|
||||
public class ConfigurationTestResource implements QuarkusTestResourceLifecycleManager {
|
||||
|
||||
@Override
|
||||
public Map<String, String> start() {
|
||||
return Map.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inject(Object testInstance) {
|
||||
KeycloakConfigSourceProvider.reload();
|
||||
SmallRyeConfig config = ConfigUtils.configBuilder(true, LaunchMode.NORMAL).build();
|
||||
SmallRyeConfigProviderResolver resolver = new SmallRyeConfigProviderResolver();
|
||||
resolver.registerConfig(config, Thread.currentThread().getContextClassLoader());
|
||||
ConfigProviderResolver.setInstance(resolver);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2021 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.it.junit5.extension;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* {@link DryRun} is used to configure a non-running, non-augmenting distribution
|
||||
*/
|
||||
@Target({ ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface DryRun {
|
||||
|
||||
}
|
||||
@@ -143,4 +143,9 @@ public class KeycloakDistributionDecorator implements KeycloakDistribution {
|
||||
|
||||
throw new IllegalArgumentException("Not a " + type + " type");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearEnv() {
|
||||
delegate.clearEnv();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import org.keycloak.it.junit5.extension.CLIResult;
|
||||
import org.testcontainers.DockerClientFactory;
|
||||
import org.testcontainers.containers.GenericContainer;
|
||||
import org.testcontainers.containers.output.OutputFrame;
|
||||
import org.testcontainers.containers.output.OutputFrame.OutputType;
|
||||
import org.testcontainers.containers.output.ToStringConsumer;
|
||||
import org.testcontainers.containers.wait.strategy.Wait;
|
||||
import org.testcontainers.images.RemoteDockerImage;
|
||||
@@ -23,10 +24,26 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
public final class DockerKeycloakDistribution implements KeycloakDistribution {
|
||||
|
||||
private static class BackupConsumer implements Consumer<OutputFrame> {
|
||||
|
||||
final ToStringConsumer stdOut = new ToStringConsumer();
|
||||
final ToStringConsumer stdErr = new ToStringConsumer();
|
||||
|
||||
@Override
|
||||
public void accept(OutputFrame t) {
|
||||
if (t.getType() == OutputType.STDERR) {
|
||||
stdErr.accept(t);
|
||||
} else if (t.getType() == OutputType.STDOUT) {
|
||||
stdOut.accept(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(DockerKeycloakDistribution.class);
|
||||
|
||||
private final boolean debug;
|
||||
@@ -38,7 +55,7 @@ public final class DockerKeycloakDistribution implements KeycloakDistribution {
|
||||
|
||||
private String stdout = "";
|
||||
private String stderr = "";
|
||||
private ToStringConsumer backupConsumer = new ToStringConsumer();
|
||||
private BackupConsumer backupConsumer = new BackupConsumer();
|
||||
private final File dockerScriptFile = new File("../../container/ubi-null.sh");
|
||||
|
||||
private GenericContainer<?> keycloakContainer = null;
|
||||
@@ -100,7 +117,7 @@ public final class DockerKeycloakDistribution implements KeycloakDistribution {
|
||||
this.stdout = "";
|
||||
this.stderr = "";
|
||||
this.containerId = null;
|
||||
this.backupConsumer = new ToStringConsumer();
|
||||
this.backupConsumer = new BackupConsumer();
|
||||
|
||||
keycloakContainer = getKeycloakContainer();
|
||||
|
||||
@@ -113,15 +130,14 @@ public final class DockerKeycloakDistribution implements KeycloakDistribution {
|
||||
waitForStableOutput();
|
||||
} catch (Exception cause) {
|
||||
this.exitCode = -1;
|
||||
this.stdout = backupConsumer.toUtf8String();
|
||||
this.stderr = backupConsumer.toUtf8String();
|
||||
this.stdout = backupConsumer.stdOut.toUtf8String();
|
||||
this.stderr = backupConsumer.stdErr.toUtf8String();
|
||||
cleanupContainer();
|
||||
keycloakContainer = null;
|
||||
LOGGER.warn("Failed to start Keycloak container", cause);
|
||||
} finally {
|
||||
if (!manualStop) {
|
||||
stop();
|
||||
envVars.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,7 +209,9 @@ public final class DockerKeycloakDistribution implements KeycloakDistribution {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
if (containerId == null) return;
|
||||
if (containerId == null) {
|
||||
return;
|
||||
}
|
||||
DockerClient dockerClient = DockerClientFactory.lazyClient();
|
||||
dockerClient.killContainerCmd(containerId).exec();
|
||||
dockerClient.removeContainerCmd(containerId).withRemoveVolumes(true).withForce(true).exec();
|
||||
@@ -215,7 +233,7 @@ public final class DockerKeycloakDistribution implements KeycloakDistribution {
|
||||
if (keycloakContainer != null && keycloakContainer.isRunning()) {
|
||||
return keycloakContainer.getLogs(OutputFrame.OutputType.STDOUT);
|
||||
} else if (this.stdout.isEmpty()) {
|
||||
return backupConsumer.toUtf8String();
|
||||
return backupConsumer.stdOut.toUtf8String();
|
||||
} else {
|
||||
return this.stdout;
|
||||
}
|
||||
@@ -230,7 +248,7 @@ public final class DockerKeycloakDistribution implements KeycloakDistribution {
|
||||
if (keycloakContainer != null && keycloakContainer.isRunning()) {
|
||||
return keycloakContainer.getLogs(OutputFrame.OutputType.STDERR);
|
||||
} else if (this.stderr.isEmpty()) {
|
||||
return backupConsumer.toUtf8String();
|
||||
return backupConsumer.stdErr.toUtf8String();
|
||||
} else {
|
||||
return this.stderr;
|
||||
}
|
||||
@@ -269,4 +287,9 @@ public final class DockerKeycloakDistribution implements KeycloakDistribution {
|
||||
throw new IllegalArgumentException("Not a " + type + " type");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearEnv() {
|
||||
this.envVars.clear();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -69,4 +69,6 @@ public interface KeycloakDistribution {
|
||||
}
|
||||
|
||||
<D extends KeycloakDistribution> D unwrap(Class<D> type);
|
||||
|
||||
void clearEnv();
|
||||
}
|
||||
|
||||
@@ -187,7 +187,6 @@ public final class RawKeycloakDistribution implements KeycloakDistribution {
|
||||
}
|
||||
if (!manualStop) {
|
||||
stop();
|
||||
envVars.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -708,4 +707,9 @@ public final class RawKeycloakDistribution implements KeycloakDistribution {
|
||||
|
||||
throw new IllegalArgumentException("Not a " + type + " type");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearEnv() {
|
||||
this.envVars.clear();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user