KEYCLOAK-16063 Messed output when running './kc.sh'

This commit is contained in:
mposolda 2020-10-23 18:33:52 +02:00 committed by Pedro Igor
parent 9058cc75d9
commit c98048bf64
6 changed files with 153 additions and 18 deletions

View file

@ -23,15 +23,20 @@ import java.util.Iterator;
import java.util.List;
import java.util.function.IntFunction;
import io.quarkus.runtime.Quarkus;
import org.jboss.logging.Logger;
import org.keycloak.common.Profile;
import org.keycloak.configuration.PropertyMapper;
import org.keycloak.configuration.PropertyMappers;
import org.keycloak.platform.Platform;
import org.keycloak.provider.quarkus.QuarkusConfigurationException;
import org.keycloak.provider.quarkus.QuarkusPlatform;
import org.keycloak.util.Environment;
import picocli.CommandLine;
final class Picocli {
private static final Logger logger = Logger.getLogger(Picocli.class);
static CommandLine createCommandLine() {
CommandLine.Model.CommandSpec spec = CommandLine.Model.CommandSpec.forAnnotatedObject(new MainCommand())
.name(Environment.getCommand());
@ -126,22 +131,30 @@ final class Picocli {
static void error(CommandLine cmd, String message, Throwable throwable) {
List<String> cliArgs = getCliArgs(cmd);
cmd.getErr().println("ERROR: " + message);
logError(cmd, "ERROR: " + message);
if (throwable != null) {
Throwable cause = throwable;
boolean verbose = cliArgs.stream().anyMatch((arg) -> "--verbose".equals(arg));
do {
if (cause.getMessage() != null) {
cmd.getErr().println(String.format("ERROR: %s", cause.getMessage()));
if (throwable instanceof QuarkusConfigurationException) {
QuarkusConfigurationException quarkusConfigException = (QuarkusConfigurationException) throwable;
if (quarkusConfigException.getSuppressed() == null || quarkusConfigException.getSuppressed().length == 0) {
dumpException(cmd, quarkusConfigException, verbose);
} else if (quarkusConfigException.getSuppressed().length == 1) {
dumpException(cmd, quarkusConfigException.getSuppressed()[0], verbose);
} else {
logError(cmd, "ERROR: Multiple configuration errors during startup");
int counter = 0;
for (Throwable inner : quarkusConfigException.getSuppressed()) {
counter++;
logError(cmd, "ERROR " + counter);
dumpException(cmd, inner, verbose);
}
}
} while ((cause = cause.getCause())!= null);
}
if (cliArgs.stream().anyMatch((arg) -> "--verbose".equals(arg))) {
cmd.getErr().println("ERROR: Details:");
throwable.printStackTrace();
} else {
cmd.getErr().println("For more details run the same command passing the '--verbose' option.");
if (!verbose) {
logError(cmd, "For more details run the same command passing the '--verbose' option. Also you can use '--help' to see the details about the usage of the particular command.");
}
}
@ -151,4 +164,40 @@ final class Picocli {
static void println(CommandLine cmd, String message) {
cmd.getOut().println(message);
}
private static void dumpException(CommandLine cmd, Throwable cause, boolean verbose) {
if (verbose) {
logError(cmd, "ERROR: Details:", cause);
} else {
do {
if (cause.getMessage() != null) {
logError(cmd, String.format("ERROR: %s", cause.getMessage()));
}
} while ((cause = cause.getCause())!= null);
}
}
private static void logError(CommandLine cmd, String errorMessage) {
logError(cmd, errorMessage, null);
}
// The "cause" can be null
private static void logError(CommandLine cmd, String errorMessage, Throwable cause) {
QuarkusPlatform platform = (QuarkusPlatform) Platform.getPlatform();
if (platform.isStarted()) {
// Can delegate to proper logger once the platform is started
if (cause == null) {
logger.error(errorMessage);
} else {
logger.error(errorMessage, cause);
}
} else {
if (cause == null) {
cmd.getErr().println(errorMessage);
} else {
cmd.getErr().println(errorMessage);
cause.printStackTrace();
}
}
}
}

View file

@ -17,7 +17,7 @@
package org.keycloak.configuration;
import java.util.List;
import org.keycloak.util.Environment;
public final class Messages {
@ -30,6 +30,11 @@ public final class Messages {
}
static IllegalStateException httpsConfigurationNotSet() {
return new IllegalStateException("Key material not provided to setup HTTPS. Please configure your keys/certificates or enable HTTP.");
StringBuilder builder = new StringBuilder("Key material not provided to setup HTTPS. Please configure your keys/certificates or enable HTTP");
if (!"dev".equals(Environment.getProfile())) {
builder.append(" or start the server using the 'dev' profile");
}
builder.append(".");
return new IllegalStateException(builder.toString());
}
}

View file

@ -32,6 +32,8 @@ import java.util.stream.Collectors;
import io.quarkus.runtime.configuration.ProfileManager;
import io.smallrye.config.ConfigSourceInterceptorContext;
import io.smallrye.config.ConfigValue;
import org.keycloak.platform.Platform;
import org.keycloak.provider.quarkus.QuarkusPlatform;
import org.keycloak.util.Environment;
/**
@ -65,7 +67,7 @@ public final class PropertyMappers {
}
if (proceed == null || proceed.getValue() == null) {
throw Messages.httpsConfigurationNotSet();
addDeferredConfigurationException(Messages.httpsConfigurationNotSet());
}
}
@ -112,7 +114,8 @@ public final class PropertyMappers {
case "passthrough":
return "true";
}
throw Messages.invalidProxyMode(mode);
addDeferredConfigurationException(Messages.invalidProxyMode(mode));
return "false";
}, "The proxy mode if the server is behind a reverse proxy. Possible values are: none, edge, reencrypt, and passthrough.");
}
@ -164,7 +167,8 @@ public final class PropertyMappers {
case "postgres-10":
return "postgresql";
}
throw invalidDatabaseVendor(db, "h2-file", "h2-mem", "mariadb", "mysql", "postgres", "postgres-95", "postgres-10");
addDeferredConfigurationException(invalidDatabaseVendor(db, "h2-file", "h2-mem", "mariadb", "mysql", "postgres", "postgres-95", "postgres-10"));
return "h2";
}, "The database vendor. Possible values are: h2-mem, h2-file, mariadb, mysql, postgres95, postgres10.");
create("db", "quarkus.datasource.jdbc.transactions", (db, context) -> "xa", null);
create("db.url", "db", "quarkus.datasource.jdbc.url", (value, context) -> {
@ -264,4 +268,10 @@ public final class PropertyMappers {
}
}).findFirst().orElse(null);
}
// Exception to be thrown later, so that we can properly log it and collect all possible configuration issues (not just the first one)
private static void addDeferredConfigurationException(Exception e) {
QuarkusPlatform quarkusPlatform = (QuarkusPlatform) Platform.getPlatform();
quarkusPlatform.addDeferredException(e);
}
}

View file

@ -0,0 +1,28 @@
/*
* Copyright 2020 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.provider.quarkus;
/**
* Exception thrown when some error happens during initialization of Quarkus platform. Usually due the incorrect configuration of basic stuff (DB, HTTP protocol etc)
* This exception is supposed to be thrown when Quarkus platform is started (See {@link io.quarkus.runtime.StartupEvent}
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class QuarkusConfigurationException extends RuntimeException {
}

View file

@ -38,7 +38,19 @@ public class QuarkusLifecycleObserver {
private static final String KEYCLOAK_ADMIN_PASSWORD_ENV_VAR = "KEYCLOAK_ADMIN_PASSWORD";
void onStartupEvent(@Observes StartupEvent event) {
Runnable startupHook = ((QuarkusPlatform) Platform.getPlatform()).startupHook;
QuarkusPlatform platform = (QuarkusPlatform) Platform.getPlatform();
platform.started();
// Check if we had any exceptions during configuration phase
if (!platform.getDeferredExceptions().isEmpty()) {
QuarkusConfigurationException quarkusException = new QuarkusConfigurationException();
for (Throwable inner : platform.getDeferredExceptions()) {
quarkusException.addSuppressed(inner);
}
throw quarkusException;
}
Runnable startupHook = platform.startupHook;
if (startupHook != null) {
startupHook.run();

View file

@ -17,6 +17,10 @@
package org.keycloak.provider.quarkus;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
import org.keycloak.platform.PlatformProvider;
public class QuarkusPlatform implements PlatformProvider {
@ -24,6 +28,9 @@ public class QuarkusPlatform implements PlatformProvider {
Runnable startupHook;
Runnable shutdownHook;
private AtomicBoolean started = new AtomicBoolean(false);
private List<Throwable> deferredExceptions = new CopyOnWriteArrayList<>();
@Override
public void onStartup(Runnable startupHook) {
this.startupHook = startupHook;
@ -39,4 +46,28 @@ public class QuarkusPlatform implements PlatformProvider {
throw new RuntimeException(cause);
}
/**
* Called when Quarkus platform is started
*/
public void started() {
this.started.set(true);
}
public boolean isStarted() {
return started.get();
}
/**
* Add the exception, which won't be thrown right-away, but should be thrown later after QuarkusPlatform is initialized (including proper logging)
*
* @param t
*/
public void addDeferredException(Throwable t) {
deferredExceptions.add(t);
}
public List<Throwable> getDeferredExceptions() {
return deferredExceptions;
}
}