warm_reconcile.py: per-spec setup hook + health_domain; SPECS[traefik] (stateful=False, version-rollback-only, _traefik_setup preserves wildcard-cert/ file-provider config, health on routed dashboard host). keycloak path unchanged. proxy.nix: deploy-proxy.service now execs warm_reconcile.py traefik. ZERO-disruption migration (traefik already at latest 5.1.1+v3.6.15; pre-seeded TYPE+last_good → clean no-op converge; traefik 200 + keycloak-through-traefik 200 + 0 failed). 65 unit pass. Per operator out: code+converge delivered; destructive rollback (brief TLS blip) = Adversary's required cold proof. Closes the W0.10a tracked-open. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
46 lines
2.3 KiB
Nix
46 lines
2.3 KiB
Nix
# Reverse proxy = the canonical Co-op Cloud `traefik` recipe, deployed via abra in
|
|
# wildcard / file-provider mode (wildcard cert as ssl_cert/ssl_key swarm secrets,
|
|
# LETS_ENCRYPT_ENV empty => NO ACME, no DNS token). See DECISIONS.md "Proxy: real coop-cloud/traefik".
|
|
# Phase-1c: the cert at CERT_DIR is sops-decrypted from git (cc-ci-secrets) at activation
|
|
# (modules/secrets.nix wildcard_cert/wildcard_key), NOT an out-of-band operator file drop.
|
|
#
|
|
# Phase-2w / WC1.1: traefik is now UNPINNED + health-gated like keycloak — the deploy is driven by
|
|
# the shared `runner/warm_reconcile.py traefik` (STATELESS = version-rollback-only, NO snapshot):
|
|
# record last-good version → deploy latest tag → health-gate (a ROUTED host, the dashboard
|
|
# ci.commoninternet.net, returns 200) → healthy commits last-good / unhealthy rolls back to last-good
|
|
# + alert. traefik's wildcard-cert/file-provider config (ssl_cert/ssl_key secrets, WILDCARDS_ENABLED,
|
|
# COMPOSE_FILE) is preserved EXACTLY by the spec's `setup` (warm_reconcile._traefik_setup). The
|
|
# runner/ tree is copied into the nix store → D8-clean; recipe fetched at runtime → closure stable.
|
|
#
|
|
# Idempotent-RECONCILE systemd oneshot (unchanged unit name `deploy-proxy` — other modules order
|
|
# after it): converges every activation/boot, self-healing drift. No run-once sentinel.
|
|
{ pkgs, ... }:
|
|
let
|
|
runnerSrc = ../../runner;
|
|
reconcile = pkgs.writeShellApplication {
|
|
name = "cc-ci-reconcile-proxy";
|
|
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 traefik
|
|
'';
|
|
};
|
|
in
|
|
{
|
|
systemd.services.deploy-proxy = {
|
|
description = "Reconcile the Co-op Cloud traefik proxy (wildcard/no-ACME, health-gated) via abra";
|
|
after = [ "swarm-init.service" "docker.service" "network-online.target" ];
|
|
requires = [ "swarm-init.service" "docker.service" ];
|
|
wants = [ "network-online.target" ];
|
|
wantedBy = [ "multi-user.target" ];
|
|
environment.HOME = "/root";
|
|
serviceConfig = {
|
|
Type = "oneshot";
|
|
RemainAfterExit = true;
|
|
# Generous: a traefik (re)deploy + health-gate; rollback on an unhealthy upgrade.
|
|
TimeoutStartSec = "900";
|
|
ExecStart = "${reconcile}/bin/cc-ci-reconcile-proxy";
|
|
};
|
|
};
|
|
}
|