diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 03a76d08..c915a65f 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -778,6 +778,9 @@ services: # роняя ВСЕ сайты хоста, а не только metrics.gendsgn.ru. - ./caddy/metrics-ingest.caddy.snippet:/etc/caddy/caddy/metrics-ingest.caddy.snippet:ro - ./caddy/metrics-ui.caddy.snippet:/etc/caddy/caddy/metrics-ui.caddy.snippet:ro + # То же самое для страницы окна деплоя (#3274): caddy/sites/apps.caddy + # импортирует её как `import ../deploy-window.caddy.snippet`. + - ./caddy/deploy-window.caddy.snippet:/etc/caddy/caddy/deploy-window.caddy.snippet:ro # Untracked локальные site-блоки (см. import в конце Caddyfile). Каталог # держится в git через caddy/local/.gitignore — иначе docker создал бы # отсутствующий bind-source сам, root-owned пустышкой. diff --git a/scripts/smoke-mera-perimeter.sh b/scripts/smoke-mera-perimeter.sh index a0906279..84629c9c 100644 --- a/scripts/smoke-mera-perimeter.sh +++ b/scripts/smoke-mera-perimeter.sh @@ -52,15 +52,22 @@ check() { } check_post() { - local desc="$1" url="$2" body="$3" expected="$4" - local code - code=$(curl -s -o /dev/null -w '%{http_code}' --max-time 15 \ + local desc="$1" url="$2" body="$3" expected="$4" reject="${5:-}" + local out code head_and_body + # -i: заголовки попадают в вывод вместе с телом — по ним отличаем ответ + # приложения от заглушки Caddy (см. $reject у вызова payments/notify). + out=$(curl -s -i -w '\n%{http_code}' --max-time 15 \ -X POST -H 'Content-Type: application/json' -d "$body" "$url" 2>/dev/null) - if [ "$code" = "$expected" ]; then - echo "PASS: $desc ($url -> $code)" - else + code=${out##*$'\n'} + head_and_body=${out%$'\n'*} + if [ "$code" != "$expected" ]; then echo "FAIL: $desc ($url -> got '${code:-}', expected $expected)" fail=1 + elif [ -n "$reject" ] && printf '%s' "$head_and_body" | grep -qEi "$reject"; then + echo "FAIL: $desc ($url -> $code, но ответ от заглушки окна деплоя, а не от приложения)" + fail=1 + else + echo "PASS: $desc ($url -> $code)" fi } @@ -251,8 +258,16 @@ check "meraocenka.ru payments/checkout — must 404 (Caddy не проксиру # маршрут существует (не 404 — старый образ) И что приём платежей выключен # (`payments_enabled=False`). 200 здесь означал бы, что флаг включили, не тронув # этот смоук. GET → 405 закрепляет, что путь принимает только POST. +# +# С #3274 голый код 503 больше не доказывает ничего: Caddy сам отдаёт 503 на +# окне деплоя (`handle_errors` -> caddy/deploy-window.caddy.snippet), и тогда +# запрос до приложения не дошёл вовсе. Отличаем по признакам заглушки — +# заголовок `Retry-After: 30` и `"error":"service_unavailable"` в теле; у +# приложения тело `{"detail":"payments are disabled"}` и никакого Retry-After. +# Совпал код, но пришла заглушка → FAIL, а не молчаливый PASS. check_post "trade-in payments/notify — 503 anonymous (маршрут есть, приём выключен)" \ - "$BASE_MAIN/trade-in/api/v1/trade-in/payments/notify" '{}' 503 + "$BASE_MAIN/trade-in/api/v1/trade-in/payments/notify" '{}' 503 \ + 'service_unavailable|^retry-after: *30' check "trade-in payments/notify — 405 на GET (только POST)" \ "$BASE_MAIN/trade-in/api/v1/trade-in/payments/notify" 405 check "trade-in payments/checkout — 401 anonymous (не публичный по построению)" \