Add additional metrics for user/client/session count (#31)

This commit is contained in:
Stephan Schnabel 2023-04-25 10:28:30 +02:00 committed by GitHub
parent 566f31ddc2
commit 37dcc07309
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 312 additions and 6 deletions

View file

@ -1,11 +1,14 @@
package io.kokuwa.keycloak.metrics;
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.time.Instant;
import java.util.UUID;
import java.util.function.Supplier;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
@ -73,4 +76,41 @@ public class KeycloakIT {
() -> assertEquals(loginErrorBefore1 + 0, loginErrorAfter1, "login failure #1"),
() -> assertEquals(loginErrorBefore2 + 1, loginErrorAfter2, "login failure #2"));
}
@DisplayName("user count")
@Test
void userCount(KeycloakClient keycloak, Prometheus prometheus) {
var realmName1 = "userCount_1";
var realmName2 = "userCount_2";
var username = UUID.randomUUID().toString();
keycloak.createRealm(realmName1);
keycloak.createRealm(realmName2);
await(() -> prometheus.userCount(realmName1) == 0, prometheus, "realm 1 not found");
await(() -> prometheus.userCount(realmName2) == 0, prometheus, "realm 2 not found");
keycloak.createUser(realmName1, username, UUID.randomUUID().toString());
keycloak.createUser(realmName1, UUID.randomUUID().toString(), UUID.randomUUID().toString());
keycloak.createUser(realmName1, UUID.randomUUID().toString(), UUID.randomUUID().toString());
keycloak.createUser(realmName2, UUID.randomUUID().toString(), UUID.randomUUID().toString());
await(() -> prometheus.userCount(realmName1) == 3, prometheus, "realm 1 shoud have 3 users");
await(() -> prometheus.userCount(realmName2) == 1, prometheus, "realm 2 shoud have 1 users");
keycloak.deleteUser(realmName1, username);
await(() -> prometheus.userCount(realmName1) == 2, prometheus, "realm 1 shoud have 2 users after deletion");
await(() -> prometheus.userCount(realmName2) == 1, prometheus, "realm 2 shoud have 1 users");
}
void await(Supplier<Boolean> check, Prometheus prometheus, String message) {
var end = Instant.now().plusSeconds(10);
while (Instant.now().isBefore(end) && !check.get()) {
assertDoesNotThrow(() -> Thread.sleep(1000));
prometheus.scrap();
}
assertTrue(check.get(), message);
}
}

View file

@ -1,5 +1,7 @@
package io.kokuwa.keycloak.metrics.junit;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.List;
import java.util.Map;
import java.util.UUID;
@ -45,7 +47,8 @@ public class KeycloakClient {
client.setClientId(clientId);
client.setPublicClient(true);
client.setDirectAccessGrantsEnabled(true);
keycloak.realms().realm(realmName).clients().create(client);
var response = keycloak.realms().realm(realmName).clients().create(client);
assertEquals(201, response.getStatus());
}
public void createUser(String realmName, String username, String password) {
@ -59,7 +62,15 @@ public class KeycloakClient {
user.setEmailVerified(true);
user.setUsername(username);
user.setCredentials(List.of(credential));
keycloak.realms().realm(realmName).users().create(user);
var response = keycloak.realms().realm(realmName).users().create(user);
assertEquals(201, response.getStatus());
}
public void deleteUser(String realmName, String username) {
keycloak.realms().realm(realmName).users()
.searchByUsername(username, true).stream()
.map(UserRepresentation::getId)
.forEach(keycloak.realms().realm(realmName).users()::delete);
}
public boolean login(String clientId, String realmName, String username, String password) {

View file

@ -55,8 +55,11 @@ public class KeycloakExtension implements BeforeAllCallback, ParameterResolver {
.withEnv("KEYCLOAK_ADMIN", "admin")
.withEnv("KEYCLOAK_ADMIN_PASSWORD", "password")
.withEnv("KC_LOG_CONSOLE_COLOR", "true")
.withEnv("KC_LOG_LEVEL", "io.kokuwa:trace")
.withEnv("KC_HEALTH_ENABLED", "true")
.withEnv("KC_METRICS_ENABLED", "true")
.withEnv("KC_METRICS_STATS_ENABLED", "true")
.withEnv("KC_METRICS_STATS_INTERVAL", "PT1s")
.withCopyFileToContainer(MountableFile.forHostPath(jar), "/opt/keycloak/providers/metrics.jar")
.withLogConsumer(out -> System.out.print(out.getUtf8String()))
.withExposedPorts(8080)

View file

@ -41,6 +41,14 @@ public class Prometheus {
.sum();
}
public int userCount(String realm) {
return state.stream()
.filter(metric -> Objects.equals(metric.name(), "keycloak_users"))
.filter(metric -> Objects.equals(metric.tags().get("realm"), realm))
.mapToInt(metric -> metric.value().intValue())
.sum();
}
public void scrap() {
state.clear();
Stream.of(client.scrap().split("[\\r\\n]+"))