fix(infra): use python urllib for container healthcheck (ET-015) #30

Merged
admin merged 6 commits from feature/ET-015-healthcheck-enduro-trails-app- into main 2026-06-05 18:40:31 +03:00
Owner

Summary

Заменяем healthcheck сервиса app в docker-compose.yml с curl -f (отсутствует в базовом python:3.12-slim, из-за чего контейнер enduro-trails-app-1 ≥31 час висит в unhealthy при работающем приложении) на python one-liner через stdlib urllib.request — без правок Dockerfile и src/api/main.py, без ребилда образа (достаточно docker compose up -d app).

  • docker-compose.yml (services.app.healthcheck.test):
    test:
      - "CMD"
      - "python"
      - "-c"
      - "import urllib.request,sys; sys.exit(0 if urllib.request.urlopen('http://localhost:5556/api/health', timeout=3).status == 200 else 1)"
    interval: 30s
    timeout: 5s
    retries: 3
    start_period: 20s
    
    Параметры interval=30s / timeout=5s / retries=3 сохранены; внутренний timeout=3 строго меньше внешнего timeout: 5s (AC-07); добавлен start_period: 20s.
  • Dockerfile / src/api/main.py: не меняются (BRD §6, AC-04, AC-08).
  • CHANGELOG.md: запись в [Unreleased] / Fixed.
  • ADR-020: docs/work-items/ET-015/06-adr/ADR-020-healthcheck-via-python-urllib.md (создан архитектором ранее).

Покрытие тестами

  • tests/static/test_healthcheck_compose.py — 10 тестов (ST-01..ST-07 + защита от регресса по target URL, start_period, baseline interval/retries).
  • tests/unit/test_healthcheck_oneliner.py — 6 тестов (UT-01..UT-03), исполняют ровно ту же one-liner-команду из compose через subprocess против локального мок-HTTPServer (200 / 301 / 404 / 500 / 503) и неиспользуемого порта. URL подменяется через _retarget, чтобы тестировать живой код, а не его копию.

Все 16 тестов проходят локально.

Test plan

  • CI: make test — все static + unit тесты ET-015 зелёные.
  • CI: make lint — ruff проходит.
  • Локально или в integration-CI: IT-01 — после docker compose up -d app контейнер становится healthy за ≤ 120s (FailingStreak == 0).
  • Локально/CI: IT-03 — при остановке uvicorn контейнер становится unhealthy за ≤ 120s.
  • Локально/CI: IT-04 — переход на новый healthcheck без ребилда образа.
  • mva154 после make deploy-test: E2E-01 — docker inspect enduro-trails-app-1 --format '{{.State.Health.Status}}'healthy (streak 0) трижды (0, +5 мин, +10 мин).
  • mva154: E2E-02 — curl https://openclaw.mva154.duckdns.org/enduro/api/health → HTTP 200, time_total < 1s.

Refs: ET-015

Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com

