Skip to content

Commit 62bd0de

Browse files
authored
fix(keycloak): Add support for Keycloak version >=25 (#694)
Keycloak changed the health endpoint to a separate "management port" starting version 25.0.0 as can be read in the [changelogs](https://www.keycloak.org/docs/25.0.0/release_notes/#management-port-for-metrics-and-health-endpoints). The keycloak module in testcontainers-python uses this endpoint for the readiness_probe. Currently compatibility with Keycloak >= 25 is broken. This MR adds compatibility with Keycloak >= 25 by changing the readiness probe to do the following: - Try the health endpoint on the management port - If that gives a ConnectionError try the legacy health endpoint An alternative approach would have been to create a separate LegacyKeycloakContainer class as has been done in other modules, however I think this unnecessarily complicates the user experience. Additionally a public `get_management_url` method has been added to give end-users convenient access to the new Keycloak management endpoint.
1 parent 935693e commit 62bd0de

File tree

2 files changed

+16
-4
lines changed

2 files changed

+16
-4
lines changed

modules/keycloak/testcontainers/keycloak/__init__.py

+15-3
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ class KeycloakContainer(DockerContainer):
3434
3535
>>> from testcontainers.keycloak import KeycloakContainer
3636
37-
>>> with KeycloakContainer(f"quay.io/keycloak/keycloak:24.0.1") as keycloak:
37+
>>> with KeycloakContainer(f"quay.io/keycloak/keycloak:25.0.4") as keycloak:
3838
... keycloak.get_client().users_count()
3939
1
4040
"""
@@ -45,13 +45,15 @@ def __init__(
4545
username: Optional[str] = None,
4646
password: Optional[str] = None,
4747
port: int = 8080,
48+
management_port: int = 9000,
4849
cmd: Optional[str] = _DEFAULT_DEV_COMMAND,
4950
) -> None:
5051
super().__init__(image=image)
5152
self.username = username or os.environ.get("KEYCLOAK_ADMIN", "test")
5253
self.password = password or os.environ.get("KEYCLOAK_ADMIN_PASSWORD", "test")
5354
self.port = port
54-
self.with_exposed_ports(self.port)
55+
self.management_port = management_port
56+
self.with_exposed_ports(self.port, self.management_port)
5557
self.cmd = cmd
5658

5759
def _configure(self) -> None:
@@ -71,10 +73,20 @@ def get_url(self) -> str:
7173
port = self.get_exposed_port(self.port)
7274
return f"http://{host}:{port}"
7375

76+
def get_management_url(self) -> str:
77+
host = self.get_container_host_ip()
78+
port = self.get_exposed_port(self.management_port)
79+
return f"http://{host}:{port}"
80+
7481
@wait_container_is_ready(requests.exceptions.ConnectionError, requests.exceptions.ReadTimeout)
7582
def _readiness_probe(self) -> None:
7683
# Keycloak provides REST API endpoints for health checks: https://www.keycloak.org/server/health
77-
response = requests.get(f"{self.get_url()}/health/ready", timeout=1)
84+
try:
85+
# Try the new health endpoint for keycloak 25.0.0 and above
86+
# See https://www.keycloak.org/docs/25.0.0/release_notes/#management-port-for-metrics-and-health-endpoints
87+
response = requests.get(f"{self.get_management_url()}/health/ready", timeout=1)
88+
except requests.exceptions.ConnectionError:
89+
response = requests.get(f"{self.get_url()}/health/ready", timeout=1)
7890
response.raise_for_status()
7991
if _DEFAULT_DEV_COMMAND in self._command:
8092
wait_for_logs(self, "Added user .* to realm .*")

modules/keycloak/tests/test_keycloak.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from testcontainers.keycloak import KeycloakContainer
33

44

5-
@pytest.mark.parametrize("image_version", ["24.0.1", "18.0"])
5+
@pytest.mark.parametrize("image_version", ["25.0", "24.0.1", "18.0"])
66
def test_docker_run_keycloak(image_version: str):
77
with KeycloakContainer(f"quay.io/keycloak/keycloak:{image_version}") as keycloak_admin:
88
assert keycloak_admin.get_client().users_count() == 1

0 commit comments

Comments
 (0)