fix(staging): host-side ssh execution + env classification for staging-runner (ORCH-123)
The ORCH-115 deterministic staging-runner ran `docker exec` FROM INSIDE the prod `orchestrator` container, which ships only `openssh-client git curl` — no `docker` CLI (Dockerfile:11). `Popen(["docker", ...])` hit FileNotFoundError -> a PERMANENT environment defect that was mis-routed as a code-fail rollback `deploy-staging -> development` (burning developer-retries). Incident ORCH-116: every self-hosting task reaching deploy-staging was doomed to a false rollback. Fix (adr-0049, additive, flag-gated, never-raise, self-hosting scope; the gate / artifact contract / STAGE_TRANSITIONS / DB schema are byte-for-byte unchanged): - D1: build_staging_command() wraps the SAME `docker exec ... staging_check.py ... --mode stub` in `ssh <user@host> '<...>'` so it runs HOST-SIDE over the existing trusted ssh channel (mirror self_deploy / image_freshness). New flag staging_runner_exec_host_side (default True). No docker CLI/SDK added to the image, docker.sock not used in-container (D2 security). - D3: three-way classify_staging_outcome (suite-ran / permanent-env / transient-infra), disambiguating the exit=1 collision by scanning stderr. - D4: invariant "infra != code-fail" — permanent-env / exhausted transient-infra end in an infra-HOLD (no rollback, no developer-retry), NOT a false FAILED rollback (supersedes ORCH-115 D5). A really-executed failing suite still rolls back (anti-over-tolerance). R-2 verified: a held deploy-staging task is not rolled back by the reconciler. - D5: prod-like preflight() of the host-side channel at startup (main.lifespan, best-effort, never blocks). - D8: snapshot adds permanent_env / exec_host_side / preflight. Docs (golden source, same PR): INFRA.md execution-boundary section, architecture/README.md, CLAUDE.md, CHANGELOG.md, .env.example. Tests: tests/test_orch123_staging_runner_exec.py (TC-01 mandatory regression red->green; TC-02..TC-14 + R-2). ORCH-115 anti-drift green (3 tests updated for the D1/D4/D8 supersession). Full suite: 2131 passed. Refs: ORCH-123 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -244,10 +244,39 @@ watchdog'а: **watchdog сигналит, pruner убирает**.
|
||||
- **Свежесть staging-образа (ORCH-058):** на ребре `deploy-staging → deploy` (ПОСЛЕ merge-gate, ДО Phase A) QG-под-чек `check_staging_image_fresh` пересобирает staging-образ из валидированного коммита и пересоздаёт 8501 (Strategy A), а хук перед build-once retag fail-closed сверяет OCI-лейбл `revision` с `EXPECTED_REVISION` (Strategy B). Гарантирует: в прод промоутится РОВНО провалидированный артефакт (инцидент LESSONS_ORCH-036 п.4 — тихий промоут устаревшего образа). Сборки/recreate — ТОЛЬКО staging (8501); FAIL → откат на `development`. Условный: реален только для self-hosting.
|
||||
- **Гигиена shared deploy-базы (ORCH-112):** self-deploy `git pull origin main` устойчив к грязному рабочему дереву deploy-базы (модифицированные tracked + untracked-остатки failed/cancelled/брошенных задач). Хук `--deploy` перед pull приводит базу к чистому `origin/main` (resilient-pull: `git fetch` + `git reset --hard origin/main` + `git clean -fd`), **строго сохраняя** rollback-снимки `.deploy-prev-image-*`, `deploy-hook.log`, gitignored `.env`/`data/`/`*.db` (НИКОГДА `-x`!), sibling `.deploy-state-*`/`.merge-lease-*.json`, `.git/worktrees/*`. Гейт — kill-switch `ORCH_CHECKOUT_HYGIENE_ENABLED` (дефолт `True`; off → голый pull 1:1); скоуп `ORCH_CHECKOUT_HYGIENE_REPOS` (пусто → self-hosting only). Грязь базы детектируется → лог + Telegram-алерт (Phase-C finalizer). Решает инцидент ORCH-111 (грязь ORCH-104 заблокировала `git pull`). Детально — `docs/work-items/ORCH-112/06-adr/ADR-001`, сквозной adr-0044.
|
||||
|
||||
### Граница исполнения docker-операций — host-side, НЕ изнутри прод-контейнера (ORCH-123)
|
||||
|
||||
**Инвариант (нормативно, adr-0049):** прод-контейнер `orchestrator` несёт **только**
|
||||
`openssh-client git curl` (+ pinned gitleaks) — **бинаря `docker` в образе НЕТ** (`Dockerfile:11`,
|
||||
`python:3.12-slim` его не несёт). `/var/run/docker.sock` **смонтирован** (`docker-compose.yml`,
|
||||
rw + `group_add 999`, «МИНА 1» ORCH-040), но **сознательно НЕ используется изнутри** контейнера: нет
|
||||
docker-клиента, и добавлять его (CLI/SDK) **нельзя** — это активировало бы дремлющий
|
||||
root-эквивалентный путь для всего, что бежит в контейнере, включая LLM-агентов (security-разбор —
|
||||
ADR-001 D2 / adr-0049 / R-1).
|
||||
|
||||
Поэтому **все** docker-операции self-hosting исполняются **host-side** через **ssh на `127.0.0.1`**
|
||||
(`ORCH_DEPLOY_SSH_HOST`/`ORCH_DEPLOY_SSH_USER`, ssh-ключ смонтирован `:ro`), где docker CLI есть:
|
||||
- **прод-деплой** (ORCH-036, `self_deploy.build_deploy_command`) — `ssh … setsid bash hook --deploy`;
|
||||
- **image-freshness** (ORCH-058, `image_freshness.image_revision`/`rebuild_staging_image`) — `ssh … docker …`;
|
||||
- **staging-runner** (ORCH-123, `staging_runner.build_staging_command`) — `ssh … docker exec orchestrator-staging python3 … staging_check.py … --mode stub`.
|
||||
|
||||
Сама staging-сюита (`scripts/staging_check.py`) **по-прежнему** исполняется **внутри** контейнера
|
||||
`orchestrator-staging` (8501) — меняется лишь **кто инициирует** `docker exec` (хост через ssh, а не
|
||||
прод-контейнер). До ORCH-123 staging-runner (ORCH-115) вызывал `docker exec` **изнутри**
|
||||
прод-контейнера → `FileNotFoundError` (нет `docker`) → постоянный environment-дефект ложно
|
||||
маршрутизировался как код-фейл-откат `deploy-staging → development` (инцидент ORCH-116). Фикс:
|
||||
host-side ssh-обёртка (флаг `ORCH_STAGING_RUNNER_EXEC_HOST_SIDE=true`, дефолт) + трёхсторонняя
|
||||
классификация (suite-ran / permanent-env / transient-infra), где environment/инфра **никогда** не
|
||||
оканчивается код-фейл-откатом (infra-HOLD + алерт «инфра, НЕ дефект кода»), и prod-like preflight
|
||||
канала на старте сервиса. Откат — `ORCH_STAGING_RUNNER_EXEC_HOST_SIDE=false` (прежний in-container
|
||||
вызов — валиден лишь там, где docker CLI запечён в образ) или `ORCH_STAGING_RUNNER_ENABLED=false`
|
||||
(LLM-deployer 1:1). Детали — `docs/work-items/ORCH-123/06-adr/ADR-001`, сквозной adr-0049.
|
||||
|
||||
**Правила для агентов при задачах ORCH:**
|
||||
1. НЕ перезапускать / не ронять прод-контейнер `orchestrator` в рамках задачи.
|
||||
2. Все проверки деплоя — на staging (8501), боевой 8500 не трогать.
|
||||
3. Деплой self — только через хук с health-check + авто-rollback (`DEPLOY_HOOK.md`).
|
||||
4. docker-операции исполняются **host-side через ssh** — в контейнере `docker` CLI нет (ORCH-123).
|
||||
|
||||
## Эксплуатация (быстрые команды)
|
||||
```bash
|
||||
|
||||
Reference in New Issue
Block a user