KEYCLOAK-19307 provide hints in CLI
This commit is contained in:
parent
af97849feb
commit
5628370099
20 changed files with 254 additions and 92 deletions
|
@ -48,7 +48,7 @@ do
|
|||
if [[ $1 = --* || ! $1 =~ ^-D.* ]]; then
|
||||
CONFIG_ARGS="$CONFIG_ARGS $1"
|
||||
if [[ "$1" = "start-dev" ]]; then
|
||||
CONFIG_ARGS="$CONFIG_ARGS --auto-build"
|
||||
CONFIG_ARGS="$CONFIG_ARGS --profile=dev --auto-build"
|
||||
fi
|
||||
else
|
||||
SERVER_OPTS="$SERVER_OPTS $1"
|
||||
|
|
|
@ -128,7 +128,11 @@ public final class Environment {
|
|||
}
|
||||
|
||||
// if running in quarkus:dev mode
|
||||
return ProfileManager.getLaunchMode() == LaunchMode.DEVELOPMENT;
|
||||
if (ProfileManager.getLaunchMode() == LaunchMode.DEVELOPMENT) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return "dev".equals(getBuiltTimeProperty(PROFILE).orElse(null));
|
||||
}
|
||||
|
||||
public static boolean isImportExportMode() {
|
||||
|
@ -140,8 +144,7 @@ public final class Environment {
|
|||
}
|
||||
|
||||
public static void forceDevProfile() {
|
||||
System.setProperty(PROFILE, "dev");
|
||||
System.setProperty("quarkus.profile", "dev");
|
||||
setProfile("dev");
|
||||
}
|
||||
|
||||
public static Map<String, File> getProviderFiles() {
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
package org.keycloak.quarkus.runtime;
|
||||
|
||||
import static org.keycloak.quarkus.runtime.Environment.isDevMode;
|
||||
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;
|
||||
|
@ -29,6 +30,7 @@ import java.util.List;
|
|||
import io.quarkus.runtime.ApplicationLifecycleManager;
|
||||
import io.quarkus.runtime.Quarkus;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.quarkus.runtime.cli.Picocli;
|
||||
import org.keycloak.common.Version;
|
||||
|
||||
|
@ -41,6 +43,8 @@ import io.quarkus.runtime.annotations.QuarkusMain;
|
|||
@QuarkusMain(name = "keycloak")
|
||||
public class KeycloakMain implements QuarkusApplication {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(KeycloakMain.class);
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.setProperty("kc.version", Version.VERSION_KEYCLOAK);
|
||||
List<String> cliArgs = new ArrayList<>(Arrays.asList(args));
|
||||
|
@ -61,13 +65,13 @@ public class KeycloakMain implements QuarkusApplication {
|
|||
Quarkus.run(KeycloakMain.class, (integer, cause) -> {
|
||||
if (cause != null) {
|
||||
error(cliArgs, errorWriter,
|
||||
String.format("Failed to start server using profile (%s)", getProfileOrDefault("none")),
|
||||
String.format("Failed to start server using profile (%s)", getProfileOrDefault("prod")),
|
||||
cause.getCause());
|
||||
}
|
||||
});
|
||||
} catch (Throwable cause) {
|
||||
error(cliArgs, errorWriter,
|
||||
String.format("Unexpected error when starting the server using profile (%s)", getProfileOrDefault("none")),
|
||||
String.format("Unexpected error when starting the server using profile (%s)", getProfileOrDefault("prod")),
|
||||
cause.getCause());
|
||||
}
|
||||
}
|
||||
|
@ -77,6 +81,9 @@ public class KeycloakMain implements QuarkusApplication {
|
|||
*/
|
||||
@Override
|
||||
public int run(String... args) throws Exception {
|
||||
if (isDevMode()) {
|
||||
LOGGER.warnf("Running the server in dev mode. DO NOT use this configuration in production.");
|
||||
}
|
||||
Quarkus.waitForExit();
|
||||
return ApplicationLifecycleManager.getExitCode();
|
||||
}
|
||||
|
|
|
@ -18,11 +18,12 @@
|
|||
package org.keycloak.quarkus.runtime.cli;
|
||||
|
||||
import picocli.CommandLine;
|
||||
import picocli.CommandLine.ParseResult;
|
||||
|
||||
public class ExecutionExceptionHandler implements CommandLine.IExecutionExceptionHandler {
|
||||
|
||||
@Override
|
||||
public int handleExecutionException(Exception ex, CommandLine commandLine, CommandLine.ParseResult parseResult) {
|
||||
public int handleExecutionException(Exception ex, CommandLine commandLine, ParseResult parseResult) {
|
||||
commandLine.getErr().println(ex.getMessage());
|
||||
commandLine.usage(commandLine.getErr());
|
||||
return commandLine.getCommandSpec().exitCodeOnExecutionException();
|
||||
|
|
|
@ -28,12 +28,14 @@ import java.io.File;
|
|||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.file.FileSystemException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.function.IntFunction;
|
||||
|
@ -47,13 +49,20 @@ import org.keycloak.quarkus.runtime.cli.command.Start;
|
|||
import org.keycloak.quarkus.runtime.cli.command.StartDev;
|
||||
import org.keycloak.common.Profile;
|
||||
import org.keycloak.quarkus.runtime.configuration.KeycloakConfigSourceProvider;
|
||||
import org.keycloak.quarkus.runtime.configuration.Messages;
|
||||
import org.keycloak.quarkus.runtime.configuration.PropertyMapper;
|
||||
import org.keycloak.quarkus.runtime.configuration.PropertyMappers;
|
||||
import org.keycloak.platform.Platform;
|
||||
import org.keycloak.quarkus.runtime.InitializationException;
|
||||
import org.keycloak.quarkus.runtime.integration.QuarkusPlatform;
|
||||
import org.keycloak.quarkus.runtime.Environment;
|
||||
|
||||
import io.smallrye.config.ConfigValue;
|
||||
import picocli.CommandLine;
|
||||
import picocli.CommandLine.Model.CommandSpec;
|
||||
import picocli.CommandLine.UnmatchedArgumentException;
|
||||
import picocli.CommandLine.ParseResult;
|
||||
import picocli.CommandLine.Model.OptionSpec;
|
||||
|
||||
public final class Picocli {
|
||||
|
||||
|
@ -72,13 +81,13 @@ public final class Picocli {
|
|||
CommandLine cmd = createCommandLine(cliArgs);
|
||||
|
||||
try {
|
||||
CommandLine.ParseResult result = cmd.parseArgs(cliArgs.toArray(new String[0]));
|
||||
ParseResult result = cmd.parseArgs(cliArgs.toArray(new String[0]));
|
||||
|
||||
if (!result.hasSubcommand() && !result.isUsageHelpRequested() && !result.isVersionHelpRequested()) {
|
||||
// if no command was set, the start command becomes the default
|
||||
cliArgs.add(0, Start.NAME);
|
||||
}
|
||||
} catch (CommandLine.UnmatchedArgumentException e) {
|
||||
} catch (UnmatchedArgumentException e) {
|
||||
// if no command was set but options were provided, the start command becomes the default
|
||||
if (!cmd.getParseResult().hasSubcommand() && cliArgs.get(0).startsWith("--")) {
|
||||
cliArgs.add(0, "start");
|
||||
|
@ -196,6 +205,13 @@ public final class Picocli {
|
|||
}
|
||||
|
||||
private static boolean hasConfigChanges() {
|
||||
Optional<String> currentProfile = Optional.ofNullable(Environment.getProfile());
|
||||
Optional<String> persistedProfile = Environment.getBuiltTimeProperty("kc.profile");
|
||||
|
||||
if (!persistedProfile.orElse("").equals(currentProfile.orElse(""))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (String propertyName : getConfig().getPropertyNames()) {
|
||||
// only check keycloak build-time properties
|
||||
if (!isBuildTimeProperty(propertyName)) {
|
||||
|
@ -224,9 +240,11 @@ public final class Picocli {
|
|||
}
|
||||
|
||||
private static CommandLine createCommandLine(List<String> cliArgs) {
|
||||
CommandLine.Model.CommandSpec spec = CommandLine.Model.CommandSpec.forAnnotatedObject(new Main())
|
||||
CommandSpec spec = CommandSpec.forAnnotatedObject(new Main())
|
||||
.name(Environment.getCommand());
|
||||
|
||||
spec.usageMessage().width(100);
|
||||
|
||||
boolean addBuildOptionsToStartCommand = cliArgs.contains(AUTO_BUILD_OPTION);
|
||||
|
||||
addOption(spec, Start.NAME, addBuildOptionsToStartCommand);
|
||||
|
@ -285,8 +303,8 @@ public final class Picocli {
|
|||
return options.toString();
|
||||
}
|
||||
|
||||
private static void addOption(CommandLine.Model.CommandSpec spec, String command, boolean includeBuildTime) {
|
||||
CommandLine.Model.CommandSpec commandSpec = spec.subcommands().get(command).getCommandSpec();
|
||||
private static void addOption(CommandSpec spec, String command, boolean includeBuildTime) {
|
||||
CommandSpec commandSpec = spec.subcommands().get(command).getCommandSpec();
|
||||
List<PropertyMapper> mappers = new ArrayList<>(PropertyMappers.getRuntimeMappers());
|
||||
|
||||
if (includeBuildTime) {
|
||||
|
@ -309,8 +327,8 @@ public final class Picocli {
|
|||
type -> type.name().toLowerCase()).toArray((IntFunction<CharSequence[]>) String[]::new)), null);
|
||||
}
|
||||
|
||||
private static void addOption(CommandLine.Model.CommandSpec commandSpec, String name, String description, PropertyMapper mapper) {
|
||||
CommandLine.Model.OptionSpec.Builder builder = CommandLine.Model.OptionSpec.builder(name)
|
||||
private static void addOption(CommandSpec commandSpec, String name, String description, PropertyMapper mapper) {
|
||||
OptionSpec.Builder builder = OptionSpec.builder(name)
|
||||
.description(description)
|
||||
.paramLabel(name.substring(2))
|
||||
.type(String.class);
|
||||
|
@ -323,7 +341,7 @@ public final class Picocli {
|
|||
}
|
||||
|
||||
public static List<String> getCliArgs(CommandLine cmd) {
|
||||
CommandLine.ParseResult parseResult = cmd.getParseResult();
|
||||
ParseResult parseResult = cmd.getParseResult();
|
||||
|
||||
if (parseResult == null) {
|
||||
return Collections.emptyList();
|
||||
|
@ -385,8 +403,21 @@ public final class Picocli {
|
|||
if (cause.getMessage() != null) {
|
||||
logError(errorWriter, String.format("ERROR: %s", cause.getMessage()));
|
||||
}
|
||||
printErrorHints(errorWriter, cause);
|
||||
} while ((cause = cause.getCause())!= null);
|
||||
}
|
||||
printErrorHints(errorWriter, cause);
|
||||
}
|
||||
|
||||
private static void printErrorHints(PrintWriter errorWriter, Throwable cause) {
|
||||
if (cause instanceof FileSystemException) {
|
||||
FileSystemException fse = (FileSystemException) cause;
|
||||
ConfigValue httpsCertFile = getConfig().getConfigValue("kc.https.certificate.file");
|
||||
|
||||
if (fse.getFile().equals(Optional.ofNullable(httpsCertFile.getValue()).orElse(null))) {
|
||||
logError(errorWriter, Messages.httpsConfigurationNotSet().getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void logError(PrintWriter errorWriter, String errorMessage) {
|
||||
|
|
|
@ -21,22 +21,28 @@ import java.util.Collection;
|
|||
import java.util.Map;
|
||||
|
||||
import picocli.CommandLine;
|
||||
import picocli.CommandLine.Help;
|
||||
import picocli.CommandLine.Help.Column;
|
||||
import picocli.CommandLine.Help.TextTable;
|
||||
import picocli.CommandLine.Model.CommandSpec;
|
||||
import picocli.CommandLine.Model.UsageMessageSpec;
|
||||
|
||||
/**
|
||||
* A {@link picocli.CommandLine.IHelpSectionRenderer} based on Quarkus CLI to show subcomands in help messages.
|
||||
* A {@link picocli.CommandLine.IHelpSectionRenderer} based on Quarkus CLI to show subcommands in help messages.
|
||||
*/
|
||||
class SubCommandListRenderer implements CommandLine.IHelpSectionRenderer {
|
||||
// @Override
|
||||
public String render(CommandLine.Help help) {
|
||||
CommandLine.Model.CommandSpec spec = help.commandSpec();
|
||||
public String render(Help help) {
|
||||
CommandSpec spec = help.commandSpec();
|
||||
if (spec.subcommands().isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
CommandLine.Help.Column commands = new CommandLine.Help.Column(24, 2, CommandLine.Help.Column.Overflow.SPAN);
|
||||
CommandLine.Help.Column descriptions = new CommandLine.Help.Column(spec.usageMessage().width() - 24, 2,
|
||||
CommandLine.Help.Column.Overflow.WRAP);
|
||||
CommandLine.Help.TextTable textTable = CommandLine.Help.TextTable.forColumns(help.colorScheme(), commands, descriptions);
|
||||
Column commands = new Column(24, 2, Column.Overflow.SPAN);
|
||||
Column descriptions = new Column(spec.usageMessage().width() - 24, 2,
|
||||
Column.Overflow.WRAP);
|
||||
|
||||
TextTable textTable = TextTable.forColumns(help.colorScheme(), commands, descriptions);
|
||||
textTable.setAdjustLineBreaksForWideCJKCharacters(spec.usageMessage().adjustLineBreaksForWideCJKCharacters());
|
||||
|
||||
addHierarchy(spec.subcommands().values(), textTable, "");
|
||||
|
@ -58,7 +64,7 @@ class SubCommandListRenderer implements CommandLine.IHelpSectionRenderer {
|
|||
});
|
||||
}
|
||||
|
||||
private String description(CommandLine.Model.UsageMessageSpec usageMessage) {
|
||||
private String description(UsageMessageSpec usageMessage) {
|
||||
if (usageMessage.header().length > 0) {
|
||||
return usageMessage.header()[0];
|
||||
}
|
||||
|
|
|
@ -19,14 +19,20 @@ package org.keycloak.quarkus.runtime.cli.command;
|
|||
|
||||
import org.keycloak.quarkus.runtime.Environment;
|
||||
|
||||
import picocli.CommandLine;
|
||||
import picocli.CommandLine.Model.CommandSpec;
|
||||
import picocli.CommandLine.Spec;
|
||||
import picocli.CommandLine.Option;
|
||||
import picocli.CommandLine.ScopeType;
|
||||
|
||||
public abstract class AbstractCommand {
|
||||
|
||||
@CommandLine.Spec
|
||||
protected CommandLine.Model.CommandSpec spec;
|
||||
@Spec
|
||||
protected CommandSpec spec;
|
||||
|
||||
@CommandLine.Option(names = "--profile", arity = "1", description = "Set the profile. Use 'dev' profile to enable development mode.", scope = CommandLine.ScopeType.INHERIT)
|
||||
@Option(names = "--profile",
|
||||
arity = "1",
|
||||
description = "Set the profile. Use 'dev' profile to enable development mode.",
|
||||
scope = ScopeType.INHERIT)
|
||||
public void setProfile(String profile) {
|
||||
Environment.setProfile(profile);
|
||||
}
|
||||
|
|
|
@ -22,14 +22,29 @@ import static org.keycloak.quarkus.runtime.cli.Picocli.error;
|
|||
import org.keycloak.quarkus.runtime.Environment;
|
||||
|
||||
import picocli.CommandLine;
|
||||
import picocli.CommandLine.Option;
|
||||
|
||||
public abstract class AbstractExportImportCommand extends AbstractCommand implements Runnable {
|
||||
|
||||
private final String action;
|
||||
|
||||
@CommandLine.Option(names = "--dir", arity = "1", description = "Set the path to a directory where files will be created with the exported data.", paramLabel = "<path>") String toDir;
|
||||
@CommandLine.Option(names = "--file", arity = "1", description = "Set the path to a file that will be created with the exported data.", paramLabel = "<path>") String toFile;
|
||||
@CommandLine.Option(names = "--realm", arity = "1", description = "Set the name of the realm to export", paramLabel = "<realm>") String realm;
|
||||
@Option(names = "--dir",
|
||||
arity = "1",
|
||||
description = "Set the path to a directory where files will be created with the exported data.",
|
||||
paramLabel = "<path>")
|
||||
String toDir;
|
||||
|
||||
@Option(names = "--file",
|
||||
arity = "1",
|
||||
description = "Set the path to a file that will be created with the exported data.",
|
||||
paramLabel = "<path>")
|
||||
String toFile;
|
||||
|
||||
@Option(names = "--realm",
|
||||
arity = "1",
|
||||
description = "Set the name of the realm to export",
|
||||
paramLabel = "<realm>")
|
||||
String realm;
|
||||
|
||||
protected AbstractExportImportCommand(String action) {
|
||||
this.action = action;
|
||||
|
|
|
@ -20,12 +20,17 @@ package org.keycloak.quarkus.runtime.cli.command;
|
|||
import org.keycloak.quarkus.runtime.KeycloakMain;
|
||||
|
||||
import picocli.CommandLine;
|
||||
import picocli.CommandLine.Option;
|
||||
|
||||
public abstract class AbstractStartCommand extends AbstractCommand implements Runnable {
|
||||
|
||||
public static final String AUTO_BUILD_OPTION = "--auto-build";
|
||||
|
||||
@CommandLine.Option(names = AUTO_BUILD_OPTION, 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.", order = 1)
|
||||
@Option(names = AUTO_BUILD_OPTION,
|
||||
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.",
|
||||
order = 1)
|
||||
Boolean autoConfig;
|
||||
|
||||
@Override
|
||||
|
|
|
@ -24,19 +24,38 @@ import org.keycloak.quarkus.runtime.Environment;
|
|||
|
||||
import io.quarkus.bootstrap.runner.QuarkusEntryPoint;
|
||||
import io.quarkus.bootstrap.runner.RunnerClassLoader;
|
||||
import picocli.CommandLine;
|
||||
|
||||
@CommandLine.Command(name = Build.NAME,
|
||||
header = "Creates a new and optimized server image based on the configuration options passed to this command.",
|
||||
import picocli.CommandLine.Command;
|
||||
|
||||
@Command(name = Build.NAME,
|
||||
header = "Creates a new and optimized server image.",
|
||||
description = {
|
||||
"Creates a new and optimized server image based on the configuration options passed to this command. Once created, configuration will be read from the server image and the server can be started without passing the same options again.",
|
||||
"%nCreates a new and optimized server image based on the configuration options passed to this command. Once created, the configuration will be persisted and read during startup without having to pass them over again.",
|
||||
"",
|
||||
"Some configuration options require this command to be executed in order to actually change a configuration. For instance, the database vendor."
|
||||
"Some configuration options require this command to be executed in order to actually change a configuration. For instance",
|
||||
"",
|
||||
"- Change database vendor%n" +
|
||||
"- Enable/disable features%n" +
|
||||
"- Enable/Disable providers or set a default",
|
||||
"",
|
||||
"Consider running this command before running the server in production for an optimal runtime."
|
||||
},
|
||||
footerHeading = "%nExamples:%n%n"
|
||||
+ " Optimize the server based on a profile configuration:%n%n"
|
||||
+ " $ ${PARENT-COMMAND-FULL-NAME:-$PARENTCOMMAND} ${COMMAND-NAME} --profile=prod%n%n"
|
||||
+ " Change database settings:%n%n"
|
||||
+ " $ ${PARENT-COMMAND-FULL-NAME:-$PARENTCOMMAND} ${COMMAND-NAME} --db=postgres [--db-url][--db-username][--db-password]%n%n"
|
||||
+ " Enable a feature:%n%n"
|
||||
+ " $ ${PARENT-COMMAND-FULL-NAME:-$PARENTCOMMAND} ${COMMAND-NAME} --features-<feature_name>=[enabled|disabled]%n%n"
|
||||
+ " Or alternatively, enable all tech preview features:%n%n"
|
||||
+ " $ ${PARENT-COMMAND-FULL-NAME:-$PARENTCOMMAND} ${COMMAND-NAME} --features=preview%n%n"
|
||||
+ " Enable metrics:%n%n"
|
||||
+ " $ ${PARENT-COMMAND-FULL-NAME:-$PARENTCOMMAND} ${COMMAND-NAME} --metrics-enabled=true%n%n"
|
||||
+ "You can also use the \"--auto-build\" option when starting the server to avoid running this command every time you change a configuration:%n%n"
|
||||
+ " $ ${PARENT-COMMAND-FULL-NAME:-$PARENTCOMMAND} --auto-build <OPTIONS>%n%n"
|
||||
+ "By doing that you have an additional overhead when the server is starting.%n%n",
|
||||
mixinStandardHelpOptions = true,
|
||||
usageHelpAutoWidth = true,
|
||||
optionListHeading = "%nOptions%n",
|
||||
parameterListHeading = "Available Commands%n")
|
||||
optionListHeading = "%nConfiguration Options%n%n")
|
||||
public final class Build extends AbstractCommand implements Runnable {
|
||||
|
||||
public static final String NAME = "build";
|
||||
|
@ -65,7 +84,7 @@ public final class Build extends AbstractCommand implements Runnable {
|
|||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||
|
||||
if (classLoader instanceof RunnerClassLoader) {
|
||||
RunnerClassLoader.class.cast(classLoader).resetInternalCaches();
|
||||
((RunnerClassLoader) classLoader).resetInternalCaches();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,13 +19,16 @@ package org.keycloak.quarkus.runtime.cli.command;
|
|||
|
||||
import picocli.AutoComplete;
|
||||
import picocli.CommandLine;
|
||||
import picocli.CommandLine.Command;
|
||||
|
||||
@CommandLine.Command(name = "completion", version = "generate-completion " + CommandLine.VERSION,
|
||||
@Command(name = "completion",
|
||||
version = "generate-completion " + CommandLine.VERSION,
|
||||
header = "Generate bash/zsh completion script for ${ROOT-COMMAND-NAME:-the root command of this command}.",
|
||||
helpCommand = false, headerHeading = "%n", commandListHeading = "%nCommands:%n",
|
||||
synopsisHeading = "%nUsage: ", optionListHeading = "Options:%n",
|
||||
description = {
|
||||
"Generate bash/zsh completion script for ${ROOT-COMMAND-NAME:-the root command of this command}.%nRun the following command to give `${ROOT-COMMAND-NAME:-$PARENTCOMMAND}` TAB completion in the current shell:",
|
||||
"Generate bash/zsh completion script for ${ROOT-COMMAND-NAME:-the root command of this command}.%n" +
|
||||
"Run the following command to give `${ROOT-COMMAND-NAME:-$PARENTCOMMAND}` TAB completion in the current shell:",
|
||||
"",
|
||||
" source <(${PARENT-COMMAND-FULL-NAME:-$PARENTCOMMAND} ${COMMAND-NAME})",
|
||||
""},
|
||||
|
|
|
@ -19,9 +19,10 @@ package org.keycloak.quarkus.runtime.cli.command;
|
|||
|
||||
import static org.keycloak.exportimport.ExportImportConfig.ACTION_EXPORT;
|
||||
|
||||
import picocli.CommandLine;
|
||||
import picocli.CommandLine.Command;
|
||||
import picocli.CommandLine.Option;
|
||||
|
||||
@CommandLine.Command(name = "export",
|
||||
@Command(name = "export",
|
||||
description = "Export data from realms to a file or directory.",
|
||||
mixinStandardHelpOptions = true,
|
||||
showDefaultValues = true,
|
||||
|
@ -29,8 +30,19 @@ import picocli.CommandLine;
|
|||
parameterListHeading = "Available Commands%n")
|
||||
public final class Export extends AbstractExportImportCommand implements Runnable {
|
||||
|
||||
@CommandLine.Option(names = "--users", arity = "1", description = "Set how users should be exported. Possible values are: skip, realm_file, same_file, different_files.", paramLabel = "<strategy>", defaultValue = "different_files") String users;
|
||||
@CommandLine.Option(names = "--users-per-file", arity = "1", description = "Set the number of users per file. It’s used only if --users=different_files.", paramLabel = "<number>", defaultValue = "50") Integer usersPerFile;
|
||||
@Option(names = "--users",
|
||||
arity = "1",
|
||||
description = "Set how users should be exported. Possible values are: skip, realm_file, same_file, different_files.",
|
||||
paramLabel = "<strategy>",
|
||||
defaultValue = "different_files")
|
||||
String users;
|
||||
|
||||
@Option(names = "--users-per-file",
|
||||
arity = "1",
|
||||
description = "Set the number of users per file. It’s used only if --users=different_files.",
|
||||
paramLabel = "<number>",
|
||||
defaultValue = "50")
|
||||
Integer usersPerFile;
|
||||
|
||||
public Export() {
|
||||
super(ACTION_EXPORT);
|
||||
|
|
|
@ -21,9 +21,10 @@ import static org.keycloak.exportimport.ExportImportConfig.ACTION_IMPORT;
|
|||
import static org.keycloak.exportimport.Strategy.IGNORE_EXISTING;
|
||||
import static org.keycloak.exportimport.Strategy.OVERWRITE_EXISTING;
|
||||
|
||||
import picocli.CommandLine;
|
||||
import picocli.CommandLine.Command;
|
||||
import picocli.CommandLine.Option;
|
||||
|
||||
@CommandLine.Command(name = "import",
|
||||
@Command(name = "import",
|
||||
description = "Import data from a directory or a file.",
|
||||
mixinStandardHelpOptions = true,
|
||||
showDefaultValues = true,
|
||||
|
@ -31,7 +32,12 @@ import picocli.CommandLine;
|
|||
parameterListHeading = "Available Commands%n")
|
||||
public final class Import extends AbstractExportImportCommand implements Runnable {
|
||||
|
||||
@CommandLine.Option(names = "--override", arity = "1", description = "Set if existing data should be skipped or overridden.", paramLabel = "false", defaultValue = "true") boolean override;
|
||||
@Option(names = "--override",
|
||||
arity = "1",
|
||||
description = "Set if existing data should be skipped or overridden.",
|
||||
paramLabel = "false",
|
||||
defaultValue = "true")
|
||||
boolean override;
|
||||
|
||||
public Import() {
|
||||
super(ACTION_IMPORT);
|
||||
|
|
|
@ -19,20 +19,32 @@ package org.keycloak.quarkus.runtime.cli.command;
|
|||
|
||||
import org.keycloak.quarkus.runtime.configuration.KeycloakConfigSourceProvider;
|
||||
|
||||
import picocli.CommandLine;
|
||||
import picocli.CommandLine.Command;
|
||||
import picocli.CommandLine.Option;
|
||||
import picocli.CommandLine.ScopeType;
|
||||
|
||||
@Command(name = "keycloak",
|
||||
usageHelpWidth = 150,
|
||||
header = {
|
||||
"Keycloak - Open Source Identity and Access Management",
|
||||
"",
|
||||
"Find more information at: https://www.keycloak.org/",
|
||||
"Find more information at: https://www.keycloak.org/docs/latest",
|
||||
""
|
||||
},
|
||||
description = "Use this command-line tool to manage your Keycloak cluster%n", footerHeading = "%nUse \"${COMMAND-NAME} <command> --help\" for more information about a command.",
|
||||
footer = "%nby Red Hat",
|
||||
description = "%nUse this command-line tool to manage your Keycloak cluster.%n",
|
||||
footerHeading = "%nExamples:%n%n"
|
||||
+ " Start the server in development mode for local development or testing:%n%n"
|
||||
+ " $ ${COMMAND-NAME} start-dev%n%n"
|
||||
+ " Building an optimized server runtime:%n%n"
|
||||
+ " $ ${COMMAND-NAME} build <OPTIONS>%n%n"
|
||||
+ " Start the server in production mode:%n%n"
|
||||
+ " $ ${COMMAND-NAME} <OPTIONS>%n%n"
|
||||
+ " Please, take a look at the documentation for more details before deploying in production.%n",
|
||||
footer = {
|
||||
"",
|
||||
"Use \"${COMMAND-NAME} start --help\" for the available options when starting the server.",
|
||||
"Use \"${COMMAND-NAME} <command> --help\" for more information about other commands.",
|
||||
"",
|
||||
"by Red Hat" },
|
||||
optionListHeading = "Configuration Options%n%n",
|
||||
commandListHeading = "%nCommands%n%n",
|
||||
version = {
|
||||
|
@ -48,25 +60,35 @@ import picocli.CommandLine.Option;
|
|||
Import.class,
|
||||
ShowConfig.class,
|
||||
Tools.class
|
||||
}
|
||||
)
|
||||
})
|
||||
public final class Main {
|
||||
|
||||
@CommandLine.Option(names = "-D<key>=<value>", description = "Set a Java system property", scope = CommandLine.ScopeType.INHERIT, order = 0)
|
||||
@Option(names = "-D<key>=<value>",
|
||||
description = "Set a Java system property",
|
||||
scope = ScopeType.INHERIT,
|
||||
order = 0)
|
||||
Boolean sysProps;
|
||||
|
||||
@Option(names = { "-h", "--help" }, description = "This help message.", usageHelp = true)
|
||||
@Option(names = { "-h", "--help" },
|
||||
description = "This help message.",
|
||||
usageHelp = true)
|
||||
boolean help;
|
||||
|
||||
@Option(names = { "-V", "--version" }, description = "Show version information", versionHelp = true)
|
||||
@Option(names = { "-V", "--version" },
|
||||
description = "Show version information",
|
||||
versionHelp = true)
|
||||
boolean version;
|
||||
|
||||
@Option(names = { "-v", "--verbose" },
|
||||
description = "Print out more details when running this command. Useful for troubleshooting if some unexpected error occurs.", required = false,
|
||||
scope = CommandLine.ScopeType.INHERIT)
|
||||
description = "Print out more details when running this command. Useful for troubleshooting if some unexpected error occurs.",
|
||||
required = false,
|
||||
scope = ScopeType.INHERIT)
|
||||
Boolean verbose;
|
||||
|
||||
@Option(names = {"-cf", "--config-file"}, arity = "1", description = "Set the path to a configuration file.", paramLabel = "<path>")
|
||||
@Option(names = { "-cf", "--config-file" },
|
||||
arity = "1",
|
||||
description = "Set the path to a configuration file. By default, configuration properties are read from the \"keycloak.properties\" file in the \"conf\" directory.",
|
||||
paramLabel = "<path>")
|
||||
public void setConfigFile(String path) {
|
||||
System.setProperty(KeycloakConfigSourceProvider.KEYCLOAK_CONFIG_FILE_PROP, path);
|
||||
}
|
||||
|
|
|
@ -31,22 +31,28 @@ import java.util.function.BiConsumer;
|
|||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import org.keycloak.quarkus.runtime.configuration.MicroProfileConfigProvider;
|
||||
import org.keycloak.quarkus.runtime.configuration.PersistedConfigSource;
|
||||
import org.keycloak.quarkus.runtime.configuration.PropertyMappers;
|
||||
import org.keycloak.quarkus.runtime.Environment;
|
||||
|
||||
import io.smallrye.config.ConfigValue;
|
||||
import picocli.CommandLine;
|
||||
import picocli.CommandLine.Command;
|
||||
import picocli.CommandLine.Parameters;
|
||||
|
||||
@CommandLine.Command(name = "show-config",
|
||||
@Command(name = "show-config",
|
||||
description = "Print out the current configuration.",
|
||||
mixinStandardHelpOptions = true,
|
||||
optionListHeading = "%nOptions%n",
|
||||
parameterListHeading = "Available Commands%n")
|
||||
public final class ShowConfig extends AbstractCommand implements Runnable {
|
||||
|
||||
@CommandLine.Parameters(paramLabel = "filter", defaultValue = "none", description = "Show all configuration options. Use 'all' to show all options.") String filter;
|
||||
@Parameters(
|
||||
paramLabel = "filter",
|
||||
defaultValue = "none",
|
||||
description = "Show all configuration options. Use 'all' to show all options.")
|
||||
String filter;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
|
@ -101,7 +107,7 @@ public final class ShowConfig extends AbstractCommand implements Runnable {
|
|||
.forEach((p, properties1) -> {
|
||||
if (p.equals(profile)) {
|
||||
spec.commandLine().getOut().printf("Profile \"%s\" Configuration (%s):%n", p,
|
||||
p.equals(profile) ? "current" : "");
|
||||
"current");
|
||||
} else {
|
||||
spec.commandLine().getOut().printf("Profile \"%s\" Configuration:%n", p);
|
||||
}
|
||||
|
|
|
@ -17,14 +17,18 @@
|
|||
|
||||
package org.keycloak.quarkus.runtime.cli.command;
|
||||
|
||||
import picocli.CommandLine;
|
||||
import picocli.CommandLine.Command;
|
||||
|
||||
@CommandLine.Command(name = Start.NAME,
|
||||
description = "Start the server.",
|
||||
mixinStandardHelpOptions = true,
|
||||
usageHelpAutoWidth = true,
|
||||
optionListHeading = "%nOptions%n",
|
||||
parameterListHeading = "Available Commands%n")
|
||||
@Command(name = Start.NAME,
|
||||
header = "Start the server.",
|
||||
description = {
|
||||
"%nUse this command to run the server in production."
|
||||
},
|
||||
footerHeading = "%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"
|
||||
+ "By doing that you have an additional overhead when the server is starting. Run \"${PARENT-COMMAND-FULL-NAME:-$PARENTCOMMAND} build -h\" for more details.%n%n",
|
||||
optionListHeading = "%nConfiguration Options%n%n",
|
||||
mixinStandardHelpOptions = true)
|
||||
public final class Start extends AbstractStartCommand implements Runnable {
|
||||
|
||||
public static final String NAME = "start";
|
||||
|
|
|
@ -19,13 +19,16 @@ package org.keycloak.quarkus.runtime.cli.command;
|
|||
|
||||
import org.keycloak.quarkus.runtime.Environment;
|
||||
|
||||
import picocli.CommandLine;
|
||||
import picocli.CommandLine.Command;
|
||||
|
||||
@CommandLine.Command(name = StartDev.NAME,
|
||||
description = "Start the server in development mode.",
|
||||
mixinStandardHelpOptions = true,
|
||||
optionListHeading = "%nOptions%n",
|
||||
parameterListHeading = "Available Commands%n")
|
||||
@Command(name = StartDev.NAME,
|
||||
header = "Start the server in development mode.",
|
||||
description = {
|
||||
"%nUse this command if you want to run the server locally for development or testing purposes.",
|
||||
},
|
||||
footerHeading = "%nDo NOT start the server using this command when deploying to production.%n%n",
|
||||
optionListHeading = "%nConfiguration Options%n%n",
|
||||
mixinStandardHelpOptions = true)
|
||||
public final class StartDev extends AbstractStartCommand implements Runnable {
|
||||
|
||||
public static final String NAME = "start-dev";
|
||||
|
@ -33,6 +36,5 @@ public final class StartDev extends AbstractStartCommand implements Runnable {
|
|||
@Override
|
||||
protected void doBeforeRun() {
|
||||
Environment.forceDevProfile();
|
||||
spec.commandLine().getOut().printf("Running the server in dev mode. DO NOT run the '%s' command in production.%n", NAME);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,10 +17,10 @@
|
|||
|
||||
package org.keycloak.quarkus.runtime.cli.command;
|
||||
|
||||
import picocli.CommandLine;
|
||||
import picocli.CommandLine.Command;
|
||||
|
||||
@CommandLine.Command(name = "tools",
|
||||
description = "Provides utilities for using and interacting with the server.",
|
||||
@Command(name = "tools",
|
||||
description = "Utilities for use and interaction with the server.",
|
||||
mixinStandardHelpOptions = true,
|
||||
optionListHeading = "%nOptions%n",
|
||||
parameterListHeading = "Available Commands%n",
|
||||
|
|
|
@ -29,10 +29,10 @@ public final class Messages {
|
|||
return new IllegalArgumentException("Invalid value [" + mode + "] for configuration property [proxy].");
|
||||
}
|
||||
|
||||
static IllegalStateException httpsConfigurationNotSet() {
|
||||
StringBuilder builder = new StringBuilder("Key material not provided to setup HTTPS. Please configure your keys/certificates or enable HTTP");
|
||||
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())) {
|
||||
builder.append(" or start the server using the 'dev' profile");
|
||||
builder.append(" or start the server in development mode");
|
||||
}
|
||||
builder.append(".");
|
||||
return new IllegalStateException(builder.toString());
|
||||
|
|
|
@ -12,6 +12,20 @@ metrics.enabled=false
|
|||
# Themes
|
||||
spi.theme.folder.dir=${kc.home.dir:}/themes
|
||||
|
||||
# Basic settings for running in production. Change accordingly before deploying the server.
|
||||
# Database
|
||||
#%prod.db=postgres
|
||||
#%prod.db.username=keycloak
|
||||
#%prod.db.password=password
|
||||
#%prod.db.url=jdbc:postgresql://localhost/keycloak
|
||||
# Observability
|
||||
#%prod.metrics.enabled=true
|
||||
# HTTP
|
||||
#%prod.spi.hostname.frontend-url=https://localhost:8443
|
||||
#%prod.https.certificate.file=${kc.home.dir}conf/server.crt.pem
|
||||
#%prod.https.certificate.key-file=${kc.home.dir}conf/server.key.pem
|
||||
#%prod.proxy=reencrypt
|
||||
|
||||
# Default, and insecure, and non-production grade configuration for the development profile
|
||||
%dev.http.enabled=true
|
||||
%dev.cluster=local
|
||||
|
|
Loading…
Reference in a new issue