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.io.FilenameFilter;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.Arrays;
|
import java.util.*;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.stream.Collectors;
|
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 ENV_PROFILE ="KC_PROFILE";
|
||||||
public static final String DATA_PATH = "/data";
|
public static final String DATA_PATH = "/data";
|
||||||
public static final String DEFAULT_THEMES_PATH = "/themes";
|
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() {}
|
private Environment() {}
|
||||||
|
|
||||||
|
@ -93,6 +93,29 @@ public final class Environment {
|
||||||
return "./kc.sh";
|
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() {
|
public static String getConfigArgs() {
|
||||||
return System.getProperty(CLI_ARGS, "");
|
return System.getProperty(CLI_ARGS, "");
|
||||||
}
|
}
|
||||||
|
@ -123,7 +146,7 @@ public final class Environment {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isDevMode() {
|
public static boolean isDevMode() {
|
||||||
if ("dev".equalsIgnoreCase(getProfile())) {
|
if (DEV_PROFILE_VALUE.equalsIgnoreCase(getProfile())) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,7 +155,11 @@ public final class Environment {
|
||||||
return true;
|
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() {
|
public static boolean isImportExportMode() {
|
||||||
|
@ -144,7 +171,7 @@ public final class Environment {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void forceDevProfile() {
|
public static void forceDevProfile() {
|
||||||
setProfile("dev");
|
setProfile(DEV_PROFILE_VALUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Map<String, File> getProviderFiles() {
|
public static Map<String, File> getProviderFiles() {
|
||||||
|
|
|
@ -17,10 +17,10 @@
|
||||||
|
|
||||||
package org.keycloak.quarkus.runtime;
|
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.error;
|
||||||
import static org.keycloak.quarkus.runtime.cli.Picocli.parseAndRun;
|
import static org.keycloak.quarkus.runtime.cli.Picocli.parseAndRun;
|
||||||
import static org.keycloak.quarkus.runtime.Environment.getProfileOrDefault;
|
|
||||||
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -80,7 +80,7 @@ public class KeycloakMain implements QuarkusApplication {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public int run(String... args) throws Exception {
|
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.");
|
LOGGER.warnf("Running the server in dev mode. DO NOT use this configuration in production.");
|
||||||
}
|
}
|
||||||
Quarkus.waitForExit();
|
Quarkus.waitForExit();
|
||||||
|
|
|
@ -132,12 +132,13 @@ public final class Picocli {
|
||||||
private static boolean requiresReAugmentation(CommandLine cmd) {
|
private static boolean requiresReAugmentation(CommandLine cmd) {
|
||||||
if (hasConfigChanges()) {
|
if (hasConfigChanges()) {
|
||||||
cmd.getOut().println("Changes detected in configuration. Updating the server image.");
|
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",
|
if(!isDevMode()) {
|
||||||
Build.NAME,
|
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",
|
||||||
Environment.getCommand(),
|
Build.NAME,
|
||||||
Build.NAME,
|
Environment.getCommand(),
|
||||||
String.join(" ", asList(ARG_SPLIT.split(Environment.getConfigArgs()))) + "\n");
|
Build.NAME,
|
||||||
|
String.join(" ", asList(ARG_SPLIT.split(Environment.getConfigArgs()))) + "\n");
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,6 +153,8 @@ public final class Picocli {
|
||||||
// force the server image to be set with the dev profile
|
// force the server image to be set with the dev profile
|
||||||
Environment.forceDevProfile();
|
Environment.forceDevProfile();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Environment.setUserInvokedCliArgs(cliArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<String> configArgsList = new ArrayList<>(cliArgs);
|
List<String> configArgsList = new ArrayList<>(cliArgs);
|
||||||
|
@ -166,7 +169,9 @@ public final class Picocli {
|
||||||
|
|
||||||
cmd.execute(configArgsList.toArray(new String[0]));
|
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() {
|
private static boolean hasProviderChanges() {
|
||||||
|
|
|
@ -25,6 +25,8 @@ import picocli.CommandLine.Model.CommandSpec;
|
||||||
import picocli.CommandLine.Spec;
|
import picocli.CommandLine.Spec;
|
||||||
import picocli.CommandLine.Option;
|
import picocli.CommandLine.Option;
|
||||||
|
|
||||||
|
import static org.keycloak.quarkus.runtime.cli.Picocli.error;
|
||||||
|
|
||||||
public abstract class AbstractCommand {
|
public abstract class AbstractCommand {
|
||||||
|
|
||||||
@Spec
|
@Spec
|
||||||
|
@ -49,4 +51,8 @@ public abstract class AbstractCommand {
|
||||||
public void setConfigFile(String path) {
|
public void setConfigFile(String path) {
|
||||||
System.setProperty(KeycloakConfigSourceProvider.KEYCLOAK_CONFIG_FILE_PROP, 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;
|
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.error;
|
||||||
import static org.keycloak.quarkus.runtime.cli.Picocli.println;
|
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 io.quarkus.runtime.configuration.ProfileManager;
|
||||||
import picocli.CommandLine.Command;
|
import picocli.CommandLine.Command;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@Command(name = Build.NAME,
|
@Command(name = Build.NAME,
|
||||||
header = "Creates a new and optimized server image.",
|
header = "Creates a new and optimized server image.",
|
||||||
description = {
|
description = {
|
||||||
|
@ -67,14 +69,19 @@ public final class Build extends AbstractCommand implements Runnable {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
exitWithErrorIfDevProfileIsSetAndNotStartDev();
|
||||||
|
|
||||||
System.setProperty("quarkus.launch.rebuild", "true");
|
System.setProperty("quarkus.launch.rebuild", "true");
|
||||||
println(spec.commandLine(), "Updating the configuration and installing your custom providers, if any. Please wait.");
|
println(spec.commandLine(), "Updating the configuration and installing your custom providers, if any. Please wait.");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
beforeReaugmentationOnWindows();
|
beforeReaugmentationOnWindows();
|
||||||
QuarkusEntryPoint.main();
|
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) {
|
} catch (Throwable throwable) {
|
||||||
error(spec.commandLine(), "Failed to update server configuration.", throwable);
|
error(spec.commandLine(), "Failed to update server configuration.", throwable);
|
||||||
} finally {
|
} 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() {
|
private void beforeReaugmentationOnWindows() {
|
||||||
// On Windows, files generated during re-augmentation are locked and can't be re-created.
|
// 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
|
// 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;
|
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.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;
|
||||||
import picocli.CommandLine.Command;
|
import picocli.CommandLine.Command;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
@Command(name = Start.NAME,
|
@Command(name = Start.NAME,
|
||||||
header = "Start the server.",
|
header = "Start the server.",
|
||||||
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 = "%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"
|
+ " $ ${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";
|
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 = "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;
|
||||||
|
|
||||||
|
@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() {
|
public static IllegalStateException httpsConfigurationNotSet() {
|
||||||
StringBuilder builder = new StringBuilder("Key material not provided to setup HTTPS. Please configure your keys/certificates");
|
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(" or start the server in development mode");
|
||||||
}
|
}
|
||||||
builder.append(".");
|
builder.append(".");
|
||||||
|
|
Loading…
Reference in a new issue