Add additional metrics for user/client/session count (#31)
This commit is contained in:
parent
566f31ddc2
commit
37dcc07309
11 changed files with 312 additions and 6 deletions
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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]+"))
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue