Skip to content

Commit

Permalink
fix(keycloak): Add support for Keycloak version >=25 (#694)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Jinior authored Sep 8, 2024
1 parent 935693e commit 62bd0de
Show file tree
Hide file tree
Showing 2 changed files with 16 additions and 4 deletions.
18 changes: 15 additions & 3 deletions modules/keycloak/testcontainers/keycloak/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class KeycloakContainer(DockerContainer):
>>> from testcontainers.keycloak import KeycloakContainer
>>> with KeycloakContainer(f"quay.io/keycloak/keycloak:24.0.1") as keycloak:
>>> with KeycloakContainer(f"quay.io/keycloak/keycloak:25.0.4") as keycloak:
... keycloak.get_client().users_count()
1
"""
Expand All @@ -45,13 +45,15 @@ def __init__(
username: Optional[str] = None,
password: Optional[str] = None,
port: int = 8080,
management_port: int = 9000,
cmd: Optional[str] = _DEFAULT_DEV_COMMAND,
) -> None:
super().__init__(image=image)
self.username = username or os.environ.get("KEYCLOAK_ADMIN", "test")
self.password = password or os.environ.get("KEYCLOAK_ADMIN_PASSWORD", "test")
self.port = port
self.with_exposed_ports(self.port)
self.management_port = management_port
self.with_exposed_ports(self.port, self.management_port)
self.cmd = cmd

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

def get_management_url(self) -> str:
host = self.get_container_host_ip()
port = self.get_exposed_port(self.management_port)
return f"http://{host}:{port}"

@wait_container_is_ready(requests.exceptions.ConnectionError, requests.exceptions.ReadTimeout)
def _readiness_probe(self) -> None:
# Keycloak provides REST API endpoints for health checks: https://www.keycloak.org/server/health
response = requests.get(f"{self.get_url()}/health/ready", timeout=1)
try:
# Try the new health endpoint for keycloak 25.0.0 and above
# See https://www.keycloak.org/docs/25.0.0/release_notes/#management-port-for-metrics-and-health-endpoints
response = requests.get(f"{self.get_management_url()}/health/ready", timeout=1)
except requests.exceptions.ConnectionError:
response = requests.get(f"{self.get_url()}/health/ready", timeout=1)
response.raise_for_status()
if _DEFAULT_DEV_COMMAND in self._command:
wait_for_logs(self, "Added user .* to realm .*")
Expand Down
2 changes: 1 addition & 1 deletion modules/keycloak/tests/test_keycloak.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from testcontainers.keycloak import KeycloakContainer


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

0 comments on commit 62bd0de

Please sign in to comment.