fix(reboot): harden public maintenance fallback
All checks were successful
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 58s
CD Pipeline / build-and-deploy (push) Successful in 4m27s
CD Pipeline / post-deploy-checks (push) Successful in 1m44s
All checks were successful
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 58s
CD Pipeline / build-and-deploy (push) Successful in 4m27s
CD Pipeline / post-deploy-checks (push) Successful in 1m44s
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# AWOOOI 全棧冷啟動與主機重啟 SOP
|
||||
|
||||
> Version: v1.103
|
||||
> Version: v1.104
|
||||
> Last updated: 2026-07-03 Asia/Taipei
|
||||
> Scope: 99 / 110 / 111 / 112 / 120 / 121 / 188 全棧重啟恢復。112 仍是 Kali / VM guest 訊號,但 2026-06-30 全主機重啟後已納入 10 分鐘 SLO 的必要 boot / power signal;此納入不代表授權任何破壞性 runtime apply。
|
||||
|
||||
@@ -40,6 +40,8 @@ v1.102 production scorecard / deploy cutover rule:2026-07-03 03:55 production
|
||||
|
||||
v1.103 Windows99 console verify artifact rule:若 99 可 ping / RDP / Hyper-V VMConnect,但 SSH BatchMode 與 WinRM 都不可用,不能停在「等人工」。必須把 console channel 的 `windows99-vmware-autostart.ps1 -Mode Verify` stdout 存成 no-secret artifact,先跑 `python3.11 scripts/reboot-recovery/validate-windows99-console-verify-artifact.py --input <console-stdout.txt> --output <windows99-vmware-verify.txt>`,再用 normalized artifact 重跑 reboot SLO scorecard。validator 必須檢查 `AWOOOI_WINDOWS99_VMWARE_AUTOSTART=1`、`MODE=Verify`、每個 required alias 的 `VMX` / `VM_POWER`、VMware services、autostart task、Windows Update policy 與 ready flags;若看到 secret-like key、欄位缺失或非 Verify marker 必須 fail-closed。此路徑只接受 Verify stdout,不授權 Windows password collection、VM power change、Windows Update policy apply、service restart、host reboot 或 registry edit。
|
||||
|
||||
v1.104 public maintenance location-level intercept rule:deploy marker 切換或主機重啟期間,public API / SLO route 只要回 `502` 且沒有 `X-AWOOOI-Fallback=local-maintenance` 與維護頁 body,即使稍後恢復,也必須列為 P0 public UX blocker。`ops/nginx/awoooi.wooo.work.conf`、`k8s/nginx/awoooi-prod.conf` 與 `ops/maintenance/nginx-502-maintenance-snippet.conf` 必須使用 `error_page 502 503 504 =503 /__awoooi-maintenance.html;`,讓 fallback 明確回 `503` / `Retry-After`;所有 proxy 到 AWOOOI API / Web 的 `location` 必須各自宣告 `proxy_intercept_errors on;` 與同一條 `error_page`,不得只依賴 server 層繼承。`public-maintenance-fallback-probe.py` 需將帶 fallback header/body 的 `502` / `503` / `504` 判定為 maintenance fallback serving;只有缺 header/body 的 5xx 才算 `raw_5xx_without_maintenance_fallback`。完成條件仍是 marker-time public route watch 不再讀到 raw 502;source contract 通過不等於 live Nginx runtime 已套用。
|
||||
|
||||
2026-07-02 110 control-path / Harbor recovery receipt rule:若 Gitea Harbor repair queue 仍保留 `harbor_110_remote_ssh_publickey_auth_stalled`、remote-control unavailable、jobs stale 或 historical failure,但同一輪本地證據同時證明 `wooo` command path ready、110 local Harbor `/v2/` ready、public/internal registry `/v2/` 回 `401`,則該 Gitea Harbor repair 失敗只能列為 historical queue metadata,不得再當成 current SSH blocker。必須用 `/api/v1/agents/harbor-registry-controlled-recovery-receipt` 或同等 validator 合併 `diagnose-110-ssh-publickey-auth.sh`、`recover-110-control-path-and-harbor-local.sh --check`、public Gitea queue readback 與 registry `/v2/` verifier,並把機器可讀結果寫入 `docs/operations/harbor-110-control-path-recovery-readback-2026-07-02.snapshot.json` 類型的 snapshot。2026-07-02 live receipt 顯示:public/internal registry `/v2/` 均為 `401`、latest visible CD `#4335` 為 `Success`、Gitea Harbor repair failure 已是 `historical_after_latest_cd_success=true`;active blockers 收斂為 110 controlled CD lane config / binary / registration / service guardrail、active action container pressure,以及 Gitea CD jobs head-SHA / stale readback mismatch。若 local-console output 只有 `AWOOOI_110_CONTROLLED_CD_LANE_READY` marker,non110 runner parser 不得從 110 `BLOCKER` 行推導 non110 blocker;non110 只有看到 `AWOOOI_NON110_RUNNER_READY` marker 才能列入 active blocker。
|
||||
|
||||
2026-07-02 110 controlled CD lane fail-closed enforcer staging rule:110 runner 壓力事故後,legacy / generic runner 仍必須 fail-closed;但 `awoooi-cd-lane-drain.service` 的非 secret staging artifact 不得再被 enforcer 無差別封回 stub。`scripts/reboot-recovery/enforce-110-runner-failclosed.sh` 只有在 `config.yaml` 符合 `capacity <= 1`、只含 `awoooi-host:host` 與 `awoooi-ubuntu:docker://192.168.0.110:5000/awoooi/ci-runner:act-22.04`、binary 是 executable ELF、systemd unit 具備 `ConditionPathExists=/home/wooo/awoooi-cd-lane-drain/data/.runner`、`CPUAccounting` / `MemoryAccounting` / `TasksAccounting` / `NoNewPrivileges` 等 guardrail,且 service `inactive`、`MainPID=0`、未 enabled / 未 masked 時,才可保留 drain config / binary / unit,並輸出 `CONTROLLED_DRAIN_STAGING_ALLOWED=1` 與 textfile metric。此 staging 規則不得讀 token、不得讀 `.runner` 內容、不得註冊 runner、不得啟動 service;若 registration 缺失,readiness verifier 仍必須只留下 `controlled_cd_lane_registration_missing` / `controlled_cd_lane_service_not_active` 類 blocker。若 `CONTROLLED_DRAIN_STAGING_ALLOWED=0` 且 config / binary 又被搬走,優先修 source enforcer / unit guardrail,不要手工反覆補同一組 artifact。
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
## L0 受控套用
|
||||
|
||||
1. 將 `ops/maintenance/maintenance.html` 部署到 edge host 的 `/var/www/maintenance/maintenance.html`。
|
||||
2. 在每個 public `server {}` 內 include `ops/maintenance/nginx-502-maintenance-snippet.conf` 的等價內容;AWOOOI source conf `ops/nginx/awoooi.wooo.work.conf` 與 `k8s/nginx/awoooi-prod.conf` 已固定 `proxy_intercept_errors on`、`/__awoooi-maintenance.html` 與 `X-AWOOOI-Fallback: local-maintenance`。
|
||||
2. 在每個 public `server {}` 內 include `ops/maintenance/nginx-502-maintenance-snippet.conf` 的等價內容;AWOOOI source conf `ops/nginx/awoooi.wooo.work.conf` 與 `k8s/nginx/awoooi-prod.conf` 已固定 `proxy_intercept_errors on`、`error_page 502 503 504 =503 /__awoooi-maintenance.html` 與 `X-AWOOOI-Fallback: local-maintenance`。所有會 `proxy_pass` 到 AWOOOI API / Web 的 `location` 也必須明確帶同一組 `proxy_intercept_errors` / `error_page`,不得只依賴 server 層繼承。
|
||||
3. `nginx -t` 通過後才 reload Nginx。
|
||||
4. 用壞 upstream staging vhost 或暫存 upstream 驗證 `X-AWOOOI-Fallback: local-maintenance`,不得用 production DB / app restart 當測試手段。
|
||||
|
||||
@@ -37,7 +37,7 @@ L1 active 條件:
|
||||
|
||||
## 驗證
|
||||
|
||||
- L0:對測試 vhost 讀回 `200` 或 `503` 的維護頁 HTML,且 header `X-AWOOOI-Fallback=local-maintenance` 存在。
|
||||
- L0:對測試 vhost 讀回 `503` 的維護頁 HTML,且 header `X-AWOOOI-Fallback=local-maintenance` 與 `Retry-After` 存在;probe 若讀到 `502` / `503` / `504` 但同時有 fallback header/body,必須分類為 maintenance fallback,不得算 raw 5xx。
|
||||
- L1:從外部網路讀回靜態維護頁,且不經過 99 / 110 / 188 / 120 / 121 / 112。
|
||||
- Recovery:主要 upstream 連續 2 分鐘健康後切回正式 origin;Telegram 發送 recovery 通知,並在 cold-start scorecard 留下 readback。
|
||||
|
||||
@@ -73,6 +73,15 @@ python3 scripts/reboot-recovery/reboot-auto-recovery-slo-scorecard.py \
|
||||
- Source verifier:`scripts/reboot-recovery/tests/test_reboot_p0_operational_contract.py` 必須確認上述 source 不可退回 `/50x.html`。
|
||||
- Runtime verifier:在 edge host 用 staging vhost / `nginx -t` / header readback 證明 `X-AWOOOI-Fallback=local-maintenance`;通過前不得宣稱 production runtime 已接管 raw 502。
|
||||
|
||||
## 2026-07-03 API / SLO cutover raw 502 hardening
|
||||
|
||||
Gitea CD `#4517` 在 deploy marker 切換期間再次讀到 production API / SLO route 短暫 `502`,且沒有 maintenance body / fallback header。修正口徑:
|
||||
|
||||
- `error_page` 必須使用 `=503`,讓維護頁帶明確 `503` / `Retry-After`,而不是回傳裸 `502`。
|
||||
- AWOOOI API、SSE、WebSocket、front-end 與 static proxy location 必須各自明確宣告 `proxy_intercept_errors on` 與同一條 `error_page`。
|
||||
- `public-maintenance-fallback-probe.py` 必須把帶 `X-AWOOOI-Fallback=local-maintenance` 與維護頁 body 的 `502` / `503` / `504` 判成 fallback serving;只有缺 header/body 的 5xx 才是 `raw_5xx_without_maintenance_fallback`。
|
||||
- 驗收仍以 marker-time public route watch 為準;source verifier 通過不等於 live Nginx runtime 已 reload。
|
||||
|
||||
## 2026-07-02 API runtime artifact overlay
|
||||
|
||||
`/api/v1/agents/reboot-auto-recovery-slo-scorecard` 不得只依賴 committed snapshot 判斷 public maintenance fallback;它必須能讀取 `reboot-auto-recovery-slo-exporter.sh` 產出的 runtime artifact。
|
||||
|
||||
Reference in New Issue
Block a user