Files
awg-dash/deploy/VERIFICATION-21.md
T

16 KiB
Raw Blame History

Проверка dev/staging стенда (#21) — фактические прогоны на Docker-хосте

Дата: 2026-10-22 · Хост: 192.168.1.147 (Debian 13, Docker 29.4.1, compose v5.1.3, kernel 6.17) Ветка: feat/21-dev-stand, HEAD d63b9cea · Ref: #21, #15, #17, #18, #31, #56

0. Стенд запускает именно то, что в ветке

Каталог стенда (/opt/awg-dash-dev/repo) не является git-чекаутом (git clone по HTTP обрывается на этом Gitea — fetch-pack: invalid index-pack output), поэтому файлы сверены побайтово через git-blob-SHA с деревом HEAD:

файл git blob (HEAD == хост)
Makefile 638ec381f3d1e57a8ab84f194e40ff6a0c51d8cf
deploy/docker-compose.dev.yml 6bed89a5ea2a197aa9f6fd6bcda6d8131303114d
deploy/docker-compose.stub.yml 936efb38dfff4ba8b942c9757acd4ba9c39c4064
deploy/docker-compose.test.yml b628c129ae4de9b33875503b2c8273675b235a84
deploy/verify-dev-stand.sh c4e13f11f9023700be531ebb312ad33d7beea4d8
deploy/api_stub.py 93663113f118676a7e896e0e9d32bd20a21f6a0e
tests/deploy/validate_compose.py 0fa8b3c02675a364f4f5b447220680a7818faa5a
tests/deploy/pytest.ini 7299b6b45487dd4f214f0444e2f0f5c237eb2fac
tests/deploy/.coveragerc 9b41183e29a155b5498c3722cf4639e9c69268c6
tests/deploy/test_api_stub.py 1d5cec3a72b02da6cb48a6ca433e7699b768f631
tests/deploy/test_validate_compose.py fb045949848cb70a1880614201930c2d4ecd9d0a
tests/netns/lib.sh 806375299b7c3932d20b275d98b72f65e48940ec
tests/netns/run.sh c6797bf9c42abe7c4f72cf5402af82e2192ee867
tests/netns/topology.sh 4892f6b9f9b92c38ae058df00e7c79a692537eaa
tests/netns/netns_common.py 9d3bfd94cd8e8b321af92274835b3f0ebc48c842
tests/netns/README.md 845863188ffb26c1aae7e475addc0c511e0d8f1f
остальные файлы tests/netns/ (5) без изменений с раунда 1

1. Предварительные пакеты на стенд-хосте

apt-get install -y make python3-yaml python3-pytest python3-pytest-cov shellcheck

(make на хосте отсутствовал — без него гейты make * невоспроизводимы; пакеты добавлены сверх существующего набора, ничего не удалялось и не переконфигурировалось.)

2. Гейты (реальные прогоны)

Лог целиком: /opt/awg-dash-dev/verify-final-1792617226.log (на хосте).

гейт команда результат
манифесты make test-manifests exit 0, validate-compose: ok (1 предупреждений)¹
статический анализ шелла make lint-shell exit 0
unit-тесты манифестов python3 -m pytest tests/deploy -q --no-cov exit 0
netns-харнесс (AC-3) make test-netns exit 0, 8 ok / 0 fail / 0 err, отчёт /tmp/awg-netns/report.json
AC-1 (health 200) make dev-up + curl :8180/api/v1/health HTTP 200, контейнер healthy
AC-2 (состояние переживает рестарт) make dev-verify [ ok ] AC-1, [ ok ] AC-2 ×2, exit 0
логи make dev-logs стрим логов, есть [api-stub] ... GET /api/v1/health 200

¹ предупреждение: build.dockerfile deploy/Dockerfile отсутствует в дереве (ожидаемо до мержа #18). Образ awg-dash:dev для стенда собран из Dockerfile PR #49 (ветка feat/18-docker-runtime), в дерево этой ветки он не входит: make build вернёт внятную ошибку до мержа #18. Валидатор переключается в режим ошибки флагом make test-manifests MANIFEST_ARGS=--require-dockerfile — этот режим нужно включить в CI гейте после мержа #18.

AC-1 — детали

$ make dev-up
# (авто-режим: backend/awg_api/app.py отсутствует (#31) → + deploy/docker-compose.stub.yml)
$ curl -s -w '\nHTTP=%{http_code}\n' http://127.0.0.1:8180/api/v1/health
{"issue": "#31", "note": "заглушка dev-стенда: реальный API появится в #31", "status": "ok",
 "stub": true, "stub_env": "1", "uptime_s": 14.909}
HTTP=200
$ docker ps --filter name=awg-dash-dev
awg-dash-dev  Up 15 seconds (healthy)  0.0.0.0:8180->8080/tcp, [::]:8180->8080/tcp

Почему с заглушкой: FastAPI-каркас (#31) ещё не реализован, сам образ HTTP не поднимает (entrypoint пишет awg_api/app.py ещё не реализован и ждёт). Чтобы AC-1 #21 проверялся на живом compose-стенде уже в Sprint 0, make dev-up добавляет оверрайд docker-compose.stub.yml только пока backend/awg_api/app.py отсутствует; ответ явно помечен "stub": true. После мержа #31 оверрайд не добавляется, make dev-up поднимает реальный API без правок Makefile.

AC-2 — детали

$ make dev-verify
[verify][ ok ] AC-1: http://127.0.0.1:8180/api/v1/health -> 200 (api=stub)
[verify] AC-2: пишу маркеры в /config и /data и перезапускаю контейнер
[verify][ ok ] AC-2: /config и /data пережили restart и пересоздание контейнера
[verify][ ok ] AC-2: маркеры видны на хосте (/opt/awg-dash-dev/{config,data}) — это bind mount, а не слой контейнера

$ ls /opt/awg-dash-dev/config /opt/awg-dash-dev/data
/opt/awg-dash-dev/config/.ac2-marker   (24 байта)
/opt/awg-dash-dev/data/.ac2-marker     (24 байта)

Проверяется два сценария: docker compose restart (как в AC) и up -d --force-recreate (сильнее).

AC-3 (netns) — детали

[netns] создаю netns: client=awgt21-client gw=awgt21-gw uplink=awgt21-uplink
[ ok ] стенд поднят: awgt21-client(10.213.0.2) -> awgt21-gw(10.213.0.1/10.214.0.1) -> awgt21-uplink(10.214.0.2)
[ ok ] policy routing: fwmark 51820 -> table 51820 (default via 10.214.0.2)
[ ok ] mock endpoint: udp/51820 в awgt21-uplink; детектор утечки: 198.51.100.9:9999/udp
Ran 8 tests in 5.447s — OK
test_01_namespaces_and_links ... test_08_killswitch_blocks_direct_egress — все ok
[ ok ] топология снесена, namespace'ы удалены   (ip netns list пусто)

report.json: {"suite": "tests/netns", "tests": 8, "passed": 8, "failures": 0, "errors": 0, "skipped": 0, "returncode": 0, "duration_s": 5.447}

3. Что чинилось в этой итерации

проблема причина правка
make dev-up давал unhealthy и AC-1 не выполнялся API (#31) не реализован Makefile: авто-подключение stub-оверрайда, пока нет backend/awg_api/app.py; dev-up-app / dev-up-stub для явных режимов
make dev-verify печатал [verify][fail] нет монтирования /config при реально сохранённых томах и падал verify-dev-stand.sh кэшировал id контейнера и делал docker inspect по нему после --force-recreate → no such object перезапрос id (cid()) после пересоздания и явный fatal_env вместо ложного fail
dev-up падал без deploy/Dockerfile (--build) Dockerfile живёт в #18, ветка не смёржена --build форсируется только при наличии deploy/Dockerfile

4. Ограничения и что делать дальше

  1. CI-гейты (DoD) не автоматизированы: Gitea Actions runner для embeddevio/awg-dash не зарегистрирован (см. отчёт в #17, run #1 остаётся queued). Все гейты в таблице §2 прогнаны вручную на стенд-хосте; после регистрации runner'а в #17 они переносятся в workflow без изменений команд.
  2. Порт: на 192.168.1.147 хост-порт 8080 занят (drawio), поэтому стенд публикуется как 8180:8080 (контейнерный порт приложения — 8080, AWG_DASH_PORT переопределяет хост-порт).
  3. Сборка образа — только после мержа #18; до этого образ awg-dash:dev собирается из ветки #18 вручную.
  4. Prod/staging: stub-оверрайд и deploy/api_stub.py — DEV-ONLY. В staging/prod поднимается только docker-compose.dev.yml (или прод-манифест) без stub-файла.

5. Откат / очистка

make dev-down          # остановить стенд, каталоги /opt/awg-dash-dev/{config,data} сохраняются
make dev-reset         # + удалить осиротевшие контейнеры/сети проекта
make test-netns        # netns-харнесс сносит топологию сам; аварийно: bash tests/netns/topology.sh down

6. Раунд 2 (issue #56): netns в контейнере и покрытие тестов

Дата: 2026-10-28 · Хост: 192.168.1.147 · Ветка: feat/21-dev-stand · Ref: #21, #15, #17, #56

Ревью qa-engineer (раунд 1) нашло два блокера: make test-netns-ci падал в контейнере (mount of /sys failed: Operation not permitted, exit 255 вместо 0/1/3/4) и гейт покрытия pytest tests/deploy обходился флагом --no-cov (0.00 %, deploy/api_stub.py 0 %).

6.1 Причина падения netns в контейнере

Воспроизведено на образе awg-dash:dev (iproute2 6.11.0):

команда в контейнере результат
ip netns add <ns> rc=0
ip netns exec <ns> true mount of /sys failed: Operation not permitted, rc=255
ip -n <ns> link set lo up mount of /sys failed: Operation not permitted, rc=255
nsenter --net=/var/run/netns/<ns> true rc=0

ip netns exec и ip -n перемонтируют /sys внутри namespace; в контейнере это падает. Поэтому харнесс больше не использует эти формы: вход в namespace идёт через ns_exec() / ns_prefix() (tests/netns/lib.sh) и netns_common.ns_sh(), которые один раз пробуют ip netns exec, а при отказе переключаются на nsenter --net=... (AWG_NS_EXEC_MODE=auto|ip|nsenter). Все ip -n "$NS" заменены на ns_exec "$NS" ip ....

Коды возврата приведены к контракту 0/1/3/4 (run.sh, topology.sh): неожиданный код утилиты (255) нормализуется в 4 («ошибка стенда»), нет прав — 3, упавшие тесты — 1.

6.2 Реальные прогоны

прогон команда результат
стенд на хосте make test-netns exit 0, 8 ok / 0 fail, 5.46 s, ip netns list пуст, мок и sink не остались в процессах
стенд в контейнере (путь CI) make test-netns-ci exit 0, [netns] вход в netns: nsenter, backend iptables, 8 ok / 0 fail, 5.50 s, [ ok ] топология снесена, отчёт /artifacts/netns-report.json
нет прав docker run без --privileged exit 3, [blocked] не удалось создать netns — нет CAP_NET_ADMIN/SYS_ADMIN
форс нерабочего режима AWG_NS_EXEC_MODE=ip в контейнере exit 4, mount of /sys failed → [warn] run.sh: непредвиденный код 255 — трактую как ошибку стенда (4)
упавшие тесты синтетический проваленный тест в контейнере exit 1, [warn] netns-стенд: тесты/очистка упали (код 1)
гейт покрытия make test-deploy exit 0, 51 passed, coverage 98.86 % (deploy/api_stub.py 98 %, tests/deploy/validate_compose.py 99 %)
манифесты / shellcheck make test-manifests, make lint-shell exit 0 / exit 0
стабильность в контейнере 5 прогонов make test-netns-ci подряд 5/5 exit 0, 8 ok каждый, ни одного «остался netns»
AC-1 после правок curl :8180/api/v1/health HTTP 200, "stub": true, контейнер healthy
AC-2 после правок make dev-verify VERIFY_ARGS="--no-up --skip-build" exit 0: маркеры /config и /data пережили restart и пересоздание, видны на хосте (bind mount)

6.3 Что изменено для гейта покрытия

  • tests/deploy/pytest.ini — отдельный профиль: --cov=deploy --cov=tests/deploy --cov-config=tests/deploy/.coveragerc --cov-fail-under=70. Раньше pytest tests/deploy наследовал addopts корневого pyproject.toml (--cov=awg_agent --cov=awg_api), поэтому покрытие всегда было 0.00 % и RC=1 — гейт гоняли с --no-cov в обход порога.
  • tests/deploy/.coveragerc — из покрытия исключены сами тестовые модули (tests/deploy/test_*.py), иначе «покрытие» измеряет тестовый код.
  • tests/deploy/test_api_stub.py — 9 тестов на живой HTTP-сервер заглушки: 200 и "stub": true на /api/v1/health, алиас /healthz, игнор query-string, 404 not_implemented на прочие пути, HEAD, отказ на POST, разбор аргументов main().
  • tests/deploy/test_validate_compose.py — 39 негативных/граничных тестов валидатора: каждая инварианта AC #21 ломается по отдельности (порт, тома, healthcheck, NET_ADMIN, /dev/net/tun, ip_forward, privileged netns-сервис, DEV-ONLY-метка, --require-dockerfile).
  • deploy/api_stub.py — make_server() выделен из main() (тесты поднимают сервер на порту 0).
  • make test-deploy — гейт покрытия; make test-netns-ci прокидывает AWG_NS_EXEC_MODE.
  • topology.sh: снос топологии удаляет namespace'ы безусловно и до 5 раз проверяет результат — в контейнере ip netns delete иногда «терялся» и run.sh отдавал код 4 на зелёных тестах (флак 1 из 5 прогонов, после правки — 5/5 зелёных).

6.4 Что закрыто и что осталось

Закрыты оба блокера раунда 1 (make test-netns-ci работает в контейнере, покрытие измеряется и ≥70 %). CI green по-прежнему недостижим: Gitea Actions runner не зарегистрирован (#17), все прогоны в таблице 6.2 — ручные на 192.168.1.147; команды make test-netns-ci и make test-deploy — ровно те, что должны попасть в workflow #17.

Порт и stub-оверрайд (см. §4) без изменений: privileged: true и cap_add SYS_MODULE из deploy/docker-compose.test.yml — только DEV/TEST, для staging/prod непригодны.