# Phase 2w / WC1+WC1.1+WC1.2 — a live-warm, shared keycloak SSO provider, auto-updating to LATEST # with a pre-deploy safety gate + post-deploy health-gated rollback. Deployed via abra at a STABLE # domain (distinct from cold per-run `-<6hex>`; see DECISIONS.md Phase-2w). SSO-dependent # recipe runs use this one instance (per-run namespaced realm, created+deleted) instead of # co-deploying a fresh keycloak each run. # # The reconcile logic lives in `runner/warm_reconcile.py` (Python — reuses warmsnap/abra/lifecycle so # there is ONE snapshot impl, also used by the runner for WC5). The runner/ tree is copied into the # nix store, so this is D8-clean (no dependence on the /root/cc-ci checkout) and the recipe is fetched # at *runtime* → the nix closure stays byte-identical regardless of which keycloak version is live # (UNPINNED; the kcVersion pin is gone). # # Idempotent RECONCILE oneshot (like deploy-proxy / swarm-init): converges every activation/boot. # WC1.2 safety gate (major / manual-migration → hold + alert, no churn) runs BEFORE WC1.1's # health-gated upgrade-with-rollback (snapshot keycloak's data volume before upgrade; restore + # redeploy prior version on an unhealthy upgrade). Alerts are sentinel JSON under # /var/lib/ci-warm/alerts/ relayed by the Builder loop (see DECISIONS). { pkgs, ... }: let runnerSrc = ../../runner; reconcile = pkgs.writeShellApplication { name = "cc-ci-reconcile-warm-keycloak"; runtimeInputs = with pkgs; [ abra docker git curl jq gnused gnugrep gnutar coreutils ]; text = '' export HOME=/root exec ${pkgs.python3}/bin/python3 ${runnerSrc}/warm_reconcile.py keycloak ''; }; in { systemd.services.warm-keycloak = { description = "Reconcile the live-warm shared keycloak SSO provider (WC1/WC1.1/WC1.2) via abra"; after = [ "deploy-proxy.service" "swarm-init.service" "docker.service" "network-online.target" ]; requires = [ "swarm-init.service" "docker.service" ]; wants = [ "deploy-proxy.service" "network-online.target" ]; wantedBy = [ "multi-user.target" ]; environment.HOME = "/root"; serviceConfig = { Type = "oneshot"; RemainAfterExit = true; # Generous: a cold keycloak boot (JVM + DB migration) can take ~10min, and a health-gated # upgrade may snapshot + deploy + (rollback) within one run. TimeoutStartSec = "1800"; ExecStart = "${reconcile}/bin/cc-ci-reconcile-warm-keycloak"; }; }; }