mirror of
https://github.com/keycloak/keycloak.git
synced 2026-01-09 23:12:06 -03:30
fix: resetting failingSince on datasourceHealthCheck == UP (#35905)
closes: #35904 Signed-off-by: Steve Hawkins <shawkins@redhat.com>
This commit is contained in:
parent
0783e2a058
commit
3db9689010
@ -173,6 +173,11 @@
|
||||
<artifactId>quarkus-junit5-internal</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-junit5-mockito</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.rest-assured</groupId>
|
||||
<artifactId>rest-assured</artifactId>
|
||||
|
||||
@ -16,42 +16,24 @@
|
||||
*/
|
||||
package test.org.keycloak.quarkus.services.health;
|
||||
|
||||
import io.quarkus.test.QuarkusUnitTest;
|
||||
import io.restassured.RestAssured;
|
||||
import org.jboss.shrinkwrap.api.ShrinkWrap;
|
||||
import org.jboss.shrinkwrap.api.spec.JavaArchive;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
import static io.restassured.RestAssured.given;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import io.quarkus.test.junit.QuarkusTest;
|
||||
import io.quarkus.test.junit.TestProfile;
|
||||
import io.restassured.RestAssured;
|
||||
|
||||
@QuarkusTest
|
||||
@TestProfile(MetricsEnabledProfile.class)
|
||||
class KeycloakMetricsConfigurationTest {
|
||||
|
||||
@BeforeAll
|
||||
static void setUpAll() {
|
||||
System.setProperty("KC_CACHE", "local");
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
static void tearDownAll() {
|
||||
System.clearProperty("KC_CACHE");
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
RestAssured.port = 9001;
|
||||
}
|
||||
|
||||
@RegisterExtension
|
||||
static final QuarkusUnitTest test = new QuarkusUnitTest()
|
||||
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
|
||||
.addAsResource("keycloak.conf", "META-INF/keycloak.conf"))
|
||||
.overrideConfigKey("quarkus.micrometer.export.prometheus.path", "/prom/metrics")
|
||||
.overrideConfigKey("quarkus.class-loading.removed-artifacts", "io.quarkus:quarkus-jdbc-oracle,io.quarkus:quarkus-jdbc-oracle-deployment"); // config works a bit odd in unit tests, so this is to ensure we exclude Oracle to avoid ClassNotFound ex
|
||||
|
||||
@Test
|
||||
void testMetrics() {
|
||||
given().basePath("/")
|
||||
|
||||
@ -16,41 +16,50 @@
|
||||
*/
|
||||
package test.org.keycloak.quarkus.services.health;
|
||||
|
||||
import io.agroal.api.AgroalDataSource;
|
||||
import io.quarkus.test.QuarkusUnitTest;
|
||||
import io.restassured.RestAssured;
|
||||
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 jakarta.inject.Inject;
|
||||
|
||||
import static io.restassured.RestAssured.given;
|
||||
|
||||
import org.eclipse.microprofile.health.HealthCheckResponse;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.keycloak.quarkus.runtime.services.health.KeycloakReadyHealthCheck;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import io.agroal.api.AgroalDataSource;
|
||||
import io.agroal.api.AgroalDataSourceMetrics;
|
||||
import io.quarkus.agroal.runtime.health.DataSourceHealthCheck;
|
||||
import io.quarkus.test.InjectMock;
|
||||
import io.quarkus.test.junit.QuarkusTest;
|
||||
import io.quarkus.test.junit.TestProfile;
|
||||
import io.restassured.RestAssured;
|
||||
|
||||
@QuarkusTest
|
||||
@TestProfile(MetricsEnabledProfile.class)
|
||||
public class KeycloakNegativeHealthCheckTest {
|
||||
|
||||
@Inject
|
||||
@InjectMock
|
||||
AgroalDataSource agroalDataSource;
|
||||
|
||||
@RegisterExtension
|
||||
static final QuarkusUnitTest test = new QuarkusUnitTest()
|
||||
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
|
||||
.addAsResource("keycloak.conf", "META-INF/keycloak.conf"))
|
||||
.overrideConfigKey("quarkus.class-loading.removed-artifacts", "io.quarkus:quarkus-jdbc-oracle,io.quarkus:quarkus-jdbc-oracle-deployment"); // config works a bit odd in unit tests, so this is to ensure we exclude Oracle to avoid ClassNotFound ex
|
||||
@InjectMock
|
||||
DataSourceHealthCheck dataSourceHealthCheck;
|
||||
|
||||
@Test
|
||||
public void testReadinessDown() {
|
||||
agroalDataSource.close();
|
||||
AgroalDataSourceMetrics metrics = Mockito.mock(AgroalDataSourceMetrics.class);
|
||||
Mockito.when(agroalDataSource.getMetrics()).thenReturn(metrics);
|
||||
Mockito.when(dataSourceHealthCheck.call()).thenReturn(HealthCheckResponse.down("down"));
|
||||
|
||||
RestAssured.port = 9001;
|
||||
System.setProperty("KC_CACHE", "local"); // avoid flaky port conflicts
|
||||
given()
|
||||
.when().get("/health/ready")
|
||||
.then()
|
||||
.statusCode(503)
|
||||
.body(Matchers.containsString("DOWN"));
|
||||
System.clearProperty("KC_CACHE");
|
||||
.body(Matchers.allOf(Matchers.containsString("DOWN"), Matchers.containsString(KeycloakReadyHealthCheck.FAILING_SINCE)));
|
||||
|
||||
// now have an active connection, failing since should be cleared
|
||||
Mockito.when(metrics.activeCount()).thenReturn(2L);
|
||||
given()
|
||||
.when().get("/health/ready")
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.body(Matchers.not(Matchers.containsString(KeycloakReadyHealthCheck.FAILING_SINCE)));
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,44 +16,26 @@
|
||||
*/
|
||||
package test.org.keycloak.quarkus.services.health;
|
||||
|
||||
import io.quarkus.test.QuarkusUnitTest;
|
||||
import io.restassured.RestAssured;
|
||||
import org.jboss.shrinkwrap.api.ShrinkWrap;
|
||||
import org.jboss.shrinkwrap.api.spec.JavaArchive;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
import static io.restassured.RestAssured.given;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import io.quarkus.test.junit.QuarkusTest;
|
||||
import io.quarkus.test.junit.TestProfile;
|
||||
import io.restassured.RestAssured;
|
||||
|
||||
@QuarkusTest
|
||||
@TestProfile(MetricsEnabledProfileWithPath.class)
|
||||
class KeycloakPathConfigurationTest {
|
||||
|
||||
@BeforeAll
|
||||
static void setUpAll() {
|
||||
System.setProperty("KC_CACHE", "local");
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
static void tearDownAll() {
|
||||
System.clearProperty("KC_CACHE");
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
RestAssured.port = 9001;
|
||||
}
|
||||
|
||||
@RegisterExtension
|
||||
static final QuarkusUnitTest test = new QuarkusUnitTest()
|
||||
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
|
||||
.addAsResource("keycloak.conf", "META-INF/keycloak.conf"))
|
||||
.overrideConfigKey("kc.http-relative-path","/auth")
|
||||
.overrideConfigKey("quarkus.micrometer.export.prometheus.path", "/prom/metrics")
|
||||
.overrideConfigKey("quarkus.class-loading.removed-artifacts", "io.quarkus:quarkus-jdbc-oracle,io.quarkus:quarkus-jdbc-oracle-deployment"); // config works a bit odd in unit tests, so this is to ensure we exclude Oracle to avoid ClassNotFound ex
|
||||
|
||||
@Test
|
||||
void testMetrics() {
|
||||
given().basePath("/")
|
||||
|
||||
@ -16,42 +16,25 @@
|
||||
*/
|
||||
package test.org.keycloak.quarkus.services.health;
|
||||
|
||||
import io.quarkus.test.QuarkusUnitTest;
|
||||
import io.restassured.RestAssured;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.jboss.shrinkwrap.api.ShrinkWrap;
|
||||
import org.jboss.shrinkwrap.api.spec.JavaArchive;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
import static io.restassured.RestAssured.given;
|
||||
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import io.quarkus.test.junit.QuarkusTest;
|
||||
import io.quarkus.test.junit.TestProfile;
|
||||
import io.restassured.RestAssured;
|
||||
|
||||
@QuarkusTest
|
||||
@TestProfile(MetricsEnabledProfile.class)
|
||||
public class KeycloakReadyHealthCheckTest {
|
||||
|
||||
@BeforeAll
|
||||
static void setUpAll() {
|
||||
System.setProperty("KC_CACHE", "local");
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
static void tearDownAll() {
|
||||
System.clearProperty("KC_CACHE");
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
RestAssured.port = 9001;
|
||||
}
|
||||
|
||||
@RegisterExtension
|
||||
static final QuarkusUnitTest test = new QuarkusUnitTest()
|
||||
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
|
||||
.addAsResource("keycloak.conf", "META-INF/keycloak.conf"))
|
||||
.overrideConfigKey("quarkus.class-loading.removed-artifacts", "io.quarkus:quarkus-jdbc-oracle,io.quarkus:quarkus-jdbc-oracle-deployment"); // config works a bit odd in unit tests, so this is to ensure we exclude Oracle to avoid ClassNotFound ex
|
||||
|
||||
@Test
|
||||
public void testLivenessUp() {
|
||||
given()
|
||||
|
||||
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2025 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 test.org.keycloak.quarkus.services.health;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import io.quarkus.test.junit.QuarkusTestProfile;
|
||||
|
||||
public class MetricsEnabledProfile implements QuarkusTestProfile {
|
||||
|
||||
@Override
|
||||
public Map<String, String> getConfigOverrides() {
|
||||
return Map.of("kc.http-enabled", "true",
|
||||
"kc.cluster", "local",
|
||||
"kc.hostname-strict", "false",
|
||||
"kc.db", "dev-mem",
|
||||
"kc.health-enabled","true",
|
||||
"kc.metrics-enabled", "true",
|
||||
"kc.cache", "local",
|
||||
"quarkus.micrometer.export.prometheus.path", "/prom/metrics",
|
||||
"quarkus.class-loading.removed-artifacts", "io.quarkus:quarkus-jdbc-oracle,io.quarkus:quarkus-jdbc-oracle-deployment"); // config works a bit odd in unit tests, so this is to ensure we exclude Oracle to avoid ClassNotFound ex
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2025 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 test.org.keycloak.quarkus.services.health;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class MetricsEnabledProfileWithPath extends MetricsEnabledProfile {
|
||||
|
||||
@Override
|
||||
public Map<String, String> getConfigOverrides() {
|
||||
HashMap<String, String> config = new HashMap<>();
|
||||
config.put("kc.http-relative-path","/auth");
|
||||
config.putAll(super.getConfigOverrides());
|
||||
return config;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,9 +0,0 @@
|
||||
http-enabled=true
|
||||
cluster=local
|
||||
hostname-strict=false
|
||||
hostname-strict-https=false
|
||||
db=dev-mem
|
||||
db-username = sa
|
||||
db-password = keycloak
|
||||
health-enabled=true
|
||||
metrics-enabled=true
|
||||
@ -17,6 +17,7 @@
|
||||
package org.keycloak.quarkus.runtime.services.health;
|
||||
|
||||
import io.agroal.api.AgroalDataSource;
|
||||
import io.agroal.api.AgroalDataSourceMetrics;
|
||||
import io.quarkus.agroal.runtime.health.DataSourceHealthCheck;
|
||||
import io.quarkus.smallrye.health.runtime.QuarkusAsyncHealthCheckFactory;
|
||||
import io.smallrye.health.api.AsyncHealthCheck;
|
||||
@ -46,6 +47,8 @@ import java.util.concurrent.atomic.AtomicReference;
|
||||
@ApplicationScoped
|
||||
public class KeycloakReadyHealthCheck implements AsyncHealthCheck {
|
||||
|
||||
public static final String FAILING_SINCE = "Failing since";
|
||||
|
||||
/**
|
||||
* Date formatter, the same as used by Quarkus. This enables users to quickly compare the date printed
|
||||
* by the probe with the logs.
|
||||
@ -66,15 +69,18 @@ public class KeycloakReadyHealthCheck implements AsyncHealthCheck {
|
||||
@Override
|
||||
public Uni<HealthCheckResponse> call() {
|
||||
HealthCheckResponseBuilder builder = HealthCheckResponse.named("Keycloak database connections async health check").up();
|
||||
long activeCount = agroalDataSource.getMetrics().activeCount();
|
||||
long invalidCount = agroalDataSource.getMetrics().invalidCount();
|
||||
AgroalDataSourceMetrics metrics = agroalDataSource.getMetrics();
|
||||
long activeCount = metrics.activeCount();
|
||||
long invalidCount = metrics.invalidCount();
|
||||
if (activeCount < 1 || invalidCount > 0) {
|
||||
return healthCheckFactory.callSync(() -> {
|
||||
HealthCheckResponse activeCheckResult = dataSourceHealthCheck.call();
|
||||
if (activeCheckResult.getStatus() == HealthCheckResponse.Status.DOWN) {
|
||||
builder.down();
|
||||
Instant failingTime = failingSince.updateAndGet(this::createInstanceIfNeeded);
|
||||
builder.withData("Failing since", DATE_FORMATTER.format(failingTime));
|
||||
builder.withData(FAILING_SINCE, DATE_FORMATTER.format(failingTime));
|
||||
} else {
|
||||
failingSince.set(null);
|
||||
}
|
||||
return builder.build();
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user