From c846d2c1ba20543e4b38bcd08048faf4e57dac02 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Tue, 20 Jan 2026 18:11:13 -0300 Subject: [PATCH] ci: add root Jenkinsfile and update keycloak ldap job --- Jenkinsfile | 77 ++++++++++++++++++++++ services/keycloak/ldap-federation-job.yaml | 50 +++++++++++++- 2 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 Jenkinsfile diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 0000000..4d6b23e --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,77 @@ +// Mirror of ci/Jenkinsfile.titan-iac for multibranch discovery. +pipeline { + agent { + kubernetes { + defaultContainer 'python' + yaml """ +apiVersion: v1 +kind: Pod +spec: + nodeSelector: + hardware: rpi5 + kubernetes.io/arch: arm64 + node-role.kubernetes.io/worker: "true" + containers: + - name: python + image: python:3.12-slim + command: + - cat + tty: true +""" + } + } + environment { + PIP_DISABLE_PIP_VERSION_CHECK = '1' + PYTHONUNBUFFERED = '1' + } + stages { + stage('Checkout') { + steps { + checkout scm + } + } + stage('Install deps') { + steps { + sh 'pip install --no-cache-dir -r ci/requirements.txt' + } + } + stage('Glue tests') { + steps { + sh 'pytest -q ci/tests/glue' + } + } + stage('Resolve Flux branch') { + steps { + script { + env.FLUX_BRANCH = sh( + returnStdout: true, + script: "awk '/branch:/{print $2; exit}' clusters/atlas/flux-system/gotk-sync.yaml" + ).trim() + if (!env.FLUX_BRANCH) { + error('Flux branch not found in gotk-sync.yaml') + } + echo "Flux branch: ${env.FLUX_BRANCH}" + } + } + } + stage('Promote') { + when { + expression { + def branch = env.BRANCH_NAME ?: (env.GIT_BRANCH ?: '').replaceFirst('origin/', '') + return env.FLUX_BRANCH && branch == env.FLUX_BRANCH + } + } + steps { + withCredentials([usernamePassword(credentialsId: 'gitea-pat', usernameVariable: 'GIT_USER', passwordVariable: 'GIT_TOKEN')]) { + sh ''' + set +x + git config user.email "jenkins@bstein.dev" + git config user.name "jenkins" + git remote set-url origin https://${GIT_USER}:${GIT_TOKEN}@scm.bstein.dev/bstein/titan-iac.git + git push origin HEAD:${FLUX_BRANCH} + ''' + } + } + } + } +} diff --git a/services/keycloak/ldap-federation-job.yaml b/services/keycloak/ldap-federation-job.yaml index 303fd9f..3c3f1c1 100644 --- a/services/keycloak/ldap-federation-job.yaml +++ b/services/keycloak/ldap-federation-job.yaml @@ -2,7 +2,7 @@ apiVersion: batch/v1 kind: Job metadata: - name: keycloak-ldap-federation-11 + name: keycloak-ldap-federation-12 namespace: sso spec: backoffLimit: 2 @@ -325,6 +325,54 @@ spec: if status not in (201, 204): raise SystemExit(f"Unexpected group mapper create status: {status}") + def ensure_user_attr_mapper(name: str, ldap_attr: str, user_attr: str): + mapper = None + for c in components: + if c.get("name") == name and c.get("parentId") == ldap_component_id: + mapper = c + break + + payload = { + "name": name, + "providerId": "user-attribute-ldap-mapper", + "providerType": "org.keycloak.storage.ldap.mappers.LDAPStorageMapper", + "parentId": ldap_component_id, + "config": { + "ldap.attribute": [ldap_attr], + "user.model.attribute": [user_attr], + "read.only": ["false"], + "always.read.value.from.ldap": ["false"], + "is.mandatory.in.ldap": ["false"], + }, + } + + if mapper: + payload["id"] = mapper["id"] + payload["parentId"] = mapper.get("parentId", payload["parentId"]) + print(f"Updating LDAP user mapper: {payload['id']} ({name})") + status, _, _ = http_json( + "PUT", + f"{base_url}/admin/realms/{realm}/components/{payload['id']}", + token, + payload, + ) + if status not in (200, 204): + raise SystemExit(f"Unexpected user mapper update status for {name}: {status}") + else: + print(f"Creating LDAP user mapper: {name}") + status, _, _ = http_json( + "POST", + f"{base_url}/admin/realms/{realm}/components", + token, + payload, + ) + if status not in (201, 204): + raise SystemExit(f"Unexpected user mapper create status for {name}: {status}") + + ensure_user_attr_mapper("openldap-email", "mail", "email") + ensure_user_attr_mapper("openldap-first-name", "givenName", "firstName") + ensure_user_attr_mapper("openldap-last-name", "sn", "lastName") + # Cleanup duplicate LDAP federation providers and their child components (mappers, etc). # Keep only the canonical provider we updated/created above. try: