diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..06731ffc6490924117096cd76bc53f19405e7369 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.venv/ +.pyenv/ + +*.pyc \ No newline at end of file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e987d7fdfd9bd9feb053b15f406934cae7345f3e..57b17b8b15badf7a54e8e40b3e584e0b6a45f94a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -37,6 +37,7 @@ workflow: stages: - build + - test - release build image: @@ -66,6 +67,22 @@ build image: - echo ${TARGET_ENV} - crane cp ${CI_REGISTRY_IMAGE}:${DOCKER_TAG} ${CI_REGISTRY_IMAGE}:${TARGET_ENV} +test image: + stage: test + image: + name: ghcr.io/astral-sh/uv:0.6-python3.12-bookworm + needs: + - job: build image + variables: + KEYCLOAK_HOSTNAME: keycloak + services: + - name: "${CI_REGISTRY_IMAGE}:${DOCKER_TAG}" + alias: "$KEYCLOAK_HOSTNAME" + before_script: + - bash ./tests/healthcheck_for_tests.sh + script: + - uv run --with pytest --with python-keycloak pytest -v tests + release latest image: stage: release extends: .tag image diff --git a/Dockerfile b/Dockerfile index 56a279bd81c1d8ba74d6e159d005d9de54f221b2..7d3391535518d0ee2c6a0162f3d06d31acb6a7fe 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,7 +3,15 @@ ARG KEYCLOAK_VERSION=26.1 FROM quay.io/keycloak/keycloak:${KEYCLOAK_VERSION} AS keycloak COPY import /opt/keycloak/data/import - COPY healthcheck_keycloak.sh /opt/keycloak/bin/ ENTRYPOINT ["/opt/keycloak/bin/kc.sh"] +CMD ["start-dev", "--import-realm"] + +ENV KC_HEALTH_ENABLED=true +ENV KC_METRICS_ENABLED=true + +EXPOSE 8080 9000 + +HEALTHCHECK --interval=10s --timeout=10s --start-period=30s --retries=5 \ + CMD "/opt/keycloak/bin/healthcheck_keycloak.sh" diff --git a/tests/healthcheck_for_tests.sh b/tests/healthcheck_for_tests.sh new file mode 100644 index 0000000000000000000000000000000000000000..3bdfd5be50ae605b7cb4ae5197825faa2e9785cb --- /dev/null +++ b/tests/healthcheck_for_tests.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +if [ -z "$KEYCLOAK_HOSTNAME" ]; then + echo "KEYCLOAK_HOSTNAME is not set. Exiting ..." + exit 1 +fi + +DURATION=60 +INTERVAL=5 + +HEALTH_CHECK_URL="http://${KEYCLOAK_HOSTNAME}:9000/health/live" +END_TIME=$(( $(date +%s) + DURATION )) + +check_health() { + curl -s "$HEALTH_CHECK_URL" > /dev/null +} + +while [ $(date +%s) -lt $END_TIME ]; do + if check_health; then + echo "Health endpoint is responding correctly." + exit 0 + else + echo "Health endpoint is not responding. Retrying in $INTERVAL seconds ..." + sleep "$INTERVAL" + fi +done + +echo "Timeout reached. Health endpoint did not respond within $DURATION seconds." +exit 1 diff --git a/tests/test_keycloak.py b/tests/test_keycloak.py new file mode 100644 index 0000000000000000000000000000000000000000..1e32a7d27fd67b2554f06de70f11cde958b00c98 --- /dev/null +++ b/tests/test_keycloak.py @@ -0,0 +1,45 @@ +import os + +import keycloak +import pytest + +users ={ + 'admin': { + 'sub': 'c97facc2-92ec-4fa6-80cf-a08ed957255b', + 'preferred_username': 'admin', + 'password': 'password', + 'email': 'admin@example.com', + 'given_name': 'AdminFN', + 'family_name': 'AdminLN', + }, + 'test': { + 'sub': '33305763-5e61-4458-94bc-8098e9288a73', + 'preferred_username': 'test', + 'password': 'password', + 'email': 'test@example.com', + 'given_name': 'TestFN', + 'family_name': 'TestLN', + }, +} + +@pytest.fixture(scope='session') +def keycloak_hostname(): + return os.environ.get('KEYCLOAK_HOSTNAME', 'localhost') + +@pytest.fixture(scope='session') +def nomad_public(keycloak_hostname): + return keycloak.KeycloakOpenID( + server_url=f'http://{keycloak_hostname}:8080', + realm_name='fairdi_nomad_test', + client_id='nomad_public', + ) + +@pytest.mark.parametrize('user', users.values(), ids=users.keys()) +def test_user(nomad_public, user): + tokens = nomad_public.token(user['preferred_username'], user['password']) + access_token = tokens.get('access_token') + token_info = nomad_public.decode_token(access_token) + + ignored_keys = {'password'} + for key in user.keys() - ignored_keys: + assert token_info.get(key) == user[key]