KEYCLOAK-19808 throw error when using start command after start-dev without rebuild first
This commit is contained in:
parent
f6daca8a60
commit
8bd18ff21a
7 changed files with 104 additions and 23 deletions
|
@ -23,9 +23,7 @@ import java.io.File;
|
|||
import java.io.FilenameFilter;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
@ -41,6 +39,8 @@ public final class Environment {
|
|||
public static final String ENV_PROFILE ="KC_PROFILE";
|
||||
public static final String DATA_PATH = "/data";
|
||||
public static final String DEFAULT_THEMES_PATH = "/themes";
|
||||
public static final String DEV_PROFILE_VALUE = "dev";
|
||||
public static final String USER_INVOKED_CLI_COMMAND = "picocli.invoked.command";
|
||||
|
||||
private Environment() {}
|
||||
|
||||
|
@ -92,7 +92,30 @@ public final class Environment {
|
|||
}
|
||||
return "./kc.sh";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the originally invoked cli args. Useful to verify the originally invoked command
|
||||
* when calling another cli command internally (e.g. start-dev calls build internally)
|
||||
*/
|
||||
public static void setUserInvokedCliArgs(List<String> cliArgs) {
|
||||
System.setProperty(USER_INVOKED_CLI_COMMAND, String.join(",", cliArgs));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the previously set system property for the originally command.
|
||||
* Use the System variable, when you trigger other command executions internally, but need a reference to the
|
||||
* actually invoked command.
|
||||
*
|
||||
* @return the invoked command from the CLI, or empty List if not set.
|
||||
*/
|
||||
public static List<String> getUserInvokedCliArgs() {
|
||||
if(System.getProperty(USER_INVOKED_CLI_COMMAND) == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return List.of(System.getProperty(USER_INVOKED_CLI_COMMAND).split(","));
|
||||
}
|
||||
|
||||
public static String getConfigArgs() {
|
||||
return System.getProperty(CLI_ARGS, "");
|
||||
}
|
||||
|
@ -123,7 +146,7 @@ public final class Environment {
|
|||
}
|
||||
|
||||
public static boolean isDevMode() {
|
||||
if ("dev".equalsIgnoreCase(getProfile())) {
|
||||
if (DEV_PROFILE_VALUE.equalsIgnoreCase(getProfile())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -132,7 +155,11 @@ public final class Environment {
|
|||
return true;
|
||||
}
|
||||
|
||||
return "dev".equals(getBuiltTimeProperty(PROFILE).orElse(null));
|
||||
return DEV_PROFILE_VALUE.equals(getBuiltTimeProperty(PROFILE).orElse(null));
|
||||
}
|
||||
|
||||
public static boolean isDevProfile(){
|
||||
return Optional.ofNullable(getProfile()).orElse("").equalsIgnoreCase(DEV_PROFILE_VALUE);
|
||||
}
|
||||
|
||||
public static boolean isImportExportMode() {
|
||||
|
@ -144,7 +171,7 @@ public final class Environment {
|
|||
}
|
||||
|
||||
public static void forceDevProfile() {
|
||||
setProfile("dev");
|
||||
setProfile(DEV_PROFILE_VALUE);
|
||||
}
|
||||
|
||||
public static Map<String, File> getProviderFiles() {
|
||||
|
|
|
@ -17,10 +17,10 @@
|
|||
|
||||
package org.keycloak.quarkus.runtime;
|
||||
|
||||
import static org.keycloak.quarkus.runtime.Environment.isDevMode;
|
||||
import static org.keycloak.quarkus.runtime.Environment.isDevProfile;
|
||||
import static org.keycloak.quarkus.runtime.Environment.getProfileOrDefault;
|
||||
import static org.keycloak.quarkus.runtime.cli.Picocli.error;
|
||||
import static org.keycloak.quarkus.runtime.cli.Picocli.parseAndRun;
|
||||
import static org.keycloak.quarkus.runtime.Environment.getProfileOrDefault;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
|
@ -80,7 +80,7 @@ public class KeycloakMain implements QuarkusApplication {
|
|||
*/
|
||||
@Override
|
||||
public int run(String... args) throws Exception {
|
||||
if (isDevMode()) {
|
||||
if (isDevProfile()) {
|
||||
LOGGER.warnf("Running the server in dev mode. DO NOT use this configuration in production.");
|
||||
}
|
||||
Quarkus.waitForExit();
|
||||
|
|
|
@ -132,12 +132,13 @@ public final class Picocli {
|
|||
private static boolean requiresReAugmentation(CommandLine cmd) {
|
||||
if (hasConfigChanges()) {
|
||||
cmd.getOut().println("Changes detected in configuration. Updating the server image.");
|
||||
cmd.getOut().printf("For an optional runtime and bypass this step, please run the '%s' command prior to starting the server:%n%n\t%s %s %s%n",
|
||||
Build.NAME,
|
||||
Environment.getCommand(),
|
||||
Build.NAME,
|
||||
String.join(" ", asList(ARG_SPLIT.split(Environment.getConfigArgs()))) + "\n");
|
||||
|
||||
if(!isDevMode()) {
|
||||
cmd.getOut().printf("For an optional runtime and bypass this step, please run the '%s' command prior to starting the server:%n%n\t%s %s %s%n",
|
||||
Build.NAME,
|
||||
Environment.getCommand(),
|
||||
Build.NAME,
|
||||
String.join(" ", asList(ARG_SPLIT.split(Environment.getConfigArgs()))) + "\n");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -152,6 +153,8 @@ public final class Picocli {
|
|||
// force the server image to be set with the dev profile
|
||||
Environment.forceDevProfile();
|
||||
}
|
||||
|
||||
Environment.setUserInvokedCliArgs(cliArgs);
|
||||
}
|
||||
|
||||
List<String> configArgsList = new ArrayList<>(cliArgs);
|
||||
|
@ -166,7 +169,9 @@ public final class Picocli {
|
|||
|
||||
cmd.execute(configArgsList.toArray(new String[0]));
|
||||
|
||||
cmd.getOut().printf("Next time you run the server, just run:%n%n\t%s %s%n%n", Environment.getCommand(), Start.NAME);
|
||||
if(!isDevMode()) {
|
||||
cmd.getOut().printf("Next time you run the server, just run:%n%n\t%s %s%n%n", Environment.getCommand(), Start.NAME);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean hasProviderChanges() {
|
||||
|
|
|
@ -25,6 +25,8 @@ import picocli.CommandLine.Model.CommandSpec;
|
|||
import picocli.CommandLine.Spec;
|
||||
import picocli.CommandLine.Option;
|
||||
|
||||
import static org.keycloak.quarkus.runtime.cli.Picocli.error;
|
||||
|
||||
public abstract class AbstractCommand {
|
||||
|
||||
@Spec
|
||||
|
@ -49,4 +51,8 @@ public abstract class AbstractCommand {
|
|||
public void setConfigFile(String path) {
|
||||
System.setProperty(KeycloakConfigSourceProvider.KEYCLOAK_CONFIG_FILE_PROP, path);
|
||||
}
|
||||
|
||||
protected void showDevNotAllowedErrorAndExit(String cmd) {
|
||||
error(spec.commandLine(), String.format("You can not '%s' the server using the 'dev' configuration profile. Please re-build the server first, using './kc.sh build' for the default production profile, or using '/.kc.sh build --profile=<profile>' with a profile more suitable for production.%n", cmd));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
package org.keycloak.quarkus.runtime.cli.command;
|
||||
|
||||
import static org.keycloak.quarkus.runtime.Environment.getHomePath;
|
||||
import static org.keycloak.quarkus.runtime.Environment.*;
|
||||
import static org.keycloak.quarkus.runtime.cli.Picocli.error;
|
||||
import static org.keycloak.quarkus.runtime.cli.Picocli.println;
|
||||
|
||||
|
@ -29,6 +29,8 @@ import io.quarkus.bootstrap.runner.RunnerClassLoader;
|
|||
import io.quarkus.runtime.configuration.ProfileManager;
|
||||
import picocli.CommandLine.Command;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Command(name = Build.NAME,
|
||||
header = "Creates a new and optimized server image.",
|
||||
description = {
|
||||
|
@ -67,14 +69,19 @@ public final class Build extends AbstractCommand implements Runnable {
|
|||
|
||||
@Override
|
||||
public void run() {
|
||||
exitWithErrorIfDevProfileIsSetAndNotStartDev();
|
||||
|
||||
System.setProperty("quarkus.launch.rebuild", "true");
|
||||
println(spec.commandLine(), "Updating the configuration and installing your custom providers, if any. Please wait.");
|
||||
|
||||
try {
|
||||
beforeReaugmentationOnWindows();
|
||||
QuarkusEntryPoint.main();
|
||||
println(spec.commandLine(), "Server configuration updated and persisted. Run the following command to review the configuration:\n");
|
||||
println(spec.commandLine(), "\t" + Environment.getCommand() + " show-config\n");
|
||||
|
||||
if (!isDevMode()) {
|
||||
println(spec.commandLine(), "Server configuration updated and persisted. Run the following command to review the configuration:\n");
|
||||
println(spec.commandLine(), "\t" + Environment.getCommand() + " show-config\n");
|
||||
}
|
||||
} catch (Throwable throwable) {
|
||||
error(spec.commandLine(), "Failed to update server configuration.", throwable);
|
||||
} finally {
|
||||
|
@ -82,6 +89,13 @@ public final class Build extends AbstractCommand implements Runnable {
|
|||
}
|
||||
}
|
||||
|
||||
private void exitWithErrorIfDevProfileIsSetAndNotStartDev() {
|
||||
List<String> userInvokedCliArgs = Environment.getUserInvokedCliArgs();
|
||||
if(Environment.isDevProfile() && !userInvokedCliArgs.contains(StartDev.NAME)) {
|
||||
showDevNotAllowedErrorAndExit(Build.NAME);
|
||||
}
|
||||
}
|
||||
|
||||
private void beforeReaugmentationOnWindows() {
|
||||
// On Windows, files generated during re-augmentation are locked and can't be re-created.
|
||||
// To workaround this behavior, we reset the internal cache of the runner classloader and force files
|
||||
|
|
|
@ -17,15 +17,22 @@
|
|||
|
||||
package org.keycloak.quarkus.runtime.cli.command;
|
||||
|
||||
import static org.keycloak.quarkus.runtime.Environment.isDevProfile;
|
||||
import static org.keycloak.quarkus.runtime.cli.Picocli.NO_PARAM_LABEL;
|
||||
import static org.keycloak.quarkus.runtime.cli.Picocli.error;
|
||||
import static org.keycloak.quarkus.runtime.configuration.Configuration.getBuiltTimeProperty;
|
||||
|
||||
import org.keycloak.quarkus.runtime.Environment;
|
||||
import picocli.CommandLine;
|
||||
import picocli.CommandLine.Command;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Command(name = Start.NAME,
|
||||
header = "Start the server.",
|
||||
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"
|
||||
+ " $ ${PARENT-COMMAND-FULL-NAME:-$PARENTCOMMAND} ${COMMAND-NAME} --auto-build <OPTIONS>%n%n"
|
||||
|
@ -37,11 +44,33 @@ public final class Start extends AbstractStartCommand implements Runnable {
|
|||
|
||||
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" +
|
||||
" 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.",
|
||||
paramLabel = NO_PARAM_LABEL,
|
||||
order = 1)
|
||||
Boolean autoConfig;
|
||||
|
||||
@Override
|
||||
protected void doBeforeRun() {
|
||||
checkIfProfileIsNotDev();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the profile provided by either the current argument, the system environment or the persisted properties is dev.
|
||||
* Fails with an error when dev profile is used for the start command, or continues with the found profile if its not the dev profile.
|
||||
*/
|
||||
private void checkIfProfileIsNotDev() {
|
||||
List<String> currentCliArgs = spec.commandLine().getParseResult().expandedArgs();
|
||||
|
||||
Optional<String> currentProfile = Optional.ofNullable(Environment.getProfile());
|
||||
Optional<String> persistedProfile = getBuiltTimeProperty("kc.profile");
|
||||
|
||||
setProfile(currentProfile.orElse(persistedProfile.orElse("prod")));
|
||||
|
||||
if (isDevProfile() && (!currentCliArgs.contains(AUTO_BUILD_OPTION_LONG) || !currentCliArgs.contains(AUTO_BUILD_OPTION_SHORT))) {
|
||||
showDevNotAllowedErrorAndExit(Start.NAME);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ public final class Messages {
|
|||
|
||||
public static IllegalStateException httpsConfigurationNotSet() {
|
||||
StringBuilder builder = new StringBuilder("Key material not provided to setup HTTPS. Please configure your keys/certificates");
|
||||
if (!"dev".equals(Environment.getProfile())) {
|
||||
if (!Environment.DEV_PROFILE_VALUE.equals(Environment.getProfile())) {
|
||||
builder.append(" or start the server in development mode");
|
||||
}
|
||||
builder.append(".");
|
||||
|
|
Loading…
Reference in a new issue