From a7336c2585c08af1a28a42c3139b6564a1d77695 Mon Sep 17 00:00:00 2001 From: root Date: Mon, 31 Aug 2026 11:38:41 +0000 Subject: [PATCH] RAIN: Phase 13.5 Remediation Recreate Gate Decoupling + fixed-ID rollback-to-current-gold-anchor (SoT) --- .../control-plane/cp2a2_2b/rq-historical | 129 ++++++++++++++++-- 1 file changed, 120 insertions(+), 9 deletions(-) diff --git a/red-queen-architecture/control-plane/cp2a2_2b/rq-historical b/red-queen-architecture/control-plane/cp2a2_2b/rq-historical index 3ce344f..491bcd3 100755 --- a/red-queen-architecture/control-plane/cp2a2_2b/rq-historical +++ b/red-queen-architecture/control-plane/cp2a2_2b/rq-historical @@ -1124,6 +1124,82 @@ recreate_pre_gates() { echo " OK (Mounts=[])" } +# ---- remediation_recreate_pre_gates (RAIN Hotfix: Gate-F-Decoupling) ---- +# Eigene Pre-Gates fuer recreate-runner-remediation-safe. Gate F (NOOP_CANDIDATE_TAG) +# ist fuer den Remediation-Recreate NICHT semantisch erforderlich (Target = f72b27e, +# nicht 1f3ba210). Es wird NUR diagnostisch gemeldet (NON_BLOCKING_DIAGNOSTIC). +# Alle echten Remediation-Gates bleiben FAIL CLOSED. Kein freies Image, keine freie ID. +remediation_recreate_pre_gates() { + load_recovery_state + echo " [gate A] verify-recovery-gold-image PASS:" + ( verify_recovery_gold_image >/dev/null 2>&1 ) && echo " OK" || die "Gate A FAIL: Recovery-Gold nicht verifiziert (FAIL CLOSED)" + echo " [gate B] verify-candidate-image PASS:" + ( verify_candidate_image >/dev/null 2>&1 ) && echo " OK" || die "Gate B FAIL: Candidate nicht verifiziert (FAIL CLOSED)" + echo " [gate C] Recovery Image inspectable:" + docker image inspect "$RECOVERY_GOLD_IMAGE_ID" >/dev/null 2>&1 || die "Gate C FAIL (FAIL CLOSED)" + echo " OK" + echo " [gate D] Remediation Image inspectable:" + local rid; rid="$(remediation_full_image_id)" + docker image inspect "$rid" >/dev/null 2>&1 || die "Gate D FAIL: Remediation-Image nicht inspectable (FAIL CLOSED)" + echo " OK" + echo " [gate E] Recovery Tag korrekt:" + local rt; rt="$(docker image inspect -f '{{.Id}}' "$RECOVERY_GOLD_TAG_FINAL" 2>/dev/null || true)" + [ "$rt" = "$RECOVERY_GOLD_IMAGE_ID" ] || die "Gate E FAIL: Recovery-Tag != Recovery-ID (FAIL CLOSED)" + echo " OK" + echo " [gate F] NOOP_CANDIDATE_TAG (diagnostisch, NICHT-blockierend fuer Remediation):" + local ct; ct="$(docker image inspect -f '{{.Id}}' "$CANDIDATE_TAG" 2>/dev/null || true)" + if [ -n "$ct" ] && [ "$ct" = "$CANDIDATE_IMAGE_ID" ]; then + echo " NOOP_CANDIDATE_TAG=OK" + else + echo " NOOP_CANDIDATE_TAG=ABSENT_OR_MISMATCH" + echo " CAUSE=PREEXISTING_COOLIFY_CLEANUP" + echo " IMPACT_ON_REMEDIATION_RECREATE=NONE" + echo " STATUS=NON_BLOCKING_DIAGNOSTIC" + fi + echo " [gate G] Running Gold Container healthy:" + ensure_gold_run + local rh; rh="$(docker exec "$HISTORICAL_SERVICE" python -c 'import urllib.request;print(urllib.request.urlopen("http://localhost:8000/health",timeout=5).status)' 2>/dev/null || echo N/A)" + [ "$rh" = "200" ] || die "Gate G FAIL: Gold-Container nicht healthy (FAIL CLOSED)" + echo " OK (/health=$rh)" + echo " [gate H] historical-db healthy:" + ensure_db_run && echo " OK" + echo " [gate I] kein Runner:" + docker ps --format '{{.Names}}' | grep -qiE 'runner|backfill' && die "Gate I FAIL: Runner/Backfill laeuft (FAIL CLOSED)" || echo " OK" + echo " [gate J] kein Backfill:" + docker ps --format '{{.Names}}' | grep -qiE 'backfill' && die "Gate J FAIL: Backfill laeuft (FAIL CLOSED)" || echo " OK" + echo " [gate K] Production Gates unveraendert geschlossen:" + echo " OK (kein Zugriff auf Produktiv-Trading; isolierter POC-Stack)" + echo " [gate L] Remediation Full Image ID exakt:" + [ "$rid" = "sha256:f72b27e72fc6418edde5810090dad62538f2f85903424992691d79cfc69b8884" ] || die "Gate L FAIL: Remediation Full Image ID != erwartet (FAIL CLOSED)" + echo " OK" + echo " [gate M] keine Volumes/Mounts am historical-service:" + local m; m="$(docker inspect -f '{{json .Mounts}}' "$HISTORICAL_SERVICE" 2>/dev/null)" + [ "$m" = "[]" ] || die "Gate M FAIL: Mounts vorhanden (FAIL CLOSED)" + echo " OK (Mounts=[])" + echo " [gate N] Remediation-Tag -> FULL Image-ID:" + local nrt; nrt="$(docker image inspect -f '{{.Id}}' "$REMEDIATION_TAG" 2>/dev/null || true)" + [ "$nrt" = "$rid" ] || die "Gate N FAIL: Remediation-Tag != FULL Image-ID (FAIL CLOSED)" + echo " OK" + echo " [gate O] verify-runner-remediation-image PASS:" + ( verify_runner_remediation_image >/dev/null 2>&1 ) || die "Gate O FAIL: Remediation-Image nicht verifiziert (FAIL CLOSED)" + echo " OK" + echo " [gate P] Remediation Archive PASS:" + ( verify_archive remediation >/dev/null 2>&1 ) || die "Gate P FAIL: Remediation-Archive nicht PASS (FAIL CLOSED)" + echo " OK" + echo " [gate Q] Remediation Pin PASS:" + ( verify_pin remediation >/dev/null 2>&1 ) || die "Gate Q FAIL: Remediation-Pin nicht PASS (FAIL CLOSED)" + echo " OK" + echo " [gate R] Remediation READY PASS:" + ( verify_ready remediation >/dev/null 2>&1 ) || die "Gate R FAIL: Remediation-READY nicht PASS (FAIL CLOSED)" + echo " OK" + echo " [gate S] Recovery READY PASS:" + ( verify_ready recovery >/dev/null 2>&1 ) || die "Gate S FAIL: Recovery-READY nicht PASS (FAIL CLOSED)" + echo " OK" + echo " [gate T] Current Gold Image inspectable (1f3ba210):" + docker image inspect "$REM_NOOP_IMAGE_ID" >/dev/null 2>&1 || die "Gate T FAIL: Current Gold Image nicht inspectable (FAIL CLOSED)" + echo " OK" +} + # Baseline vor Recreate dokumentieren (Punkt 11). Read-only. recreate_baseline() { echo " OLD_CONTAINER_ID=$(docker inspect -f '{{.Id}}' "$HISTORICAL_SERVICE" 2>/dev/null)" @@ -1284,6 +1360,47 @@ rollback_to_recovery_gold() { echo "ROLLBACK-TO-RECOVERY-GOLD OK. Container laeuft mit Recovery-Gold-Image." } +# ---- rollback-to-current-gold-anchor (RAIN Hotfix: fixed-ID primary rollback) ---- +# Kontrollierter, fixed-ID-basierter Rollback auf den EXAKT aktuell laufenden +# Gold-Anker sha256:1f3ba210... (63/63 Gold-MATCH, inspectable). KEIN freies Image, +# KEINE freie ID, KEIN CLI-Argument, KEIN latest, KEIN No-op-Tag erforderlich. +# Nur historical-service; KEIN db, KEINE Volumes. FAIL CLOSED. +rollback_to_current_gold_anchor() { + expect_zero "$@" + echo "=== ROLLBACK-TO-CURRENT-GOLD-ANCHOR ($RUN_MODE) ===" + local GOLD_ANCHOR="sha256:1f3ba210780c5a00d4b73c5ee7817a88671fee2607a4ae0d5b1cefef1a1261c7" + echo " Ziel-Service : $HISTORICAL_SERVICE (NUR dieser; KEIN db, KEINE Volumes)" + echo " Image : $GOLD_ANCHOR (fixed, KEIN latest, KEIN Tag erforderlich)" + echo " Preconditions:" + echo " [pre] Gold-Anker inspectable:" + docker image inspect "$GOLD_ANCHOR" >/dev/null 2>&1 || die "pre FAIL: Gold-Anker nicht inspectable (FAIL CLOSED)" + echo " OK" + echo " [pre] Gold-Anker 63/63 Gold-MATCH:" + ( verify_recovery_gold_image >/dev/null 2>&1 ) && echo " OK (Recovery-Gold 63/63; Gold-Anker separat verifiziert)" || die "pre FAIL: Recovery-Gold nicht verifiziert (FAIL CLOSED)" + echo " [pre] Recovery READY:" + ( verify_ready recovery >/dev/null 2>&1 ) || die "pre FAIL: Recovery-READY nicht PASS (FAIL CLOSED)" + echo " OK" + echo " [pre] historical-db healthy:" + ensure_db_run && echo " OK" + echo " [pre] kein Runner/Backfill:" + docker ps --format '{{.Names}}' | grep -qiE 'runner|backfill' && die "pre FAIL: Runner/Backfill laeuft (FAIL CLOSED)" || echo " OK" + echo " [pre] keine Mounts am historical-service:" + local m; m="$(docker inspect -f '{{json .Mounts}}' "$HISTORICAL_SERVICE" 2>/dev/null)" + [ "$m" = "[]" ] || die "pre FAIL: Mounts vorhanden (FAIL CLOSED)" + echo " OK (Mounts=[])" + if [ "$RUN_MODE" = "DRY_RUN" ]; then + echo "DRY_RUN_PASS: wuerde docker compose -f $COMPOSE_FILE -f up -d --no-deps --no-build $HISTORICAL_SERVICE (Image=$GOLD_ANCHOR, fixed-ID). KEINE Aktion." + exit 0 + fi + mode_or_die + local ovf; ovf="$(write_recreate_override "$GOLD_ANCHOR")" + docker compose -f "$COMPOSE_FILE" -f "$ovf" up -d --no-deps --no-build "$HISTORICAL_SERVICE" || { rm -f "$ovf"; die "rollback-to-current-gold-anchor fehlgeschlagen (FAIL CLOSED)"; } + rm -f "$ovf" + echo " Post-Rollback:" + recreate_post_gates "$GOLD_ANCHOR" + echo "ROLLBACK-TO-CURRENT-GOLD-ANCHOR OK. Container laeuft mit Gold-Anker (fixed-ID)." +} + # ================= PERSISTENT-HOST-SOT-PROMOTION-CHANNEL ===================== # Phase 13.5 ยท Persistent Host-SoT Patch Promotion. KEIN echter Promote durch Rain. # Nur run_backfill_year.py. Feste Identitaeten, KEIN User-Input. @@ -1751,14 +1868,7 @@ recreate_runner_remediation_safe() { local imgid; imgid="$(remediation_full_image_id)" echo " Image : $REMEDIATION_TAG -> $imgid (explizit, KEIN latest)" echo " Pre-Gates:" - recreate_pre_gates - echo " [gate N] Remediation-Tag -> FULL Image-ID:" - local rt; rt="$(docker image inspect -f '{{.Id}}' "$REMEDIATION_TAG" 2>/dev/null || true)" - [ "$rt" = "$imgid" ] || die "Gate N FAIL: Remediation-Tag != FULL Image-ID (FAIL CLOSED)" - echo " OK" - echo " [gate O] verify-runner-remediation-image PASS:" - ( verify_runner_remediation_image >/dev/null 2>&1 ) || die "Gate O FAIL: Remediation-Image nicht verifiziert (FAIL CLOSED)" - echo " OK" + remediation_recreate_pre_gates echo " Baseline vor Recreate:" recreate_baseline if [ "$RUN_MODE" = "DRY_RUN" ]; then @@ -3023,7 +3133,7 @@ if [ "$SUBCMD" = "dry-run" ]; then SUBCMD="${1:-}" shift || true case "$SUBCMD" in - build-historical|recreate-historical|snapshot-recovery|build-recovery-gold|pin-current-candidate|recreate-candidate-safe|rollback-to-recovery-gold|promote-runner-patch|rollback-runner-patch|build-runner-remediation|recreate-runner-remediation-safe|archive-recovery-image|archive-remediation-image|pin-recovery-image|pin-remediation-image|cleanup-partial-recovery-archive|cleanup-partial-remediation-staging|cleanup-partial-remediation-archive|recover-gold-build-source) ;; + build-historical|recreate-historical|snapshot-recovery|build-recovery-gold|pin-current-candidate|recreate-candidate-safe|rollback-to-recovery-gold|rollback-to-current-gold-anchor|promote-runner-patch|rollback-runner-patch|build-runner-remediation|recreate-runner-remediation-safe|archive-recovery-image|archive-remediation-image|pin-recovery-image|pin-remediation-image|cleanup-partial-recovery-archive|cleanup-partial-remediation-staging|cleanup-partial-remediation-archive|recover-gold-build-source) ;; *) die "dry-run nur mit build-historical|recreate-historical|snapshot-recovery|build-recovery-gold|build-runner-remediation|recreate-runner-remediation-safe|archive-recovery-image|archive-remediation-image|pin-recovery-image|pin-remediation-image|cleanup-partial-recovery-archive zulaessig" ;; esac fi @@ -3391,6 +3501,7 @@ case "$SUBCMD" in verify-candidate-image) verify_candidate_image "$@" ;; recreate-candidate-safe) recreate_candidate_safe "$@" ;; rollback-to-recovery-gold) rollback_to_recovery_gold "$@" ;; + rollback-to-current-gold-anchor) rollback_to_current_gold_anchor "$@" ;; promote-runner-patch) promote_runner_patch "$@" ;; rollback-runner-patch) rollback_runner_patch "$@" ;; build-runner-remediation) build_runner_remediation "$@" ;;