Skip to content

Commit 4488054

Browse files
committed
wip...
1 parent 71423c1 commit 4488054

File tree

5 files changed

+102
-0
lines changed

5 files changed

+102
-0
lines changed

test-framework/clustering/src/main/java/org/keycloak/testframework/server/ClusteredKeycloakServer.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ public void start(KeycloakServerConfigBuilder configBuilder, boolean tlsEnabled)
7575
} catch (TimeoutException e) {
7676
throw new RuntimeException("Expected %d cluster members".formatted(numServers), e);
7777
}
78+
ReadinessProbe.waitUntilReady(this::getManagementBaseUrl, numServers);
7879
}
7980

8081
private void startContainersWithMixedImage(KeycloakServerConfigBuilder configBuilder, String[] imagePeServer, CountdownLatchLoggingConsumer clusterLatch) {

test-framework/core/src/main/java/org/keycloak/testframework/server/DistributionKeycloakServer.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ public void start(KeycloakServerConfigBuilder keycloakServerConfigBuilder, boole
9797
OutputHandler outputHandler = startKeycloak(args);
9898

9999
waitForStart(outputHandler);
100+
ReadinessProbe.waitUntilReady(getManagementBaseUrl());
100101

101102
if (!Environment.isWindows()) {
102103
FileUtils.writeToFile(getPidFile(), ProcessUtils.getKeycloakPid(keycloakProcess));

test-framework/core/src/main/java/org/keycloak/testframework/server/EmbeddedKeycloakServer.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ public void start(KeycloakServerConfigBuilder keycloakServerConfigBuilder, boole
2222
}
2323

2424
keycloak = builder.start(keycloakServerConfigBuilder.toArgs());
25+
ReadinessProbe.waitUntilReady(getManagementBaseUrl());
2526
}
2627

2728
@Override
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package org.keycloak.testframework.server;
2+
3+
import java.net.HttpURLConnection;
4+
import java.net.URI;
5+
import java.security.cert.X509Certificate;
6+
import java.time.Duration;
7+
import java.util.concurrent.TimeUnit;
8+
import java.util.function.IntFunction;
9+
import javax.net.ssl.HttpsURLConnection;
10+
import javax.net.ssl.SSLContext;
11+
import javax.net.ssl.TrustManager;
12+
import javax.net.ssl.X509TrustManager;
13+
14+
/**
15+
* Polls the server's health endpoint until it reports ready, supporting both HTTP and HTTPS connections.
16+
*/
17+
public final class ReadinessProbe {
18+
19+
private static final long STARTUP_TIMEOUT_MILLIS = Duration.ofMinutes(5).toMillis();
20+
private static final int CONNECTION_TIMEOUT_MILLIS = Math.toIntExact(Duration.ofSeconds(5).toMillis());
21+
private static final long POLL_INTERVAL_MILLIS = Duration.ofMillis(500).toMillis();
22+
23+
private ReadinessProbe() {
24+
}
25+
26+
public static void waitUntilReady(String baseManagementUrl) {
27+
var url = baseManagementUrl + "/health/ready";
28+
var deadline = System.currentTimeMillis() + STARTUP_TIMEOUT_MILLIS;
29+
var sslContext = createTrustAllSslContext();
30+
waitUntilReady(url, sslContext, deadline);
31+
}
32+
33+
public static void waitUntilReady(IntFunction<String> baseManagementUrlFunction, int range) {
34+
var deadline = System.currentTimeMillis() + STARTUP_TIMEOUT_MILLIS;
35+
var sslContext = createTrustAllSslContext();
36+
for (int i = 0; i < range; i++) {
37+
var url = baseManagementUrlFunction.apply(i) + "/health/ready";
38+
waitUntilReady(url, sslContext, deadline);
39+
}
40+
}
41+
42+
private static void waitUntilReady(String url, SSLContext sslContext, long deadline) {
43+
while (System.currentTimeMillis() < deadline) {
44+
try {
45+
HttpURLConnection connection = (HttpURLConnection) URI.create(url).toURL().openConnection();
46+
if (connection instanceof HttpsURLConnection https) {
47+
https.setSSLSocketFactory(sslContext.getSocketFactory());
48+
https.setHostnameVerifier((hostname, session) -> true);
49+
}
50+
connection.setConnectTimeout(CONNECTION_TIMEOUT_MILLIS);
51+
connection.setReadTimeout(CONNECTION_TIMEOUT_MILLIS);
52+
connection.setRequestMethod("GET");
53+
54+
try {
55+
if (connection.getResponseCode() == 200) {
56+
return;
57+
}
58+
} finally {
59+
connection.disconnect();
60+
}
61+
} catch (Exception e) {
62+
// server not yet available, retry
63+
}
64+
65+
try {
66+
//noinspection BusyWait
67+
Thread.sleep(POLL_INTERVAL_MILLIS);
68+
} catch (InterruptedException e) {
69+
Thread.currentThread().interrupt();
70+
throw new RuntimeException("Interrupted while waiting for server readiness", e);
71+
}
72+
}
73+
throw new IllegalStateException("Server did not become ready within " + TimeUnit.MILLISECONDS.toSeconds(STARTUP_TIMEOUT_MILLIS) + " seconds: " + url);
74+
}
75+
76+
private static SSLContext createTrustAllSslContext() {
77+
try {
78+
TrustManager[] trustAll = new TrustManager[]{
79+
new X509TrustManager() {
80+
public X509Certificate[] getAcceptedIssuers() {
81+
return new X509Certificate[0];
82+
}
83+
84+
public void checkClientTrusted(X509Certificate[] certs, String authType) {
85+
}
86+
87+
public void checkServerTrusted(X509Certificate[] certs, String authType) {
88+
}
89+
}
90+
};
91+
SSLContext ctx = SSLContext.getInstance("TLS");
92+
ctx.init(null, trustAll, null);
93+
return ctx;
94+
} catch (Exception e) {
95+
throw new RuntimeException("Failed to create trust-all SSLContext", e);
96+
}
97+
}
98+
}

test-framework/core/src/main/java/org/keycloak/testframework/server/RemoteKeycloakServer.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ public void start(KeycloakServerConfigBuilder keycloakServerConfigBuilder, boole
3030
}
3131
waitForStartup();
3232
}
33+
ReadinessProbe.waitUntilReady(getManagementBaseUrl());
3334
}
3435

3536
@Override

0 commit comments

Comments
 (0)