8000 Quarkus IT execution time savings: skip realm bootstrap when not required by michalvavrik · Pull Request #48809 · keycloak/keycloak · GitHub
[go: up one dir, main page]

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ public final class Environment {
public static final String DEFAULT_THEMES_PATH = File.separator + "themes";
public static final String PROD_PROFILE_VALUE = "prod";
public static final String LAUNCH_MODE = "kc.launch.mode";
public static final String LAUNCH_MODE_EXIT_AFTER_START = "exit_after_start";
public static final String LAUNCH_MODE_EXIT_BEFORE_BOOTSTRAP = "exit_before_bootstrap";

private Environment() {}

Expand Down Expand Up @@ -86,9 +88,6 @@ public static void setProfile(String profile) {
System.setProperty(org.keycloak.common.util.Environment.PROFILE, profile);
System.setProperty(LaunchMode.current().getProfileKey(), profile);
System.setProperty(SmallRyeConfig.SMALLRYE_CONFIG_PROFILE, profile);
if (isTestLaunchMode()) {
System.setProperty("mp.config.profile", profile);
}
}

/**
Expand Down Expand Up @@ -135,12 +134,13 @@ public boolean accept(File dir, String name) {
})).collect(Collectors.toMap(File::getName, Function.identity()));
}

public static boolean isTestLaunchMode() {
return "test".equals(System.getProperty(LAUNCH_MODE));
public static boolean hasEarlyExitLaunchMode() {
String mode = System.getProperty(LAUNCH_MODE);
return LAUNCH_MODE_EXIT_AFTER_START.equals(mode) || LAUNCH_MODE_EXIT_BEFORE_BOOTSTRAP.equals(mode);
}

public static void forceTestLaunchMode() {
System.setProperty(LAUNCH_MODE, "test");
public static void forceExitAfterStartLaunchMode() {
System.setProperty(LAUNCH_MODE, LAUNCH_MODE_EXIT_AFTER_START);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@

import static org.keycloak.common.util.Environment.isNonServerMode;
import static org.keycloak.quarkus.runtime.Environment.getKeycloakModeFromProfile;
import static org.keycloak.quarkus.runtime.Environment.isTestLaunchMode;
import static org.keycloak.quarkus.runtime.Environment.hasEarlyExitLaunchMode;

/**
* <p>The main entry point, responsible for initialize and run the CLI as well as start the server.
Expand Down Expand Up @@ -159,7 +159,7 @@ public int run(String... args) throws Exception {
QuarkusKeycloakSessionFactory sessionFactory = Arc.container().instance(QuarkusKeycloakSessionFactory.class).get();
COMMAND.onStart(application, sessionFactory);
}
if (isTestLaunchMode() || isNonServerMode()) {
if (hasEarlyExitLaunchMode() || isNonServerMode()) {
// in test mode we exit immediately
// we should be managing this behavior more dynamically depending on the tests requirements (short/long lived)
Quarkus.asyncExit(ApplicationLifecycleManager.getExitCode());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@

import static org.keycloak.common.util.Environment.isDevMode;
import static org.keycloak.common.util.Environment.isNonServerMode;
import static org.keycloak.quarkus.runtime.Environment.isTestLaunchMode;
import static org.keycloak.quarkus.runtime.Environment.hasEarlyExitLaunchMode;

@ApplicationPath("/")
@Blocking
Expand Down Expand Up @@ -119,7 +119,7 @@ protected boolean supportsAsyncInitialization() {
.map(Boolean::parseBoolean)
.orElse(Boolean.TRUE);
// skip async bootstrap in dev and non-server mode
return !isDevMode() && !isNonServerMode() && !isTestLaunchMode() && asyncBootstrap;
return !isDevMode() && !isNonServerMode() && !hasEarlyExitLaunchMode() && asyncBootstrap;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,13 @@
import org.keycloak.it.junit5.extension.DistributionTest;
import org.keycloak.it.junit5.extension.DryRun;
import org.keycloak.it.junit5.extension.RawDistOnly;
import org.keycloak.it.junit5.extension.SkipRealmBootstrap;
import org.keycloak.it.utils.KeycloakDistribution;

import org.junit.jupiter.api.Test;

@DistributionTest
@SkipRealmBootstrap
public class CacheEmbeddedMtlsDistTest {

@DryRun
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import org.keycloak.it.junit5.extension.CLIResult;
import org.keycloak.it.junit5.extension.DistributionTest;
import org.keycloak.it.junit5.extension.RawDistOnly;
import org.keycloak.it.junit5.extension.SkipRealmBootstrap;
import org.keycloak.it.junit5.extension.Storage;
import org.keycloak.it.utils.KeycloakDistribution;

Expand All @@ -47,6 +48,7 @@
@DistributionTest(defaultOptions = {"--db=dev-file", "--http-enabled=true", "--hostname-strict=false"})
@RawDistOnly(reason = "Not possible to mount files using docker.")
@Storage(defaultLocalCache = false)
@SkipRealmBootstrap
@Tag(DistributionTest.SMOKE)
@Tag(DistributionTest.SLOW)
@TestMethodOrder(OrderAnnotation.class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import org.keycloak.it.junit5.extension.CLIResult;
import org.keycloak.it.junit5.extension.DistributionTest;
import org.keycloak.it.junit5.extension.RawDistOnly;
import org.keycloak.it.junit5.extension.SkipRealmBootstrap;
import org.keycloak.it.utils.KeycloakDistribution;
import org.keycloak.quarkus.runtime.cli.command.Build;
import org.keycloak.quarkus.runtime.cli.command.Start;
Expand All @@ -30,6 +31,7 @@
@RawDistOnly(reason = "Containers are immutable")
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@Tag(DistributionTest.SMOKE)
@SkipRealmBootstrap
public class FeaturesDistTest {

private static final String PREVIEW_FEATURES_EXPECTED_LOG = "Preview features enabled: " + Arrays.stream(Profile.Feature.values())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import org.keycloak.it.junit5.extension.CLIResult;
import org.keycloak.it.junit5.extension.DistributionTest;
import org.keycloak.it.junit5.extension.RawDistOnly;
import org.keycloak.it.junit5.extension.SkipRealmBootstrap;

import io.quarkus.test.junit.main.Launch;
import io.quarkus.test.junit.main.LaunchResult;
Expand All @@ -30,6 +31,7 @@

@DistributionTest
@RawDistOnly(reason = "Containers are immutable")
@SkipRealmBootstrap
public class HostnameV2DistTest {
@Test
@Launch({"start", "--db=dev-file", "--http-enabled=true"})
Expand Down Expand Up @@ -66,4 +68,4 @@ public void testBackchannelDynamicRequiresHostname(LaunchResult result) {
public void testBackchannelDynamicRequiresFullHostnameUrl(LaunchResult result) {
assertThat(result.getErrorOutput(), containsString("hostname-backchannel-dynamic must be set to false if hostname is not provided as full URL"));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import org.keycloak.it.junit5.extension.CLIResult;
import org.keycloak.it.junit5.extension.DistributionTest;
import org.keycloak.it.junit5.extension.RawDistOnly;
import org.keycloak.it.junit5.extension.SkipRealmBootstrap;

import io.quarkus.test.junit.main.Launch;
import io.quarkus.test.junit.main.LaunchResult;
Expand All @@ -31,6 +32,7 @@
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@DistributionTest
@RawDistOnly(reason = "Containers are immutable")
@SkipRealmBootstrap
public class TracingDistTest {

static void assertTracingEnabled(CLIResult result) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
import static java.lang.System.setProperty;

import static org.keycloak.it.junit5.extension.DistributionType.RAW;
import static org.keycloak.quarkus.runtime.Environment.forceTestLaunchMode;
import static org.keycloak.quarkus.runtime.Environment.forceExitAfterStartLaunchMode;

public class CLITestExtension extends QuarkusMainTestExtension {

Expand Down Expand Up @@ -106,6 +106,10 @@ public void beforeEach(ExtensionContext context) throws Exception {
dist.setEnvVar(DryRunMixin.KC_DRY_RUN_ENV, "true");
dist.setEnvVar(DryRunMixin.KC_DRY_RUN_BUILD_ENV, "true");
}
if (isRaw() && (context.getRequiredTestClass().getAnnotation(SkipRealmBootstrap.class) != null
|| context.getRequiredTestMethod().getAnnotation(SkipRealmBootstrap.class) != null)) {
dist.unwrap(RawKeycloakDistribution.class).setLaunchMode(Environment.LAUNCH_MODE_EXIT_BEFORE_BOOTSTRAP);
}

if (launch != null) {
result = dist.run(List.of(launch.value()));
Expand Down Expand Up @@ -179,6 +183,9 @@ public void afterEach(ExtensionContext context) throws Exception {
onKeepServerAlive(context.getRequiredTestMethod().getAnnotation(KeepServerAlive.class), false);
dist.stop();
dist.clearEnv();
if (isRaw()) {
dist.unwrap(RawKeycloakDistribution.class).setLaunchMode(Environment.LAUNCH_MODE_EXIT_AFTER_START);
}
}

super.afterEach(context);
Expand Down Expand Up @@ -224,7 +231,7 @@ public void close() throws Exception {
if (distConfig != null) {
dist = createDistribution(distConfig, getStoreConfig(context), getDatabaseConfig(context));
} else {
forceTestLaunchMode();
forceExitAfterStartLaunchMode();
}

super.beforeAll(context);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
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;

/**
* Skips realm bootstrap (DB lock, realm creation, admin user setup) after provider initialization.
* Liquibase schema migration and provider {@code postInit()} still run.
* Useful for tests that only assert on log messages from server startup and don't need realm data.
*/
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface SkipRealmBootstrap {

}
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ public final class RawKeycloakDistribution implements KeycloakDistribution {
private long startTimeout = TimeUnit.SECONDS.toMillis(Long.getLong("keycloak.distribution.start.timeout", 120L));
private boolean throwErrorIfFailedToStart = false;
private boolean threadDump = true;
private String launchMode = Environment.LAUNCH_MODE_EXIT_AFTER_START;

public RawKeycloakDistribution(boolean debug, boolean manualStop, boolean enableTls, boolean reCreate, boolean removeBuildOptionsAfterBuild, int requestPort) {
this.debug = debug;
Expand Down Expand Up @@ -330,7 +331,7 @@ public String[] getCliArgs(List<String> arguments) {
}

if (!this.isManualStop()) {
allArgs.add("-D" + LAUNCH_MODE + "=test");
allArgs.add("-D" + LAUNCH_MODE + "=" + launchMode);
}

allArgs.add("-Djgroups.join_timeout=50");
Expand Down Expand Up @@ -565,7 +566,7 @@ private Path prepareDistribution() {
private void preInitH2(Path dPath) throws IOException {
LOG.info("Creating pre-initialized database for reuse");
ProcessHandle.current().info().command().ifPresent(command -> {
ProcessBuilder pb = new ProcessBuilder(List.of(command, "-Dinit_db_only=true",
ProcessBuilder pb = new ProcessBuilder(List.of(command, "-D" + LAUNCH_MODE + "=" + Environment.LAUNCH_MODE_EXIT_BEFORE_BOOTSTRAP,
"-Dkc.home.dir=" + dPath.toFile().getAbsolutePath(), "-Dkc.config.built=true",
"-Dkc.db=dev-file", "-jar", dPath.resolve("lib").resolve("quarkus-run.jar").toFile().getAbsolutePath(), "start-dev"));
try {
Expand Down Expand Up @@ -661,6 +662,10 @@ public void setEnvVar(String name, String value) {
this.envVars.put(name, value);
}

public void setLaunchMode(String launchMode) {
this.launchMode = launchMode;
}

@Override
public void removeProperty(String name) {
updateProperties(properties -> properties.remove(name), distPath.resolve("conf").resolve("keycloak.conf").toFile());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,7 @@ private synchronized void runBootstrap(DefaultKeycloakSessionFactory keycloakSes

keycloakSessionFactory.init();

// for testing, allow the quarkus application to exit early
if (io.quarkus.runtime.Application.currentApplication() != null && Boolean.getBoolean("init_db_only")) {
if ("exit_before_bootstrap".equals(System.getProperty("kc.launch.mode"))) {
Quarkus.asyncExit(0);
return;
}
Expand Down
Loading
0