## Summary Заменяем healthcheck сервиса `app` в `docker-compose.yml` с `curl -f` (отсутствует в базовом `python:3.12-slim`, из-за чего контейнер `enduro-trails-app-1` ≥31 час висит в `unhealthy` при работающем приложении) на python one-liner через stdlib `urllib.request` — без правок `Dockerfile` и `src/api/main.py`, без ребилда образа (достаточно `docker compose up -d app`). - **docker-compose.yml** (`services.app.healthcheck.test`): ```yaml test: - "CMD" - "python" - "-c" - "import urllib.request,sys; sys.exit(0 if urllib.request.urlopen('http://localhost:5556/api/health', timeout=3).status == 200 else 1)" interval: 30s timeout: 5s retries: 3 start_period: 20s ``` Параметры `interval=30s` / `timeout=5s` / `retries=3` сохранены; внутренний `timeout=3` строго меньше внешнего `timeout: 5s` (AC-07); добавлен `start_period: 20s`. - **Dockerfile / src/api/main.py**: не меняются (BRD §6, AC-04, AC-08). - **CHANGELOG.md**: запись в `[Unreleased] / Fixed`. - **ADR-020**: `docs/work-items/ET-015/06-adr/ADR-020-healthcheck-via-python-urllib.md` (создан архитектором ранее). ## Покрытие тестами - `tests/static/test_healthcheck_compose.py` — 10 тестов (ST-01..ST-07 + защита от регресса по target URL, `start_period`, baseline `interval/retries`). - `tests/unit/test_healthcheck_oneliner.py` — 6 тестов (UT-01..UT-03), исполняют **ровно ту же** one-liner-команду из compose через `subprocess` против локального мок-HTTPServer (200 / 301 / 404 / 500 / 503) и неиспользуемого порта. URL подменяется через `_retarget`, чтобы тестировать живой код, а не его копию. Все 16 тестов проходят локально. ## Test plan - [ ] CI: `make test` — все static + unit тесты ET-015 зелёные. - [ ] CI: `make lint` — ruff проходит. - [ ] Локально или в integration-CI: IT-01 — после `docker compose up -d app` контейнер становится `healthy` за ≤ 120s (`FailingStreak == 0`). - [ ] Локально/CI: IT-03 — при остановке uvicorn контейнер становится `unhealthy` за ≤ 120s. - [ ] Локально/CI: IT-04 — переход на новый healthcheck **без ребилда образа**. - [ ] mva154 после `make deploy-test`: E2E-01 — `docker inspect enduro-trails-app-1 --format '{{.State.Health.Status}}'` → `healthy` (streak 0) трижды (0, +5 мин, +10 мин). - [ ] mva154: E2E-02 — `curl https://openclaw.mva154.duckdns.org/enduro/api/health` → HTTP 200, `time_total < 1s`. Refs: ET-015 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
admin added 4 commits 2026-06-05 18:33:13 +03:00
docs: init ET-015 business request
All checks were successful
CI / lint (push) Successful in 5s
CI / test (push) Successful in 9s
CI / build (push) Successful in 3s
41dfc4e150
analyst(ET): auto-commit from analyst run_id=101
All checks were successful
CI / lint (push) Successful in 4s
CI / test (push) Successful in 9s
CI / build (push) Successful in 3s
c2cf8280ca
architect(ET): auto-commit from architect run_id=102
All checks were successful
CI / lint (push) Successful in 4s
CI / test (push) Successful in 10s
CI / build (push) Successful in 3s
4f80c250cf
fix(infra): use python urllib for container healthcheck (ET-015)
All checks were successful
CI / lint (push) Successful in 4s
CI / test (push) Successful in 12s
CI / build (push) Successful in 2s
CI / lint (pull_request) Successful in 4s
CI / test (pull_request) Successful in 12s
CI / build (pull_request) Successful in 2s
543099b740
Базовый образ `python:3.12-slim` не содержит `curl`, поэтому текущий
healthcheck `["CMD", "curl", "-f", ...]` всегда падает (`exec: "curl":
executable file not found`), и контейнер `enduro-trails-app-1` висит
в статусе `unhealthy` (≥31 час, FailingStreak 3762 при RestartCount 0),
несмотря на то что приложение исправно отвечает HTTP 200 на /api/health.

Заменяем healthcheck на python one-liner через stdlib `urllib.request`
(ADR-020). Изменения:

  • docker-compose.yml, сервис app:
      test: ["CMD", "python", "-c",
             "import urllib.request,sys; sys.exit(0 if
              urllib.request.urlopen(...timeout=3).status == 200 else 1)"]
      + start_period: 20s
    interval/timeout/retries сохранены (30s / 5s / 3).
    Внутренний urlopen(timeout=3) строго меньше внешнего healthcheck
    timeout=5s (AC-07).

  • Dockerfile НЕ меняется (никаких apt-get install curl/wget — BRD §6,
    AC-04). Деплой без ребилда: `docker compose up -d app` достаточно.

  • src/api/main.py НЕ меняется. Контракт /api/health сохранён (AC-08).

Покрытие:
  - tests/static/test_healthcheck_compose.py — 10 тестов (ST-01..ST-07
    + защита от регресса по target URL / start_period / baseline params).
  - tests/unit/test_healthcheck_oneliner.py — 6 тестов (UT-01..UT-03),
    исполняют ровно ту же one-liner-команду через subprocess против
    локального мок-HTTPServer (200/301/404/500/503) и неиспользуемого
    порта. URL подменяется через `_retarget`, чтобы тестировать живой
    код из compose, а не его копию.

ADR: docs/work-items/ET-015/06-adr/ADR-020-healthcheck-via-python-urllib.md
CHANGELOG: запись в [Unreleased] / Fixed.

Refs: ET-015

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
admin added 1 commit 2026-06-05 18:37:05 +03:00
reviewer(ET): auto-commit from reviewer run_id=104
All checks were successful
CI / lint (push) Successful in 4s
CI / lint (pull_request) Successful in 4s
CI / test (push) Successful in 12s
CI / build (push) Successful in 3s
CI / test (pull_request) Successful in 12s
CI / build (pull_request) Successful in 2s
d501bcbbc4
admin added 1 commit 2026-06-05 18:39:55 +03:00
tester(ET): auto-commit from tester run_id=105
All checks were successful
CI / lint (push) Successful in 4s
CI / lint (pull_request) Successful in 5s
CI / test (push) Successful in 13s
CI / build (push) Successful in 2s
CI / test (pull_request) Successful in 12s
CI / build (pull_request) Successful in 1s
c05a834c26
admin merged commit e8a833572b into main 2026-06-05 18:40:31 +03:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: admin/enduro-trails#30