From 999dd0d564104293fb222eb360d92df56fd67697 Mon Sep 17 00:00:00 2001 From: autonomic-bot Date: Fri, 29 May 2026 19:32:48 +0100 Subject: [PATCH] =?UTF-8?q?fix(2):=20Q4.2=20mumble=20=E2=80=94=20CHAOS=5FB?= =?UTF-8?q?ASE=5FDEPLOY=20meta=20flag=20for=20chaos=20base=20deploy=20(cle?= =?UTF-8?q?an-tree=20gate)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mumble's pinned base deploy (prev version 0.2.0) FATAs 'has locally unstaged changes' because install_steps provides an untracked compose.host-ports.yml. New recipe_meta CHAOS_BASE_DEPLOY=True + lifecycle._recipe_meta_flag + deploy_app branch -> base uses chaos (skips clean-tree/lint, deploys the checked-out pinned version, not LATEST), mirroring the lightweight-tag chaos-base path. DECISIONS.md records the full mumble enrollment design. Co-Authored-By: Claude Opus 4.8 (1M context) --- machine-docs/DECISIONS.md | 31 +++++++++++++++++++++++++++++++ runner/harness/lifecycle.py | 26 ++++++++++++++++++++++++++ tests/mumble/recipe_meta.py | 7 +++++++ 3 files changed, 64 insertions(+) diff --git a/machine-docs/DECISIONS.md b/machine-docs/DECISIONS.md index a01ffdf..563e08f 100644 --- a/machine-docs/DECISIONS.md +++ b/machine-docs/DECISIONS.md @@ -857,3 +857,34 @@ under an active throttle. Therefore the gate's full-lifecycle green still depend the install-tier deploy's first download (achievable after a rate-limit cooldown with a single clean run). The recipe PR is filed as a deferred robustness follow-up (Q4.7b), mirroring the Q3.2b/immich pattern; Adversary/operator weigh whether it gates Phase-2 DONE. + +## mumble: TCP/voice recipe enrollment — mumbleweb HTTP readiness + host-ports + CHAOS_BASE_DEPLOY (2026-05-29) + +**Decision (settled):** mumble is a non-HTTP TLS voice server (port 64738). To enroll it in cc-ci's +HTTP-readiness + on-host (cc-ci-run) test model, deploy it with the two upstream overlays +`COMPOSE_FILE=compose.yml:compose.mumbleweb.yml:compose.host-ports.yml` (recipe_meta.EXTRA_ENV): +- **compose.mumbleweb.yml** — the upstream mumble-web HTTP client, routed through Traefik on the app + domain. Gives the generic harness a real HTTP serving/readiness signal (HEALTH_PATH "/") AND the + web_client.py parity surface. Present in every published mumble version. +- **compose.host-ports.yml** — publishes 64738 (tcp+udp, mode:host) on the cc-ci host, so the on-host + protocol tests connect to 127.0.0.1:64738. The voice server has NO HTTP API and cc-ci's Traefik only + exposes 80/443 (no `mumble` TCP entrypoint; the gateway forwards 443 only, out of our control), so a + host-published port is the reachable path. The `proxy` overlay is attachable (an ephemeral-container + path was considered) but host-ports is simpler and needs no extra image. + +**Two enrollment hazards + their fixes:** +1. The upstream `compose.host-ports.yml` exists only from version **1.0.0+**, but the upgrade tier's + base deploy is the **previous** published version (0.2.0+), which predates it → COMPOSE_FILE fails + to resolve on the base deploy. Fix: `tests/mumble/install_steps.sh` provides a cc-ci-owned identical + `compose.host-ports.yml` to the recipe checkout when absent (no-op when the version ships it natively). +2. That provided file is UNTRACKED in the older checkout → abra's PINNED base-deploy clean-tree check + FATAs ("has locally unstaged changes"). Fix: new recipe_meta flag **`CHAOS_BASE_DEPLOY=True`** + + harness support (`lifecycle._recipe_meta_flag` + a `deploy_app` branch) → the base deploy uses chaos + (skips lint/clean-tree, deploys the EXPLICITLY-checked-out pinned version — not LATEST), mirroring the + existing lightweight-tag chaos-base mechanism. HC1/deploy-count unaffected (upgrade still chaos-redeploys + to PR-head; base chaos-version=prev-commit != head → real crossover). + +**P4 (backup data-integrity):** mumble persists server state in `/data/mumble-server.sqlite` (the exact +file the recipe's backupbot hooks `.backup`/restore). ops.py seeds a `ci_marker` row there (using +`PRAGMA busy_timeout` to wait out the running murmur server's transient sqlite locks), backup, drop, +restore, assert the row survived. diff --git a/runner/harness/lifecycle.py b/runner/harness/lifecycle.py index 0867a6d..fd6855b 100644 --- a/runner/harness/lifecycle.py +++ b/runner/harness/lifecycle.py @@ -90,6 +90,19 @@ def _recipe_extra_env(recipe: str, domain: str) -> dict[str, str]: return {str(k): str(v) for k, v in (ee or {}).items()} +def _recipe_meta_flag(recipe: str, key: str) -> bool: + """Read a boolean flag from tests//recipe_meta.py (e.g. CHAOS_BASE_DEPLOY). Returns + False if the recipe ships no meta or the flag is absent/falsey. Trusted in-repo exec, same as + _recipe_extra_env.""" + path = os.path.join(os.path.dirname(__file__), "..", "..", "tests", recipe, "recipe_meta.py") + if not os.path.exists(path): + return False + ns: dict = {} + with open(path) as fh: + exec(compile(fh.read(), path, "exec"), ns) # noqa: S102 (trusted, in-repo) + return bool(ns.get(key)) + + def _record_deploy() -> None: """Increment the per-run deploy counter (DG4.1: one deploy per run). No-op unless the orchestrator set CCCI_DEPLOY_COUNT_FILE — so it never affects standalone/manual use.""" @@ -217,6 +230,19 @@ def deploy_app( flush=True, ) chaos = True + # A recipe may force a chaos base deploy via recipe_meta CHAOS_BASE_DEPLOY=True when cc-ci adds + # an untracked compose overlay to the recipe checkout (e.g. mumble's host-ports.yml, provided + # by install_steps for older versions that predate it). The untracked file makes abra's + # pinned-deploy clean-tree check FATA ('has locally unstaged changes'); chaos skips lint + + # the clean-tree gate and deploys the EXPLICITLY-checked-out pinned version (we already ran + # recipe_checkout(version) above) — NOT latest. Same mechanism as the lightweight-tag branch. + elif _recipe_meta_flag(recipe, "CHAOS_BASE_DEPLOY"): + print( + f" deploy_app({recipe}@{version}): CHAOS_BASE_DEPLOY set → chaos base deploy of the " + "checked-out pinned version (skips clean-tree/lint; deploys version, not LATEST)", + flush=True, + ) + chaos = True # Pin DOMAIN to the run domain explicitly. `abra app new -D` fills it for recipes whose # .env.sample uses a literal placeholder, but NOT for ones using a `{{ .Domain }}` Go-template # (this abra version leaves it unexpanded → deploy fails "can't evaluate field Domain"). Setting diff --git a/tests/mumble/recipe_meta.py b/tests/mumble/recipe_meta.py index 0e244f4..d2ed789 100644 --- a/tests/mumble/recipe_meta.py +++ b/tests/mumble/recipe_meta.py @@ -16,6 +16,13 @@ HEALTH_PATH = "/" # mumble-web client UI HEALTH_OK = (200,) + +# install_steps.sh provides compose.host-ports.yml to recipe versions that predate it (the upgrade +# tier's base deploy is the previous published version, 0.2.0+, which lacks the upstream overlay). +# That untracked file makes abra's PINNED base-deploy clean-tree check FATA, so deploy the +# explicitly-checked-out pinned version with chaos (skips lint/clean-tree; deploys the version, not +# LATEST). No-op for the upgrade tier (already a PR-head chaos redeploy). See DECISIONS.md. +CHAOS_BASE_DEPLOY = True DEPLOY_TIMEOUT = 900 # two images to pull (mumble-server + mumble-web) on a cold node HTTP_TIMEOUT = 300