#!/usr/bin/env bash # lasuite-drive — post-deps setup hook (operator-2026-05-28 SSO-dep plan §3.2). # # Sibling of tests/lasuite-docs/setup_custom_tests.sh (same impress/La Suite OIDC env contract). # Runs AFTER the generic tiers and AFTER the keycloak dep is deployed + provisioned with a # realm/client/user by the harness. The orchestrator wrote $CCCI_DEPS_FILE with the keycloak dep's # domain + realm + client_id + client_secret + admin creds. # # This hook: (1) inserts the OIDC client secret as the recipe-conventional `oidc_rpcs` swarm secret # (at a bumped version, since abra already generated v1 and swarm forbids overwrite); (2) writes the # OIDC env vars into the running app's .env; (3) triggers an in-place `abra app deploy --force # --chaos` so the new env takes effect. NOT a fresh `abra app new` — the deploy-count guard (DG4.1) # still sees one app_new per app. # # Env supplied by the orchestrator: # CCCI_APP_DOMAIN — the running per-run lasuite-drive app domain # CCCI_RECIPE — "lasuite-drive" # CCCI_DEPS_FILE — JSON (dict shape: {keycloak: {domain, realm, client_id, client_secret, ...}}) set -euo pipefail : "${CCCI_APP_DOMAIN:?missing}" : "${CCCI_DEPS_FILE:?missing}" test -s "$CCCI_DEPS_FILE" || { echo " setup_custom_tests: deps file empty"; exit 1; } KC_DOMAIN=$(jq -r '.keycloak.domain' "$CCCI_DEPS_FILE") KC_REALM=$( jq -r '.keycloak.realm' "$CCCI_DEPS_FILE") KC_CLIENT=$(jq -r '.keycloak.client_id' "$CCCI_DEPS_FILE") KC_SECRET=$(jq -r '.keycloak.client_secret' "$CCCI_DEPS_FILE") [ -n "$KC_DOMAIN" ] && [ "$KC_DOMAIN" != "null" ] || { echo " setup_custom_tests: no keycloak.domain in deps"; exit 1; } [ -n "$KC_SECRET" ] && [ "$KC_SECRET" != "null" ] || { echo " setup_custom_tests: no keycloak.client_secret"; exit 1; } echo " lasuite-drive setup_custom_tests: wiring OIDC against keycloak dep ${KC_DOMAIN}" # 0) Recipe post-deploy setup (lasuite-drive README): the deploy alone does NOT create the MinIO # bucket — `minio-createbuckets` is a `replicas:0` one-shot that must be triggered. The MinIO # storage test asserts the bucket exists, so create it here. We scale the one-shot to 1 directly # (deterministic) rather than the README's finicky `abra app restart` (which it notes "will appear # to fail"). (DB `backend migrate` is the README's other documented step; not run here because # neither current test needs a migrated DB — add it when an upload-via-app test does.) # # `--detach` is REQUIRED: minio-createbuckets is a run-once job (restart_policy: none) that creates # the bucket then EXITS 0, so the service never holds a steady 1/1 replica. A blocking # `docker service scale ...=1` (the default) therefore waits forever for a convergence that can't # happen and hangs the whole run (`|| true` does NOT help — the command hangs, it doesn't fail). # With `--detach` the scale just submits the one-run and returns; the bucket-poll loop below # confirms the bucket was actually created. STACK=$(printf '%s' "$CCCI_APP_DOMAIN" | tr '.' '_') echo " setup: creating MinIO bucket via the minio-createbuckets one-shot (scale 0->1)" docker service scale --detach "${STACK}_minio-createbuckets=1" >/dev/null 2>&1 || true # Wait up to 90s for the one-shot to create the bucket (mc mb drive/drive-media-storage; exit 0). # Poll by checking the bucket directly from the running minio replica container. for i in $(seq 1 30); do MC_CID=$(docker ps -q -f "name=${STACK}_minio.1" | head -1) if [ -n "$MC_CID" ] && docker exec "$MC_CID" sh -c \ 'mc alias set _c http://localhost:9000 "$(cat /run/secrets/minio_ru)" "$(cat /run/secrets/minio_rp)" >/dev/null 2>&1 && mc ls _c/drive-media-storage >/dev/null 2>&1'; then echo " setup: bucket drive-media-storage present after ${i} poll(s)" break fi sleep 3 done # 1) Insert the OIDC client secret at a bumped version (the recipe-maintainer pattern; abra already # generated oidc_rpcs:v1 randomly and swarm forbids overwriting a secret at the same version). ENV_PATH="$HOME/.abra/servers/default/${CCCI_APP_DOMAIN}.env" CUR_VER=$(grep -E '^\s*SECRET_OIDC_RPCS_VERSION=' "$ENV_PATH" | tail -1 | cut -d= -f2 | tr -d '"\r' || echo "v1") NEW_NUM=$(( ${CUR_VER#v} + 1 )) NEW_VER="v${NEW_NUM}" INSERT_LOG=$(abra app secret insert $CCCI_APP_DOMAIN oidc_rpcs $NEW_VER $KC_SECRET --no-input 2>&1) \ || INSERT_LOG=$(script -qec "abra app secret insert $CCCI_APP_DOMAIN oidc_rpcs $NEW_VER $KC_SECRET --no-input" /dev/null 2>&1) \ || { echo " setup_custom_tests: abra app secret insert oidc_rpcs@$NEW_VER failed: $INSERT_LOG"; exit 1; } sed -i "s|^\s*SECRET_OIDC_RPCS_VERSION=.*|SECRET_OIDC_RPCS_VERSION=$NEW_VER|" "$ENV_PATH" echo " setup_custom_tests: oidc_rpcs secret inserted at $NEW_VER (was $CUR_VER)" # 2) Write the OIDC env vars (explicit endpoints — deterministic, no reliance on ${AUTH_DOMAIN} # expansion). Drive's .env.sample templates the endpoints off ${AUTH_DOMAIN}; we set AUTH_DOMAIN too # for completeness and override each endpoint with the concrete keycloak realm URL. [ -z "$(tail -c1 "$ENV_PATH" 2>/dev/null)" ] || printf '\n' >> "$ENV_PATH" write_env () { local key="$1" val="$2" sed -i "/^\s*#\?\s*${key}=/d" "$ENV_PATH" [ -z "$(tail -c1 "$ENV_PATH" 2>/dev/null)" ] || printf '\n' >> "$ENV_PATH" printf '%s=%s\n' "$key" "$val" >> "$ENV_PATH" } write_env AUTH_DOMAIN "$KC_DOMAIN" write_env OIDC_REALM "$KC_REALM" write_env OIDC_OP_JWKS_ENDPOINT "https://${KC_DOMAIN}/realms/${KC_REALM}/protocol/openid-connect/certs" write_env OIDC_OP_AUTHORIZATION_ENDPOINT "https://${KC_DOMAIN}/realms/${KC_REALM}/protocol/openid-connect/auth" write_env OIDC_OP_TOKEN_ENDPOINT "https://${KC_DOMAIN}/realms/${KC_REALM}/protocol/openid-connect/token" write_env OIDC_OP_USER_ENDPOINT "https://${KC_DOMAIN}/realms/${KC_REALM}/protocol/openid-connect/userinfo" write_env OIDC_OP_LOGOUT_ENDPOINT "https://${KC_DOMAIN}/realms/${KC_REALM}/protocol/openid-connect/logout" write_env OIDC_RP_CLIENT_ID "$KC_CLIENT" write_env OIDC_RP_SIGN_ALGO "RS256" write_env OIDC_RP_SCOPES "openid email profile" write_env OIDC_REDIRECT_ALLOWED_HOSTS "[\"https://${KC_DOMAIN}\", \"https://${CCCI_APP_DOMAIN}\"]" # The recipe default acr_values=eidas1 is FranceConnect-specific; keycloak can't satisfy it and it # would break the interactive auth flow. Clear it so the keycloak OIDC client works. write_env OIDC_AUTH_REQUEST_EXTRA_PARAMS "{}" # 3) In-place redeploy so the env + secret take effect (--force: redeploy unchanged recipe; --chaos: # no chaos prompt; --no-input: non-interactive). NOT a fresh app_new. abra app deploy "$CCCI_APP_DOMAIN" --force --chaos --no-input 2>&1 | tail -10 echo " lasuite-drive setup_custom_tests: OIDC wired + redeployed"