[KEYCLOAK-19311] - Baseline for Dist.X tests
This commit is contained in:
parent
1adce39e1d
commit
e1916fbdb1
41 changed files with 1552 additions and 190 deletions
46
.github/workflows/ci.yml
vendored
46
.github/workflows/ci.yml
vendored
|
@ -373,6 +373,52 @@ jobs:
|
|||
path: reports-quarkus-cluster-tests.zip
|
||||
if-no-files-found: ignore
|
||||
|
||||
### Tests: Quarkus distribution
|
||||
|
||||
quarkus-tests:
|
||||
name: Quarkus Tests
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
MAVEN_OPTS: -Xmx1024m
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Cache Maven packages
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.m2/repository
|
||||
key: cache-1-${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
|
||||
restore-keys: cache-1-${{ runner.os }}-m2
|
||||
- name: Cleanup org.keycloak artifacts
|
||||
run: rm -rf ~/.m2/repository/org/keycloak >/dev/null || true
|
||||
|
||||
- name: Download built keycloak
|
||||
id: download-keycloak
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
path: ~/.m2/repository/org/keycloak/
|
||||
name: keycloak-artifacts.zip
|
||||
- uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: ${{ env.DEFAULT_JDK_VERSION }}
|
||||
- name: Update maven settings
|
||||
run: mkdir -p ~/.m2 ; cp .github/settings.xml ~/.m2/
|
||||
- name: Run Quarkus Tests
|
||||
run: |
|
||||
mvn clean install -nsu -B -f quarkus/tests/pom.xml | misc/log/trimmer.sh
|
||||
TEST_RESULT=${PIPESTATUS[0]}
|
||||
find . -path '*/target/surefire-reports/*.xml' | zip -q reports-quarkus-tests.zip -@
|
||||
exit $TEST_RESULT
|
||||
|
||||
- name: Quarkus test reports
|
||||
uses: actions/upload-artifact@v2
|
||||
if: failure()
|
||||
with:
|
||||
name: reports-quarkus-tests
|
||||
retention-days: 14
|
||||
path: reports-quarkus-tests.zip
|
||||
if-no-files-found: ignore
|
||||
|
||||
quickstarts-tests:
|
||||
name: Quickstarts Tests
|
||||
needs: build
|
||||
|
|
|
@ -102,7 +102,7 @@
|
|||
<outputDirectory>conf</outputDirectory>
|
||||
</file>
|
||||
<file>
|
||||
<source>target/keycloak-quarkus-server-app/META-INF/keycloak.properties</source>
|
||||
<source>../../quarkus/runtime/src/main/resources/META-INF/keycloak.properties</source>
|
||||
<outputDirectory>conf</outputDirectory>
|
||||
</file>
|
||||
</files>
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
package test.org.keycloak.quarkus;
|
||||
|
||||
import org.hamcrest.Matchers;
|
||||
import org.jboss.shrinkwrap.api.ShrinkWrap;
|
||||
import org.jboss.shrinkwrap.api.spec.JavaArchive;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
import io.quarkus.test.QuarkusUnitTest;
|
||||
import io.restassured.RestAssured;
|
||||
|
||||
public class StartupTest {
|
||||
|
||||
@RegisterExtension
|
||||
static final QuarkusUnitTest test = new QuarkusUnitTest()
|
||||
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
|
||||
.addAsResource("application.properties", "application.properties")
|
||||
.addAsResource("keycloak.properties", "META-INF/keycloak.properties"));
|
||||
|
||||
@Test
|
||||
public void testWelcomePage() throws InterruptedException {
|
||||
RestAssured.given()
|
||||
.when().get("/")
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.body(Matchers.containsString("Please create an initial admin user to get started"));
|
||||
}
|
||||
}
|
|
@ -36,7 +36,6 @@ public class KeycloakNegativeHealthCheckTest {
|
|||
@RegisterExtension
|
||||
static final QuarkusUnitTest test = new QuarkusUnitTest()
|
||||
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
|
||||
.addAsResource("application.properties", "application.properties")
|
||||
.addAsResource("keycloak.properties", "META-INF/keycloak.properties"));
|
||||
|
||||
@Test
|
||||
|
|
|
@ -33,7 +33,6 @@ public class KeycloakReadyHealthCheckTest {
|
|||
@RegisterExtension
|
||||
static final QuarkusUnitTest test = new QuarkusUnitTest()
|
||||
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
|
||||
.addAsResource("application.properties", "application.properties")
|
||||
.addAsResource("keycloak.properties", "META-INF/keycloak.properties"));
|
||||
|
||||
@Test
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
quarkus.http.root-path=/
|
||||
quarkus.application.name=Keycloak
|
||||
quarkus.banner.enabled=false
|
||||
|
||||
quarkus.resteasy.ignore-application-classes=true
|
||||
quarkus.arc.ignored-split-packages=org.keycloak.*
|
||||
|
||||
# we do not want running testcontainers when running tests in this module
|
||||
quarkus.devservices.enabled=false
|
|
@ -159,6 +159,7 @@
|
|||
<module>runtime</module>
|
||||
<module>deployment</module>
|
||||
<module>server</module>
|
||||
<module>tests</module>
|
||||
</modules>
|
||||
|
||||
<repositories>
|
||||
|
|
|
@ -41,6 +41,7 @@ public final class Environment {
|
|||
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";
|
||||
public static final String LAUNCH_MODE = "kc.launch.mode";
|
||||
|
||||
private Environment() {}
|
||||
|
||||
|
@ -90,6 +91,7 @@ public final class Environment {
|
|||
if (isWindows()) {
|
||||
return "kc.bat";
|
||||
}
|
||||
|
||||
return "kc.sh";
|
||||
}
|
||||
|
||||
|
@ -133,6 +135,10 @@ public final class Environment {
|
|||
public static void setProfile(String profile) {
|
||||
System.setProperty(PROFILE, profile);
|
||||
System.setProperty("quarkus.profile", profile);
|
||||
if (isTestLaunchMode()) {
|
||||
System.setProperty("mp.config.profile", profile);
|
||||
System.setProperty(ProfileManager.QUARKUS_TEST_PROFILE_PROP, profile);
|
||||
}
|
||||
}
|
||||
|
||||
public static String getProfileOrDefault(String defaultProfile) {
|
||||
|
@ -193,4 +199,16 @@ public final class Environment {
|
|||
public static boolean isQuarkusDevMode() {
|
||||
return ProfileManager.getLaunchMode().equals(LaunchMode.DEVELOPMENT);
|
||||
}
|
||||
|
||||
public static boolean isTestLaunchMode() {
|
||||
return "test".equals(System.getProperty(LAUNCH_MODE));
|
||||
}
|
||||
|
||||
public static void forceTestLaunchMode() {
|
||||
System.setProperty(LAUNCH_MODE, "test");
|
||||
}
|
||||
|
||||
public static boolean isDistribution() {
|
||||
return Environment.getCommand().startsWith("kc.");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,12 +19,15 @@ package org.keycloak.quarkus.runtime;
|
|||
|
||||
import static org.keycloak.quarkus.runtime.Environment.isDevProfile;
|
||||
import static org.keycloak.quarkus.runtime.Environment.getProfileOrDefault;
|
||||
import static org.keycloak.quarkus.runtime.Environment.isTestLaunchMode;
|
||||
import static org.keycloak.quarkus.runtime.cli.Picocli.parseAndRun;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import javax.enterprise.context.ApplicationScoped;
|
||||
|
||||
import io.quarkus.runtime.ApplicationLifecycleManager;
|
||||
import io.quarkus.runtime.Quarkus;
|
||||
|
||||
|
@ -41,6 +44,7 @@ import picocli.CommandLine;
|
|||
* <p>The main entry point, responsible for initialize and run the CLI as well as start the server.
|
||||
*/
|
||||
@QuarkusMain(name = "keycloak")
|
||||
@ApplicationScoped
|
||||
public class KeycloakMain implements QuarkusApplication {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(KeycloakMain.class);
|
||||
|
@ -61,7 +65,7 @@ public class KeycloakMain implements QuarkusApplication {
|
|||
|
||||
public static void start(CommandLine cmd) {
|
||||
try {
|
||||
Quarkus.run(KeycloakMain.class, (integer, cause) -> {
|
||||
Quarkus.run(KeycloakMain.class, (exitCode, cause) -> {
|
||||
if (cause != null) {
|
||||
ExecutionExceptionHandler exceptionHandler = (ExecutionExceptionHandler) cmd.getExecutionExceptionHandler();
|
||||
|
||||
|
@ -69,6 +73,12 @@ public class KeycloakMain implements QuarkusApplication {
|
|||
String.format("Failed to start server using profile (%s)", getProfileOrDefault("prod")),
|
||||
cause.getCause());
|
||||
}
|
||||
|
||||
if (Environment.isDistribution()) {
|
||||
// assume that it is running the distribution
|
||||
// as we are replacing the default exit handler, we need to force exit
|
||||
System.exit(exitCode);
|
||||
}
|
||||
});
|
||||
} catch (Throwable cause) {
|
||||
ExecutionExceptionHandler exceptionHandler = (ExecutionExceptionHandler) cmd.getExecutionExceptionHandler();
|
||||
|
@ -87,7 +97,17 @@ public class KeycloakMain implements QuarkusApplication {
|
|||
if (isDevProfile()) {
|
||||
LOGGER.warnf("Running the server in dev mode. DO NOT use this configuration in production.");
|
||||
}
|
||||
Quarkus.waitForExit();
|
||||
return ApplicationLifecycleManager.getExitCode();
|
||||
|
||||
int exitCode = ApplicationLifecycleManager.getExitCode();
|
||||
|
||||
if (isTestLaunchMode()) {
|
||||
// in test mode we exit immediately
|
||||
// we should be managing this behavior more dynamically depending on the tests requirements (short/long lived)
|
||||
Quarkus.asyncExit(exitCode);
|
||||
} else {
|
||||
Quarkus.waitForExit();
|
||||
}
|
||||
|
||||
return exitCode;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,7 +29,6 @@ import org.keycloak.quarkus.runtime.InitializationException;
|
|||
import org.keycloak.quarkus.runtime.Messages;
|
||||
import org.keycloak.quarkus.runtime.integration.QuarkusPlatform;
|
||||
|
||||
import io.quarkus.runtime.Quarkus;
|
||||
import io.smallrye.config.ConfigValue;
|
||||
import picocli.CommandLine;
|
||||
import picocli.CommandLine.ParseResult;
|
||||
|
@ -80,8 +79,6 @@ public final class ExecutionExceptionHandler implements CommandLine.IExecutionEx
|
|||
logError(errorWriter, "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.");
|
||||
}
|
||||
}
|
||||
|
||||
Quarkus.asyncExit(1);
|
||||
}
|
||||
|
||||
private void dumpException(PrintWriter errorWriter, Throwable cause) {
|
||||
|
|
|
@ -55,6 +55,7 @@ import org.keycloak.quarkus.runtime.configuration.KeycloakConfigSourceProvider;
|
|||
import org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper;
|
||||
import org.keycloak.quarkus.runtime.Environment;
|
||||
|
||||
import io.quarkus.runtime.Quarkus;
|
||||
import io.smallrye.config.ConfigValue;
|
||||
import picocli.CommandLine;
|
||||
import picocli.CommandLine.Model.CommandSpec;
|
||||
|
@ -80,14 +81,7 @@ public final class Picocli {
|
|||
|
||||
runReAugmentationIfNeeded(cliArgs, cmd);
|
||||
|
||||
int exitCode = cmd.execute(cliArgs.toArray(new String[0]));
|
||||
|
||||
if (Environment.isQuarkusDevMode()) {
|
||||
// do not exit if running in dev mode, otherwise quarkus dev mode will exit when running from IDE
|
||||
return;
|
||||
}
|
||||
|
||||
System.exit(exitCode);
|
||||
cmd.execute(cliArgs.toArray(new String[0]));
|
||||
}
|
||||
|
||||
private static void runReAugmentationIfNeeded(List<String> cliArgs, CommandLine cmd) {
|
||||
|
@ -246,7 +240,7 @@ public final class Picocli {
|
|||
return key.startsWith("kc.provider.file");
|
||||
}
|
||||
|
||||
private static CommandLine createCommandLine(List<String> cliArgs) {
|
||||
public static CommandLine createCommandLine(List<String> cliArgs) {
|
||||
CommandSpec spec = CommandSpec.forAnnotatedObject(new Main())
|
||||
.name(Environment.getCommand());
|
||||
|
||||
|
|
|
@ -30,6 +30,8 @@ import io.quarkus.runtime.configuration.ProfileManager;
|
|||
import picocli.CommandLine.Command;
|
||||
import picocli.CommandLine.Mixin;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.List;
|
||||
|
||||
@Command(name = Build.NAME,
|
||||
|
@ -115,7 +117,11 @@ public final class Build extends AbstractCommand implements Runnable {
|
|||
private void cleanTempResources() {
|
||||
if (!ProfileManager.getLaunchMode().isDevOrTest()) {
|
||||
// only needed for dev/testing purposes
|
||||
getHomePath().resolve("quarkus-artifact.properties").toFile().delete();
|
||||
try {
|
||||
Files.delete(getHomePath().resolve("quarkus-artifact.properties"));
|
||||
} catch (IOException cause) {
|
||||
throw new RuntimeException("Failed to delete temporary resources", cause);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ import static org.keycloak.quarkus.runtime.cli.Picocli.NO_PARAM_LABEL;
|
|||
import org.keycloak.quarkus.runtime.Environment;
|
||||
import org.keycloak.quarkus.runtime.cli.ExecutionExceptionHandler;
|
||||
import org.keycloak.quarkus.runtime.configuration.KeycloakConfigSourceProvider;
|
||||
import org.keycloak.quarkus.runtime.configuration.KeycloakPropertiesConfigSource;
|
||||
|
||||
import picocli.CommandLine;
|
||||
import picocli.CommandLine.Command;
|
||||
|
@ -104,6 +105,6 @@ public final class Main {
|
|||
description = "Set the path to a configuration file. By default, configuration properties are read from the \"keycloak.properties\" file in the \"conf\" directory.",
|
||||
paramLabel = "file")
|
||||
public void setConfigFile(String path) {
|
||||
System.setProperty(KeycloakConfigSourceProvider.KEYCLOAK_CONFIG_FILE_PROP, path);
|
||||
System.setProperty(KeycloakPropertiesConfigSource.KEYCLOAK_CONFIG_FILE_PROP, path);
|
||||
}
|
||||
}
|
|
@ -36,6 +36,7 @@ import org.keycloak.quarkus.runtime.configuration.MicroProfileConfigProvider;
|
|||
import org.keycloak.quarkus.runtime.configuration.PersistedConfigSource;
|
||||
import org.keycloak.quarkus.runtime.Environment;
|
||||
|
||||
import io.quarkus.runtime.Quarkus;
|
||||
import io.smallrye.config.ConfigValue;
|
||||
import picocli.CommandLine.Command;
|
||||
import picocli.CommandLine.Parameters;
|
||||
|
@ -70,8 +71,8 @@ public final class ShowConfig extends AbstractCommand implements Runnable {
|
|||
.forEachOrdered(this::printProperty);
|
||||
}
|
||||
|
||||
if (!parseBoolean(System.getProperty("kc.show.config.runtime", Boolean.FALSE.toString()))) {
|
||||
System.exit(0);
|
||||
if (!Boolean.getBoolean("kc.show.config.runtime")) {
|
||||
Quarkus.asyncExit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
package org.keycloak.quarkus.runtime.configuration;
|
||||
|
||||
import static org.keycloak.quarkus.runtime.Environment.isTestLaunchMode;
|
||||
import static org.keycloak.quarkus.runtime.cli.Picocli.ARG_KEY_VALUE_SPLIT;
|
||||
import static org.keycloak.quarkus.runtime.cli.Picocli.ARG_PREFIX;
|
||||
import static org.keycloak.quarkus.runtime.cli.Picocli.ARG_SPLIT;
|
||||
|
@ -48,20 +49,29 @@ public class ConfigArgsConfigSource extends PropertiesConfigSource {
|
|||
|
||||
private static final Logger log = Logger.getLogger(ConfigArgsConfigSource.class);
|
||||
|
||||
private final boolean alwaysParseArgs;
|
||||
|
||||
ConfigArgsConfigSource() {
|
||||
// higher priority over default Quarkus config sources
|
||||
super(parseArgument(), "CliConfigSource", 500);
|
||||
alwaysParseArgs = isTestLaunchMode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue(String propertyName) {
|
||||
String value = super.getValue(propertyName);
|
||||
Map<String, String> properties = getProperties();
|
||||
|
||||
if (alwaysParseArgs) {
|
||||
properties = parseArgument();
|
||||
}
|
||||
|
||||
String value = properties.get(propertyName);
|
||||
|
||||
if (value != null) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return super.getValue(propertyName.replace('-', '.'));
|
||||
return properties.get(propertyName.replace('-', '.'));
|
||||
}
|
||||
|
||||
private static Map<String, String> parseArgument() {
|
||||
|
|
|
@ -17,9 +17,7 @@
|
|||
|
||||
package org.keycloak.quarkus.runtime.configuration;
|
||||
|
||||
import static org.keycloak.quarkus.runtime.configuration.MicroProfileConfigProvider.NS_KEYCLOAK_PREFIX;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
|
@ -34,8 +32,6 @@ public class KeycloakConfigSourceProvider implements ConfigSourceProvider {
|
|||
|
||||
private static final Logger log = Logger.getLogger(KeycloakConfigSourceProvider.class);
|
||||
|
||||
public static final String KEYCLOAK_CONFIG_FILE_ENV = "KC_CONFIG_FILE";
|
||||
public static final String KEYCLOAK_CONFIG_FILE_PROP = NS_KEYCLOAK_PREFIX + "config.file";
|
||||
private static final List<ConfigSource> CONFIG_SOURCES = new ArrayList<>();
|
||||
public static PersistedConfigSource PERSISTED_CONFIG_SOURCE;
|
||||
|
||||
|
@ -57,14 +53,10 @@ public class KeycloakConfigSourceProvider implements ConfigSourceProvider {
|
|||
PERSISTED_CONFIG_SOURCE = new PersistedConfigSource(getPersistedConfigFile());
|
||||
CONFIG_SOURCES.add(PERSISTED_CONFIG_SOURCE);
|
||||
|
||||
Path configFile = getConfigurationFile();
|
||||
CONFIG_SOURCES.addAll(new KeycloakPropertiesConfigSource.InFileSystem().getConfigSources(Thread.currentThread().getContextClassLoader()));
|
||||
|
||||
if (configFile != null) {
|
||||
CONFIG_SOURCES.add(new KeycloakPropertiesConfigSource.InFileSystem(configFile));
|
||||
} else {
|
||||
log.debug("Loading the default server configuration");
|
||||
CONFIG_SOURCES.add(new KeycloakPropertiesConfigSource.InJar());
|
||||
}
|
||||
// by enabling this config source we are able to rely on the default settings when running tests
|
||||
CONFIG_SOURCES.addAll(new KeycloakPropertiesConfigSource.InClassPath().getConfigSources(Thread.currentThread().getContextClassLoader()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -76,31 +68,6 @@ public class KeycloakConfigSourceProvider implements ConfigSourceProvider {
|
|||
initializeSources();
|
||||
}
|
||||
|
||||
private static Path getConfigurationFile() {
|
||||
String filePath = System.getProperty(KEYCLOAK_CONFIG_FILE_PROP);
|
||||
|
||||
if (filePath == null)
|
||||
filePath = System.getenv(KEYCLOAK_CONFIG_FILE_ENV);
|
||||
|
||||
if (filePath == null) {
|
||||
String homeDir = Environment.getHomeDir();
|
||||
|
||||
if (homeDir != null) {
|
||||
File file = Paths.get(homeDir, "conf", KeycloakPropertiesConfigSource.KEYCLOAK_PROPERTIES).toFile();
|
||||
|
||||
if (file.exists()) {
|
||||
filePath = file.getAbsolutePath();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (filePath == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Paths.get(filePath);
|
||||
}
|
||||
|
||||
public static Path getPersistedConfigFile() {
|
||||
String homeDir = Environment.getHomeDir();
|
||||
|
||||
|
|
|
@ -17,99 +17,125 @@
|
|||
|
||||
package org.keycloak.quarkus.runtime.configuration;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOError;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.nio.file.NoSuchFileException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.eclipse.microprofile.config.spi.ConfigSource;
|
||||
import org.eclipse.microprofile.config.spi.ConfigSourceProvider;
|
||||
import org.keycloak.quarkus.runtime.Environment;
|
||||
|
||||
import io.smallrye.config.AbstractLocationConfigSourceLoader;
|
||||
import io.smallrye.config.PropertiesConfigSource;
|
||||
import io.smallrye.config.common.utils.ConfigSourceUtil;
|
||||
|
||||
import static org.keycloak.common.util.StringPropertyReplacer.replaceProperties;
|
||||
import static org.keycloak.quarkus.runtime.configuration.Configuration.getMappedPropertyName;
|
||||
import static org.keycloak.quarkus.runtime.configuration.MicroProfileConfigProvider.NS_KEYCLOAK;
|
||||
import static org.keycloak.quarkus.runtime.configuration.MicroProfileConfigProvider.NS_KEYCLOAK_PREFIX;
|
||||
import static org.keycloak.quarkus.runtime.configuration.MicroProfileConfigProvider.NS_QUARKUS;
|
||||
|
||||
/**
|
||||
* A configuration source for {@code keycloak.properties}.
|
||||
*/
|
||||
public abstract class KeycloakPropertiesConfigSource extends PropertiesConfigSource {
|
||||
|
||||
private static final Logger log = Logger.getLogger(KeycloakPropertiesConfigSource.class);
|
||||
public class KeycloakPropertiesConfigSource extends AbstractLocationConfigSourceLoader {
|
||||
|
||||
private static final Pattern DOT_SPLIT = Pattern.compile("\\.");
|
||||
static final String KEYCLOAK_PROPERTIES = "keycloak.properties";
|
||||
private static final String KEYCLOAK_CONFIG_FILE_ENV = "KC_CONFIG_FILE";
|
||||
private static final String KEYCLOAK_PROPERTIES = "keycloak.properties";
|
||||
public static final String KEYCLOAK_CONFIG_FILE_PROP = NS_KEYCLOAK_PREFIX + "config.file";
|
||||
|
||||
KeycloakPropertiesConfigSource(InputStream is, int ordinal) {
|
||||
super(readProperties(is), KEYCLOAK_PROPERTIES, ordinal);
|
||||
@Override
|
||||
protected String[] getFileExtensions() {
|
||||
return new String[] { "properties" };
|
||||
}
|
||||
|
||||
private static Map<String, String> readProperties(final InputStream is) {
|
||||
if (is == null) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
try (Closeable ignored = is) {
|
||||
Properties properties = new Properties();
|
||||
properties.load(is);
|
||||
return transform((Map) properties);
|
||||
} catch (IOException e) {
|
||||
throw new IOError(e);
|
||||
}
|
||||
@Override
|
||||
protected ConfigSource loadConfigSource(URL url, int ordinal) throws IOException {
|
||||
return new PropertiesConfigSource(transform(ConfigSourceUtil.urlToMap(url)), KEYCLOAK_PROPERTIES, ordinal);
|
||||
}
|
||||
|
||||
public static final class InJar extends KeycloakPropertiesConfigSource {
|
||||
public InJar() {
|
||||
super(openStream(), 250);
|
||||
public static class InClassPath extends KeycloakPropertiesConfigSource implements ConfigSourceProvider {
|
||||
|
||||
@Override
|
||||
public List<ConfigSource> getConfigSources(final ClassLoader classLoader) {
|
||||
return loadConfigSources("META-INF/keycloak.properties", 240, classLoader);
|
||||
}
|
||||
|
||||
private static InputStream openStream() {
|
||||
ClassLoader cl = Thread.currentThread().getContextClassLoader();
|
||||
if (cl == null) {
|
||||
cl = KeycloakPropertiesConfigSource.class.getClassLoader();
|
||||
}
|
||||
InputStream is;
|
||||
String fileName = "META-INF/" + KEYCLOAK_PROPERTIES;
|
||||
if (cl == null) {
|
||||
is = ClassLoader.getSystemResourceAsStream(fileName);
|
||||
} else {
|
||||
is = cl.getResourceAsStream(fileName);
|
||||
}
|
||||
if (is != null) {
|
||||
log.debug("Loading the server configuration from the classpath");
|
||||
}
|
||||
return is;
|
||||
}
|
||||
}
|
||||
|
||||
public static final class InFileSystem extends KeycloakPropertiesConfigSource {
|
||||
|
||||
public InFileSystem(Path path) {
|
||||
super(openStream(path), 250);
|
||||
}
|
||||
|
||||
private static InputStream openStream(Path path) {
|
||||
if (path == null) {
|
||||
throw new IllegalArgumentException("Configuration file path can not be null");
|
||||
}
|
||||
@Override
|
||||
protected List<ConfigSource> tryClassPath(URI uri, int ordinal, ClassLoader classLoader) {
|
||||
try {
|
||||
log.debugf("Loading the server configuration from %s", path);
|
||||
return Files.newInputStream(path);
|
||||
} catch (NoSuchFileException | FileNotFoundException e) {
|
||||
throw new IllegalArgumentException("Configuration file not found at [" + path + "]");
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Unexpected error reading configuration file at [" + path + "]", e);
|
||||
return super.tryClassPath(uri, ordinal, classLoader);
|
||||
} catch (RuntimeException e) {
|
||||
Throwable cause = e.getCause();
|
||||
|
||||
if (cause instanceof NoSuchFileException) {
|
||||
// configuration step happens before classpath is updated, and it might happen that
|
||||
// provider JARs are still in classpath index but removed from the providers dir
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<ConfigSource> tryFileSystem(final URI uri, final int ordinal) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
public static class InFileSystem extends KeycloakPropertiesConfigSource implements ConfigSourceProvider {
|
||||
|
||||
@Override
|
||||
public List<ConfigSource> getConfigSources(final ClassLoader classLoader) {
|
||||
Path configFile = getConfigurationFile();
|
||||
|
||||
if (configFile == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return loadConfigSources(configFile.toUri().toString(), 250, classLoader);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<ConfigSource> tryClassPath(final URI uri, final int ordinal, final ClassLoader classLoader) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private Path getConfigurationFile() {
|
||||
String filePath = System.getProperty(KEYCLOAK_CONFIG_FILE_PROP);
|
||||
|
||||
if (filePath == null)
|
||||
filePath = System.getenv(KEYCLOAK_CONFIG_FILE_ENV);
|
||||
|
||||
if (filePath == null) {
|
||||
String homeDir = Environment.getHomeDir();
|
||||
|
||||
if (homeDir != null) {
|
||||
File file = Paths.get(homeDir, "conf", KeycloakPropertiesConfigSource.KEYCLOAK_PROPERTIES).toFile();
|
||||
|
||||
if (file.exists()) {
|
||||
filePath = file.getAbsolutePath();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (filePath == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Paths.get(filePath);
|
||||
}
|
||||
}
|
||||
|
||||
private static Map<String, String> transform(Map<String, String> properties) {
|
||||
|
|
|
@ -20,13 +20,13 @@ final class DatabasePropertyMappers {
|
|||
.mapFrom("db")
|
||||
.to("quarkus.hibernate-orm.dialect")
|
||||
.isBuildTimeProperty(true)
|
||||
.transformer((db, context) -> Database.getDialect(db).orElse(null))
|
||||
.transformer((db, context) -> Database.getDialect(db).orElse(Database.getDialect("h2-file").get()))
|
||||
.hidden(true)
|
||||
.build(),
|
||||
builder().from("db-driver")
|
||||
.mapFrom("db")
|
||||
.to("quarkus.datasource.jdbc.driver")
|
||||
.transformer((db, context) -> Database.getDriver(db).orElse(null))
|
||||
.transformer((db, context) -> Database.getDriver(db).orElse(Database.getDriver("h2-file").get()))
|
||||
.hidden(true)
|
||||
.build(),
|
||||
builder().from("db").
|
||||
|
@ -111,7 +111,7 @@ final class DatabasePropertyMappers {
|
|||
|
||||
addInitializationException(invalidDatabaseVendor(db, Database.getAliases()));
|
||||
|
||||
return null;
|
||||
return "h2";
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,8 @@ import org.keycloak.platform.PlatformProvider;
|
|||
import org.keycloak.quarkus.runtime.InitializationException;
|
||||
import org.keycloak.quarkus.runtime.Environment;
|
||||
|
||||
import io.quarkus.runtime.Quarkus;
|
||||
|
||||
public class QuarkusPlatform implements PlatformProvider {
|
||||
|
||||
private static final Logger log = Logger.getLogger(QuarkusPlatform.class);
|
||||
|
@ -56,6 +58,9 @@ public class QuarkusPlatform implements PlatformProvider {
|
|||
for (Throwable inner : platform.getDeferredExceptions()) {
|
||||
quarkusException.addSuppressed(inner);
|
||||
}
|
||||
// reset this instance, mainly deferred exceptions, so that the subsequent starts do not fail due to previous errors
|
||||
// this is mainly important when the server is running in test mode
|
||||
platform.reset();
|
||||
throw quarkusException;
|
||||
}
|
||||
}
|
||||
|
@ -91,7 +96,7 @@ public class QuarkusPlatform implements PlatformProvider {
|
|||
|
||||
@Override
|
||||
public void exit(Throwable cause) {
|
||||
throw new RuntimeException(cause);
|
||||
Quarkus.asyncExit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -162,4 +167,8 @@ public class QuarkusPlatform implements PlatformProvider {
|
|||
}
|
||||
return tmpDir;
|
||||
}
|
||||
|
||||
private void reset() {
|
||||
deferredExceptions.clear();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,6 @@ import org.infinispan.configuration.parsing.ConfigurationBuilderHolder;
|
|||
import org.infinispan.configuration.parsing.ParserRegistry;
|
||||
import org.infinispan.jboss.marshalling.core.JBossUserMarshaller;
|
||||
import org.infinispan.manager.DefaultCacheManager;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.Config;
|
||||
|
||||
public class CacheInitializer {
|
||||
|
|
26
quarkus/runtime/src/main/resources/application.properties
Normal file
26
quarkus/runtime/src/main/resources/application.properties
Normal file
|
@ -0,0 +1,26 @@
|
|||
# This is the main configuration for Keycloak on Quarkus
|
||||
|
||||
quarkus.package.main-class=keycloak
|
||||
quarkus.http.root-path=/
|
||||
quarkus.application.name=Keycloak
|
||||
quarkus.banner.enabled=false
|
||||
|
||||
# Disable health checks from extensions, since we provide our own (default is true)
|
||||
quarkus.health.extensions.enabled=false
|
||||
|
||||
# Default transaction timeout
|
||||
quarkus.transaction-manager.default-transaction-timeout=300
|
||||
|
||||
# The JAX-RS application is programmatically registered at build time.
|
||||
# When indexing classes, both KeycloakApplication and QuarkusKeycloakApplication are indexed and multuple
|
||||
# application classes are no longer supported by resteasy extension
|
||||
quarkus.resteasy.ignore-application-classes=true
|
||||
|
||||
# Ignore split packages for Keycloak related packages
|
||||
quarkus.arc.ignored-split-packages=org.keycloak.*
|
||||
|
||||
# No need to generate dependencies list
|
||||
quarkus.package.include-dependency-list=false
|
||||
|
||||
# we do not want running dev services in distribution
|
||||
quarkus.devservices.enabled=false
|
|
@ -1,30 +1,7 @@
|
|||
#quarkus.log.level = DEBUG
|
||||
# Inherit all configuration from the default runtime settings and sets those specific to the distribution
|
||||
|
||||
quarkus.package.output-name=keycloak
|
||||
quarkus.package.type=mutable-jar
|
||||
quarkus.package.output-directory=lib
|
||||
quarkus.package.user-providers-directory=../providers
|
||||
quarkus.package.main-class=keycloak
|
||||
|
||||
quarkus.http.root-path=/
|
||||
quarkus.application.name=Keycloak
|
||||
quarkus.banner.enabled=false
|
||||
|
||||
# Disable health checks from extensions, since we provide our own (default is true)
|
||||
quarkus.health.extensions.enabled=false
|
||||
|
||||
# Default transaction timeout
|
||||
quarkus.transaction-manager.default-transaction-timeout=300
|
||||
|
||||
# The JAX-RS application is programmatically registered at build time.
|
||||
# When indexing classes, both KeycloakApplication and QuarkusKeycloakApplication are indexed and multuple
|
||||
# application classes are no longer supported by resteasy extension
|
||||
quarkus.resteasy.ignore-application-classes=true
|
||||
|
||||
# Ignore split packages for Keycloak related packages
|
||||
quarkus.arc.ignored-split-packages=org.keycloak.*
|
||||
|
||||
# No need to generate dependencies list
|
||||
quarkus.package.include-dependency-list=false
|
||||
|
||||
# we do not want running dev services in distribution
|
||||
quarkus.devservices.enabled=false
|
||||
quarkus.package.main-class=keycloak
|
65
quarkus/tests/integration/pom.xml
Normal file
65
quarkus/tests/integration/pom.xml
Normal file
|
@ -0,0 +1,65 @@
|
|||
<?xml version="1.0"?>
|
||||
<!--
|
||||
~ 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.
|
||||
-->
|
||||
|
||||
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<artifactId>keycloak-quarkus-test-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>16.0.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<name>Keycloak Quarkus Server Integration tests</name>
|
||||
<artifactId>keycloak-quarkus-integration-tests</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-quarkus-server</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Minimal test dependencies to *-deployment artifacts for consistent build order -->
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-quarkus-server-deployment</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>*</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-junit5</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.rest-assured</groupId>
|
||||
<artifactId>rest-assured</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
* 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 static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.keycloak.quarkus.runtime.cli.Picocli;
|
||||
|
||||
import io.quarkus.test.junit.main.LaunchResult;
|
||||
import picocli.CommandLine;
|
||||
|
||||
public interface CLIResult extends LaunchResult {
|
||||
|
||||
static Object create(List<String> outputStream, List<String> errStream, int exitCode, boolean distribution) {
|
||||
return new CLIResult() {
|
||||
@Override
|
||||
public List<String> getOutputStream() {
|
||||
return outputStream;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getErrorStream() {
|
||||
return errStream;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int exitCode() {
|
||||
return exitCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDistribution() {
|
||||
return distribution;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
boolean isDistribution();
|
||||
|
||||
default void assertStarted() {
|
||||
assertTrue(getOutput().contains("Listening on:"));
|
||||
assertNotDevMode();
|
||||
}
|
||||
|
||||
default void assertNotDevMode() {
|
||||
assertFalse(getOutput().contains("Running the server in dev mode."));
|
||||
}
|
||||
|
||||
default void assertStartedDevMode() {
|
||||
assertTrue(getOutput().contains("Running the server in dev mode."));
|
||||
}
|
||||
|
||||
default void assertError(String msg) {
|
||||
assertTrue(getErrorOutput().contains(msg));
|
||||
}
|
||||
|
||||
default void assertHelp(String command) {
|
||||
if (command == null) {
|
||||
fail("No command provided");
|
||||
}
|
||||
|
||||
CommandLine cmd = Picocli.createCommandLine(Arrays.asList(command, "--help"));
|
||||
|
||||
if (isDistribution()) {
|
||||
cmd.setCommandName("kc.sh");
|
||||
}
|
||||
|
||||
try (
|
||||
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
|
||||
PrintStream printStream = new PrintStream(outStream, true)
|
||||
) {
|
||||
if ("kc.sh".equals(command)) {
|
||||
cmd.usage(printStream);
|
||||
} else {
|
||||
cmd.getSubcommands().get(command).usage(printStream);
|
||||
}
|
||||
|
||||
// not very reliable, we should be comparing the output with some static reference to the help message.
|
||||
assertTrue(getOutput().equals(outStream.toString().trim()));
|
||||
} catch (IOException cause) {
|
||||
throw new RuntimeException("Failed to assert help", cause);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* 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;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
||||
@Target(ElementType.TYPE)
|
||||
@ExtendWith({ CLITestExtension.class })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface CLITest {
|
||||
}
|
|
@ -0,0 +1,172 @@
|
|||
/*
|
||||
* 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 static org.keycloak.it.junit5.extension.DistributionTest.ReInstall.BEFORE_ALL;
|
||||
import static org.keycloak.quarkus.runtime.Environment.forceTestLaunchMode;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import org.junit.jupiter.api.extension.ExtensionContext;
|
||||
import org.junit.jupiter.api.extension.ParameterContext;
|
||||
import org.junit.jupiter.api.extension.ParameterResolutionException;
|
||||
import org.keycloak.it.junit5.extension.DistributionTest.ReInstall;
|
||||
import org.keycloak.it.utils.KeycloakDistribution;
|
||||
import org.keycloak.quarkus.runtime.Environment;
|
||||
import org.keycloak.quarkus.runtime.cli.command.Start;
|
||||
import org.keycloak.quarkus.runtime.cli.command.StartDev;
|
||||
|
||||
import io.quarkus.test.junit.QuarkusMainTestExtension;
|
||||
import io.quarkus.test.junit.main.Launch;
|
||||
import io.quarkus.test.junit.main.LaunchResult;
|
||||
|
||||
public class CLITestExtension extends QuarkusMainTestExtension {
|
||||
|
||||
private KeycloakDistribution dist;
|
||||
|
||||
@Override
|
||||
public void beforeEach(ExtensionContext context) throws Exception {
|
||||
DistributionTest distConfig = getDistributionConfig(context);
|
||||
|
||||
if (distConfig != null) {
|
||||
Launch launch = context.getRequiredTestMethod().getAnnotation(Launch.class);
|
||||
|
||||
if (launch != null) {
|
||||
if (dist == null) {
|
||||
dist = createDistribution(distConfig);
|
||||
}
|
||||
dist.start(Arrays.asList(launch.value()));
|
||||
}
|
||||
} else {
|
||||
configureProfile(context);
|
||||
super.beforeEach(context);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterEach(ExtensionContext context) throws Exception {
|
||||
DistributionTest distConfig = getDistributionConfig(context);
|
||||
|
||||
if (distConfig != null) {
|
||||
if (distConfig.keepAlive()) {
|
||||
dist.stopIfRunning();
|
||||
}
|
||||
}
|
||||
|
||||
super.afterEach(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterAll(ExtensionContext context) throws Exception {
|
||||
if (dist != null) {
|
||||
// just to make sure the server is stopped after all tests
|
||||
dist.stopIfRunning();
|
||||
}
|
||||
super.afterAll(context);
|
||||
}
|
||||
|
||||
private KeycloakDistribution createDistribution(DistributionTest config) {
|
||||
KeycloakDistribution distribution = new KeycloakDistribution();
|
||||
|
||||
distribution.setReCreate(!ReInstall.NEVER.equals(config.reInstall()));
|
||||
distribution.setDebug(config.debug());
|
||||
distribution.setManualStop(config.keepAlive());
|
||||
|
||||
return distribution;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeAll(ExtensionContext context) throws Exception {
|
||||
DistributionTest distConfig = getDistributionConfig(context);
|
||||
|
||||
if (distConfig != null) {
|
||||
if (BEFORE_ALL.equals(distConfig.reInstall())) {
|
||||
dist = createDistribution(distConfig);
|
||||
}
|
||||
} else {
|
||||
forceTestLaunchMode();
|
||||
}
|
||||
|
||||
super.beforeAll(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object resolveParameter(ParameterContext parameterContext, ExtensionContext context)
|
||||
throws ParameterResolutionException {
|
||||
Class<?> type = parameterContext.getParameter().getType();
|
||||
|
||||
if (type == LaunchResult.class) {
|
||||
List<String> outputStream;
|
||||
List<String> errStream;
|
||||
int exitCode;
|
||||
|
||||
boolean isDistribution = getDistributionConfig(context) != null;
|
||||
|
||||
if (isDistribution) {
|
||||
outputStream = dist.getOutputStream();
|
||||
errStream = dist.getErrorStream();
|
||||
exitCode = dist.getExitCode();
|
||||
} else {
|
||||
LaunchResult result = (LaunchResult) super.resolveParameter(parameterContext, context);
|
||||
outputStream = result.getOutputStream();
|
||||
errStream = result.getErrorStream();
|
||||
exitCode = result.exitCode();
|
||||
}
|
||||
|
||||
return CLIResult.create(outputStream, errStream, exitCode, isDistribution);
|
||||
}
|
||||
|
||||
// for now, not support for manual launching using QuarkusMainLauncher
|
||||
throw new RuntimeException("Parameter type [" + type + "] not supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext)
|
||||
throws ParameterResolutionException {
|
||||
Class<?> type = parameterContext.getParameter().getType();
|
||||
return type == LaunchResult.class;
|
||||
}
|
||||
|
||||
private void configureProfile(ExtensionContext context) {
|
||||
List<String> cliArgs = getCliArgs(context);
|
||||
|
||||
// when running tests, build steps happen before executing our CLI so that profiles are not set and not taken
|
||||
// into account when executing the build steps
|
||||
// this is basically reproducing the behavior when using kc.sh
|
||||
if (cliArgs.contains(Start.NAME)) {
|
||||
Environment.setProfile("prod");
|
||||
} else if (cliArgs.contains(StartDev.NAME)) {
|
||||
Environment.forceDevProfile();
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> getCliArgs(ExtensionContext context) {
|
||||
Launch annotation = context.getRequiredTestMethod().getAnnotation(Launch.class);
|
||||
|
||||
if (annotation != null) {
|
||||
return Arrays.asList(annotation.value());
|
||||
}
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private DistributionTest getDistributionConfig(ExtensionContext context) {
|
||||
return context.getTestClass().get().getDeclaredAnnotation(DistributionTest.class);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* 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;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
||||
@Target(ElementType.TYPE)
|
||||
@ExtendWith({ CLITestExtension.class })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface DistributionTest {
|
||||
|
||||
boolean debug() default false;
|
||||
boolean keepAlive() default false;
|
||||
|
||||
enum ReInstall {
|
||||
BEFORE_ALL,
|
||||
BEFORE_TEST,
|
||||
NEVER;
|
||||
}
|
||||
|
||||
ReInstall reInstall() default ReInstall.BEFORE_ALL;
|
||||
}
|
||||
|
|
@ -0,0 +1,337 @@
|
|||
/*
|
||||
* 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.utils;
|
||||
|
||||
import static org.keycloak.quarkus.runtime.Environment.LAUNCH_MODE;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Supplier;
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSession;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.eclipse.aether.artifact.Artifact;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import io.quarkus.bootstrap.util.ZipUtils;
|
||||
|
||||
public final class KeycloakDistribution {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(KeycloakDistribution.class);
|
||||
|
||||
private Process keycloak;
|
||||
private int exitCode = -1;
|
||||
private final Path distPath;
|
||||
private final List<String> outputStream = new ArrayList<>();
|
||||
private final List<String> errorStream = new ArrayList<>();
|
||||
private boolean reCreate;
|
||||
private boolean manualStop;
|
||||
private String relativePath;
|
||||
private int httpPort;
|
||||
private boolean debug;
|
||||
private ExecutorService outputExecutor;
|
||||
|
||||
public <T> KeycloakDistribution() {
|
||||
distPath = prepareDistribution();
|
||||
}
|
||||
|
||||
public void start(List<String> arguments) {
|
||||
reset();
|
||||
if (manualStop && isRunning()) {
|
||||
throw new IllegalStateException("Server already running. You should manually stop the server before starting it again.");
|
||||
}
|
||||
stopIfRunning();
|
||||
try {
|
||||
startServer(arguments);
|
||||
if (manualStop) {
|
||||
asyncReadOutput();
|
||||
waitForReadiness();
|
||||
} else {
|
||||
readOutput();
|
||||
}
|
||||
} catch (Exception cause) {
|
||||
stopIfRunning();
|
||||
throw new RuntimeException("Failed to start the server", cause);
|
||||
} finally {
|
||||
if (!manualStop) {
|
||||
stopIfRunning();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void stopIfRunning() {
|
||||
if (isRunning()) {
|
||||
try {
|
||||
keycloak.destroy();
|
||||
keycloak.waitFor(10, TimeUnit.SECONDS);
|
||||
exitCode = keycloak.exitValue();
|
||||
} catch (Exception cause) {
|
||||
keycloak.destroyForcibly();
|
||||
throw new RuntimeException("Failed to stop the server", cause);
|
||||
}
|
||||
}
|
||||
|
||||
shutdownOutputExecutor();
|
||||
}
|
||||
|
||||
public List<String> getOutputStream() {
|
||||
return outputStream;
|
||||
}
|
||||
|
||||
public List<String> getErrorStream() {
|
||||
return errorStream;
|
||||
}
|
||||
|
||||
public int getExitCode() {
|
||||
return exitCode;
|
||||
}
|
||||
|
||||
public void setReCreate(boolean reCreate) {
|
||||
this.reCreate = reCreate;
|
||||
}
|
||||
|
||||
public void setDebug(boolean debug) {
|
||||
this.debug = debug;
|
||||
}
|
||||
|
||||
public void setManualStop(boolean manualStop) {
|
||||
this.manualStop = manualStop;
|
||||
}
|
||||
|
||||
private String[] getCliArgs(List<String> arguments) {
|
||||
List<String> commands = new ArrayList<>();
|
||||
|
||||
commands.add("./kc.sh");
|
||||
|
||||
if (debug) {
|
||||
commands.add("--debug");
|
||||
}
|
||||
|
||||
if (!manualStop) {
|
||||
commands.add("-D" + LAUNCH_MODE + "=test");
|
||||
}
|
||||
|
||||
this.relativePath = arguments.stream().filter(arg -> arg.startsWith("--http-relative-path")).map(arg -> arg.substring(arg.indexOf('=') + 1)).findAny().orElse("/");
|
||||
this.httpPort = Integer.parseInt(arguments.stream().filter(arg -> arg.startsWith("--http-port")).map(arg -> arg.substring(arg.indexOf('=') + 1)).findAny().orElse("8080"));
|
||||
|
||||
commands.addAll(arguments);
|
||||
|
||||
return commands.toArray(new String[0]);
|
||||
}
|
||||
|
||||
private void waitForReadiness() throws MalformedURLException {
|
||||
URL contextRoot = new URL("http://localhost:" + httpPort + ("/" + relativePath + "/realms/master/").replace("//", "/"));
|
||||
HttpURLConnection connection = null;
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
while (true) {
|
||||
if (System.currentTimeMillis() - startTime > getStartTimeout()) {
|
||||
throw new IllegalStateException(
|
||||
"Timeout [" + getStartTimeout() + "] while waiting for Quarkus server");
|
||||
}
|
||||
|
||||
try {
|
||||
// wait before checking for opening a new connection
|
||||
Thread.sleep(1000);
|
||||
if ("https".equals(contextRoot.getProtocol())) {
|
||||
HttpsURLConnection httpsConnection = (HttpsURLConnection) (connection = (HttpURLConnection) contextRoot.openConnection());
|
||||
httpsConnection.setSSLSocketFactory(createInsecureSslSocketFactory());
|
||||
httpsConnection.setHostnameVerifier(createInsecureHostnameVerifier());
|
||||
} else {
|
||||
connection = (HttpURLConnection) contextRoot.openConnection();
|
||||
}
|
||||
|
||||
connection.setReadTimeout((int) getStartTimeout());
|
||||
connection.setConnectTimeout((int) getStartTimeout());
|
||||
connection.connect();
|
||||
|
||||
if (connection.getResponseCode() == 200) {
|
||||
LOGGER.infof("Keycloak is ready at %s", contextRoot);
|
||||
break;
|
||||
}
|
||||
} catch (Exception ignore) {
|
||||
} finally {
|
||||
if (connection != null) {
|
||||
connection.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private long getStartTimeout() {
|
||||
return TimeUnit.SECONDS.toMillis(120);
|
||||
}
|
||||
|
||||
private HostnameVerifier createInsecureHostnameVerifier() {
|
||||
return new HostnameVerifier() {
|
||||
@Override
|
||||
public boolean verify(String s, SSLSession sslSession) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private SSLSocketFactory createInsecureSslSocketFactory() throws IOException {
|
||||
TrustManager[] trustAllCerts = new TrustManager[] {new X509TrustManager() {
|
||||
public void checkClientTrusted(final X509Certificate[] chain, final String authType) {
|
||||
}
|
||||
|
||||
public void checkServerTrusted(final X509Certificate[] chain, final String authType) {
|
||||
}
|
||||
|
||||
public X509Certificate[] getAcceptedIssuers() {
|
||||
return null;
|
||||
}
|
||||
}};
|
||||
|
||||
SSLContext sslContext;
|
||||
SSLSocketFactory socketFactory;
|
||||
|
||||
try {
|
||||
sslContext = SSLContext.getInstance("TLS");
|
||||
sslContext.init(null, trustAllCerts, new SecureRandom());
|
||||
socketFactory = sslContext.getSocketFactory();
|
||||
} catch (NoSuchAlgorithmException | KeyManagementException e) {
|
||||
throw new IOException("Can't create unsecure trust manager");
|
||||
}
|
||||
return socketFactory;
|
||||
}
|
||||
|
||||
private boolean isRunning() {
|
||||
return keycloak != null && keycloak.isAlive();
|
||||
}
|
||||
|
||||
private void asyncReadOutput() {
|
||||
shutdownOutputExecutor();
|
||||
outputExecutor = Executors.newSingleThreadExecutor();
|
||||
outputExecutor.execute(this::readOutput);
|
||||
}
|
||||
|
||||
private void shutdownOutputExecutor() {
|
||||
if (outputExecutor != null) {
|
||||
outputExecutor.shutdown();
|
||||
try {
|
||||
outputExecutor.awaitTermination(30, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException cause) {
|
||||
throw new RuntimeException("Failed to terminate output executor", cause);
|
||||
} finally {
|
||||
outputExecutor = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void reset() {
|
||||
outputStream.clear();
|
||||
errorStream.clear();
|
||||
exitCode = -1;
|
||||
keycloak = null;
|
||||
shutdownOutputExecutor();
|
||||
}
|
||||
|
||||
private Path prepareDistribution() {
|
||||
try {
|
||||
Path distRootPath = Paths.get(System.getProperty("java.io.tmpdir")).resolve("kc-tests");
|
||||
distRootPath.toFile().mkdirs();
|
||||
File distFile = Maven.resolveArtifact("org.keycloak", "keycloak-server-x-dist", "zip")
|
||||
.map(Artifact::getFile)
|
||||
.orElseThrow(new Supplier<RuntimeException>() {
|
||||
@Override
|
||||
public RuntimeException get() {
|
||||
return new RuntimeException("Could not obtain distribution artifact");
|
||||
}
|
||||
});
|
||||
String distDirName = distFile.getName().replace("keycloak-server-x-dist", "keycloak.x");
|
||||
Path distPath = distRootPath.resolve(distDirName.substring(0, distDirName.lastIndexOf('.')));
|
||||
|
||||
if (reCreate || !distPath.toFile().exists()) {
|
||||
distPath.toFile().delete();
|
||||
ZipUtils.unzip(distFile.toPath(), distRootPath);
|
||||
}
|
||||
|
||||
// make sure kc.sh is executable
|
||||
distPath.resolve("bin").resolve("kc.sh").toFile().setExecutable(true);
|
||||
|
||||
return distPath;
|
||||
} catch (Exception cause) {
|
||||
throw new RuntimeException("Failed to prepare distribution", cause);
|
||||
}
|
||||
}
|
||||
|
||||
private void readOutput() {
|
||||
try (
|
||||
BufferedReader outStream = new BufferedReader(new InputStreamReader(keycloak.getInputStream()));
|
||||
BufferedReader errStream = new BufferedReader(new InputStreamReader(keycloak.getErrorStream()));
|
||||
) {
|
||||
while (keycloak.isAlive()) {
|
||||
readStream(outStream, outputStream);
|
||||
readStream(errStream, errorStream);
|
||||
}
|
||||
} catch (Throwable cause) {
|
||||
throw new RuntimeException("Failed to read server output", cause);
|
||||
}
|
||||
}
|
||||
|
||||
private void readStream(BufferedReader reader, List<String> stream) throws IOException {
|
||||
String line;
|
||||
|
||||
while (reader.ready() && (line = reader.readLine()) != null) {
|
||||
stream.add(line);
|
||||
System.out.println(line);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The server is configured to redirect errors to output stream. This adds a limitation when checking whether a
|
||||
* message arrived via error stream.
|
||||
*
|
||||
* @param arguments the list of arguments to run the server
|
||||
* @throws Exception if something bad happens
|
||||
*/
|
||||
private void startServer(List<String> arguments) throws Exception {
|
||||
ProcessBuilder pb = new ProcessBuilder(getCliArgs(arguments));
|
||||
ProcessBuilder builder = pb.directory(distPath.resolve("bin").toFile());
|
||||
|
||||
builder.environment().put("KEYCLOAK_ADMIN", "admin");
|
||||
builder.environment().put("KEYCLOAK_ADMIN_PASSWORD", "admin");
|
||||
|
||||
FileUtils.deleteDirectory(distPath.resolve("data").toFile());
|
||||
|
||||
keycloak = builder.start();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* 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.utils;
|
||||
|
||||
import java.net.URL;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Optional;
|
||||
import org.eclipse.aether.artifact.Artifact;
|
||||
import org.eclipse.aether.artifact.DefaultArtifact;
|
||||
import org.eclipse.aether.resolution.ArtifactResult;
|
||||
|
||||
import io.quarkus.bootstrap.resolver.maven.BootstrapMavenContext;
|
||||
import io.quarkus.bootstrap.resolver.maven.MavenArtifactResolver;
|
||||
import io.quarkus.bootstrap.resolver.maven.workspace.LocalProject;
|
||||
import io.quarkus.bootstrap.utils.BuildToolHelper;
|
||||
|
||||
public final class Maven {
|
||||
|
||||
private static Path resolveProjectDir() {
|
||||
try {
|
||||
String classFilePath = KeycloakDistribution.class.getName().replace(".", "/") + ".class";
|
||||
URL classFileResource = Thread.currentThread().getContextClassLoader().getResource(classFilePath);
|
||||
String classPath = classFileResource.getPath();
|
||||
classPath = classPath.substring(0, classPath.length() - classFilePath.length());
|
||||
URL newResource = new URL(classFileResource.getProtocol(), classFileResource.getHost(), classFileResource.getPort(),
|
||||
classPath);
|
||||
|
||||
return BuildToolHelper.getProjectDir(Paths.get(newResource.toURI()));
|
||||
} catch (Exception cause) {
|
||||
throw new RuntimeException("Failed to resolve project dir", cause);
|
||||
}
|
||||
}
|
||||
|
||||
static Optional<Artifact> resolveArtifact(String groupId, String artifactId) {
|
||||
return resolveArtifact(groupId, artifactId, "jar");
|
||||
}
|
||||
|
||||
static Optional<Artifact> resolveArtifact(String groupId, String artifactId, String extension) {
|
||||
BootstrapMavenContext mvnCtx = createBootstrapContext();
|
||||
LocalProject project = mvnCtx.getCurrentProject();
|
||||
|
||||
try {
|
||||
MavenArtifactResolver mvnResolver = new MavenArtifactResolver(mvnCtx);
|
||||
ArtifactResult resolve = mvnResolver.resolve(new DefaultArtifact(groupId, artifactId, extension,
|
||||
project.getVersion()));
|
||||
|
||||
if (resolve.isResolved()) {
|
||||
return Optional.of(resolve.getArtifact());
|
||||
}
|
||||
} catch (Exception cause) {
|
||||
throw new RuntimeException("Failed to resolve project artifact [" + groupId + ":" + artifactId + ":" + project.getVersion() + ":" + extension, cause);
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private static BootstrapMavenContext createBootstrapContext() {
|
||||
try {
|
||||
return new BootstrapMavenContext(BootstrapMavenContext.config().setCurrentProject(resolveProjectDir().toString()));
|
||||
} catch (Exception cause) {
|
||||
throw new RuntimeException("Failed to create maven boorstrap context", cause);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* 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.cli;
|
||||
|
||||
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.CLITest;
|
||||
import org.keycloak.quarkus.runtime.cli.command.Main;
|
||||
|
||||
import io.quarkus.test.junit.main.Launch;
|
||||
import io.quarkus.test.junit.main.LaunchResult;
|
||||
|
||||
@CLITest
|
||||
public class HelpCommandTest {
|
||||
|
||||
@Test
|
||||
@Launch({ "--help" })
|
||||
void testHelpCommand(LaunchResult result) {
|
||||
CLIResult cliResult = (CLIResult) result;
|
||||
cliResult.assertHelp("kc.sh");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Launch({ "start", "--help" })
|
||||
void testStartHelpCommand(LaunchResult result) {
|
||||
CLIResult cliResult = (CLIResult) result;
|
||||
cliResult.assertHelp("start");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Launch({ "start-dev", "--help" })
|
||||
void testStartDevCommand(LaunchResult result) {
|
||||
CLIResult cliResult = (CLIResult) result;
|
||||
cliResult.assertHelp("start-dev");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Launch({ "build", "--help" })
|
||||
void testBuildCommand(LaunchResult result) {
|
||||
CLIResult cliResult = (CLIResult) result;
|
||||
cliResult.assertHelp("build");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* 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.cli;
|
||||
|
||||
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.CLITest;
|
||||
|
||||
import io.quarkus.test.junit.main.Launch;
|
||||
import io.quarkus.test.junit.main.LaunchResult;
|
||||
import io.quarkus.test.junit.main.QuarkusMainLauncher;
|
||||
|
||||
@CLITest
|
||||
public class OptionValidationTest {
|
||||
|
||||
@Test
|
||||
@Launch({"build", "--db"})
|
||||
public void failMissingOptionValue(LaunchResult result) {
|
||||
Assertions.assertTrue(result.getErrorOutput().contains("Missing required value for option '--db' (vendor). Expected values are: h2-file, h2-mem, mariadb, mssql, mssql-2012, mysql, oracle, postgres, postgres-95"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Launch({"build", "--db=invalid"})
|
||||
public void failInvalidOptionValue(LaunchResult result) {
|
||||
Assertions.assertTrue(result.getErrorOutput().contains("Invalid value for option '--db': invalid. Expected values are: h2-file, h2-mem, mariadb, mssql, mssql-2012, mysql, oracle, postgres, postgres-95"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Launch({"build", "--db", "foo", "bar"})
|
||||
public void failMultipleOptionValue(LaunchResult result) {
|
||||
Assertions.assertTrue(result.getErrorOutput().contains("Option '--db' expects a single value (vendor) Expected values are: h2-file, h2-mem, mariadb, mssql, mssql-2012, mysql, oracle, postgres, postgres-95"));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* 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.cli;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.keycloak.it.junit5.extension.CLITest;
|
||||
|
||||
import io.quarkus.test.junit.main.Launch;
|
||||
import io.quarkus.test.junit.main.LaunchResult;
|
||||
|
||||
@CLITest
|
||||
class ShowConfigCommandTest {
|
||||
|
||||
@Test
|
||||
@Launch({ "show-config" })
|
||||
void testShowConfigCommandShowsRuntimeConfig(LaunchResult result) {
|
||||
Assertions.assertTrue(result.getOutput()
|
||||
.contains("Runtime Configuration"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Launch({ "show-config", "all" })
|
||||
void testShowConfigCommandWithAllShowsAllProfiles(LaunchResult result) {
|
||||
Assertions.assertTrue(result.getOutput()
|
||||
.contains("Runtime Configuration"));
|
||||
Assertions.assertTrue(result.getOutput()
|
||||
.contains("Quarkus Configuration"));
|
||||
Assertions.assertTrue(result.getOutput()
|
||||
.contains("Profile \"import_export\" Configuration"));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* 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.cli;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.keycloak.it.junit5.extension.CLIResult;
|
||||
import org.keycloak.it.junit5.extension.CLITest;
|
||||
|
||||
import io.quarkus.test.junit.main.Launch;
|
||||
import io.quarkus.test.junit.main.LaunchResult;
|
||||
|
||||
@CLITest
|
||||
public class StartCommandTest {
|
||||
|
||||
@Test
|
||||
@Launch({ "start", "--hostname-strict=false" })
|
||||
void failNoTls(LaunchResult result) {
|
||||
assertTrue(result.getOutput().contains("Key material not provided to setup HTTPS"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Launch({ "start", "--http-enabled=true" })
|
||||
void failNoHostnameNotSet(LaunchResult result) {
|
||||
assertTrue(result.getOutput().contains("ERROR: Strict hostname resolution configured but no hostname was set"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Launch({ "--profile=dev", "start" })
|
||||
void failUsingDevProfile(LaunchResult result) {
|
||||
assertTrue(result.getErrorOutput().contains("ERROR: You can not 'start' 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."));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Launch({ "-v", "start", "--http-enabled=true", "--hostname-strict=false" })
|
||||
void testHttpEnabled(LaunchResult result) {
|
||||
CLIResult cliResult = (CLIResult) result;
|
||||
cliResult.assertStarted();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Launch({ "-v", "start", "--db=h2-mem" })
|
||||
void failBuildPropertyNotAvailable(LaunchResult result) {
|
||||
CLIResult cliResult = (CLIResult) result;
|
||||
cliResult.assertError("Unknown option: '--db=h2-mem'");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* 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.cli;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.keycloak.it.junit5.extension.CLIResult;
|
||||
import org.keycloak.it.junit5.extension.CLITest;
|
||||
|
||||
import io.quarkus.test.junit.main.Launch;
|
||||
import io.quarkus.test.junit.main.LaunchResult;
|
||||
|
||||
@CLITest
|
||||
public class StartDevCommandTest {
|
||||
|
||||
@Test
|
||||
@Launch({ "start-dev" })
|
||||
void testDevModeWarning(LaunchResult result) {
|
||||
CLIResult cliResult = (CLIResult) result;
|
||||
cliResult.assertStartedDevMode();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Launch({ "start-dev", "--db=h2-mem" })
|
||||
void testBuildPropertyAvailable(LaunchResult result) {
|
||||
CLIResult cliResult = (CLIResult) result;
|
||||
cliResult.assertStartedDevMode();
|
||||
}
|
||||
}
|
47
quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/BuildCommandDistTest.java
vendored
Normal file
47
quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/BuildCommandDistTest.java
vendored
Normal file
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* 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.cli.dist;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.keycloak.it.junit5.extension.DistributionTest;
|
||||
|
||||
import io.quarkus.test.junit.main.Launch;
|
||||
import io.quarkus.test.junit.main.LaunchResult;
|
||||
|
||||
@DistributionTest
|
||||
class BuildCommandDistTest {
|
||||
|
||||
@Test
|
||||
@Launch({ "build" })
|
||||
void resetConfig(LaunchResult result) {
|
||||
assertTrue(result.getOutput().contains("Updating the configuration and installing your custom providers, if any. Please wait."));
|
||||
assertTrue(result.getOutput().contains("Quarkus augmentation completed"));
|
||||
assertTrue(result.getOutput().contains("Server configuration updated and persisted. Run the following command to review the configuration:"));
|
||||
assertTrue(result.getOutput().contains("kc.sh show-config"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Launch({ "--profile=dev", "build" })
|
||||
void failIfDevProfile(LaunchResult result) {
|
||||
assertTrue(result.getErrorOutput().contains("ERROR: Failed to run 'build' command."));
|
||||
assertTrue(result.getErrorOutput().contains("ERROR: You can not 'build' 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."));
|
||||
assertTrue(result.getErrorOutput().contains("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."));
|
||||
}
|
||||
}
|
25
quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/HelpCommandDistTest.java
vendored
Normal file
25
quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/HelpCommandDistTest.java
vendored
Normal file
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* 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.cli.dist;
|
||||
|
||||
import org.keycloak.it.cli.HelpCommandTest;
|
||||
import org.keycloak.it.junit5.extension.DistributionTest;
|
||||
|
||||
@DistributionTest
|
||||
public class HelpCommandDistTest extends HelpCommandTest {
|
||||
}
|
48
quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/MetricsDistTest.java
vendored
Normal file
48
quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/MetricsDistTest.java
vendored
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* 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.cli.dist;
|
||||
|
||||
import static io.restassured.RestAssured.when;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.keycloak.it.junit5.extension.DistributionTest;
|
||||
|
||||
import io.quarkus.test.junit.main.Launch;
|
||||
|
||||
@DistributionTest(keepAlive =true)
|
||||
public class MetricsDistTest {
|
||||
|
||||
@Test
|
||||
@Launch({ "start-dev", "--metrics-enabled=true" })
|
||||
void testMetricsEndpoint() {
|
||||
when().get("/metrics").then()
|
||||
.statusCode(200)
|
||||
.body(containsString("base_gc_total"));
|
||||
}
|
||||
|
||||
@Disabled("https://github.com/keycloak/keycloak/pull/8878")
|
||||
@Test
|
||||
@Launch({ "start-dev", "--http-relative-path=/auth", "--metrics-enabled=true" })
|
||||
void testMetricsEndpointUsingRelativePath() {
|
||||
when().get("/auth/metrics").then()
|
||||
.statusCode(200)
|
||||
.body(containsString("base_gc_total"));
|
||||
}
|
||||
}
|
25
quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/StartCommandDistTest.java
vendored
Normal file
25
quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/StartCommandDistTest.java
vendored
Normal file
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* 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.cli.dist;
|
||||
|
||||
import org.keycloak.it.cli.StartCommandTest;
|
||||
import org.keycloak.it.junit5.extension.DistributionTest;
|
||||
|
||||
@DistributionTest
|
||||
public class StartCommandDistTest extends StartCommandTest {
|
||||
}
|
25
quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/StartDevCommandDistTest.java
vendored
Normal file
25
quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/StartDevCommandDistTest.java
vendored
Normal file
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* 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.cli.dist;
|
||||
|
||||
import org.keycloak.it.cli.StartDevCommandTest;
|
||||
import org.keycloak.it.junit5.extension.DistributionTest;
|
||||
|
||||
@DistributionTest
|
||||
public class StartDevCommandDistTest extends StartDevCommandTest {
|
||||
}
|
39
quarkus/tests/pom.xml
Normal file
39
quarkus/tests/pom.xml
Normal file
|
@ -0,0 +1,39 @@
|
|||
<?xml version="1.0"?>
|
||||
<!--
|
||||
~ 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.
|
||||
-->
|
||||
|
||||
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<artifactId>keycloak-quarkus-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>16.0.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<name>Keycloak Quarkus Test Parent</name>
|
||||
<artifactId>keycloak-quarkus-test-parent</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<modules>
|
||||
<module>integration</module>
|
||||
</modules>
|
||||
|
||||
</project>
|
Loading…
Reference in a new issue