Гейт #3467 больше не слеп к || true и к маунту без :ro (ревью PR #3475)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 14s
CI / changes (pull_request) Successful in 15s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Successful in 2m51s
CI / backend-tests (pull_request) Successful in 18m17s
All checks were successful
CI Trade-In / changes (pull_request) Successful in 14s
CI / changes (pull_request) Successful in 15s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Successful in 2m51s
CI / backend-tests (pull_request) Successful in 18m17s
Мутации ревьюера проходили гейт насквозь: - к шагу `POST /-/reload` дописано `|| true` — все 7 тестов зелёные. Regex заканчивался на URL и хвост строки не смотрел; сегодня спасает `set -e`, но именно так («зелёная сводка ≠ зелёный выход») в этом репозитории уже ломали гейты. Добавлен отрицательный lookahead на `||` в той же строке; - новый одиночный файловый маунт БЕЗ суффикса `:ro` — тест зелёный, потому что распознавание маунта требовало `:ro`. Права к типу маунта отношения не имеют: инод он держит одинаково. Требование снято. Проверка promtool переехала ВЫШЕ `docker compose up -d`: прежнее место (между подъёмом и перезагрузкой) не спасало от битого конфига, потому что `up -d` пересоздаёт контейнер при смене образа. Новый тест сторожит и это. Якоря границ серверной джобы и блока триггеров читаются через find с внятным assert: при переименовании шага гейт скажет, что сломалось, а не даст ValueError. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
c31aff68a9
commit
3aef5c3631
2 changed files with 73 additions and 43 deletions
|
|
@ -287,6 +287,43 @@ jobs:
|
|||
# обязан быть безопасен даже при полном доступе к дашбордам.
|
||||
bash scripts/setup-metrics-grafana-role.sh
|
||||
|
||||
# ── Prometheus: проверить конфигурацию ДО ЛЮБОГО применения ─────
|
||||
# Тот же порядок, что у Caddy ниже: проверка — здесь, применение —
|
||||
# только после неё. Стоит ВЫШЕ `up -d` намеренно: `up -d`
|
||||
# пересоздаёт контейнер при смене образа, и тогда битый конфиг
|
||||
# положил бы Prometheus раньше, чем деплой успел бы покраснеть.
|
||||
# Сама перезагрузка — в конце, после приёмки: дёргать ручку
|
||||
# раньше, чем процесс отвечает, нечем.
|
||||
#
|
||||
# ЗАЧЕМ ЭТО ВООБЩЕ. Правила подключены КАТАЛОГОМ, поэтому новый
|
||||
# текст контейнер видит сразу — но Prometheus читает правила только
|
||||
# при старте либо по явной перезагрузке, а деплой не делал ни того,
|
||||
# ни другого. 12.09: контейнер работал с 27.08, на диске (и внутри
|
||||
# контейнера) лежали правила из #3464, а /api/v1/rules отдавал
|
||||
# прежние. Зелёный деплой, новый файл на диске, старое поведение —
|
||||
# отказ без единого отрицательного признака. Значит и все прежние
|
||||
# правки ops/metrics/prometheus/** вступали в силу лишь тогда, когда
|
||||
# контейнер пересоздавался по какой-то посторонней причине.
|
||||
#
|
||||
# Проверяем ОДНОРАЗОВЫМ контейнером того же образа по файлам С
|
||||
# ДИСКА, а не `docker exec` в работающий: prometheus.yml подключён
|
||||
# бинд-маунтом ОДНОГО файла, и работающий контейнер после `git
|
||||
# reset` читает СТАРЫЙ инод — exec проверял бы не тот текст,
|
||||
# который применится.
|
||||
#
|
||||
# Нужны ОБЕ команды: при пустом (или не смонтированном) каталоге
|
||||
# правил `check config` печатает «0 rule files found» и проходит, а
|
||||
# `check rules` по явному шаблону в этом случае падает — проверено
|
||||
# на стенде prom/prometheus:v3.1.0.
|
||||
PROM_IMAGE=$(grep -oE 'prom/prometheus:[^ ]+' docker-compose.metrics.yml | head -1)
|
||||
if ! docker run --rm -v "$PWD/ops/metrics/prometheus:/etc/prometheus:ro" \
|
||||
--entrypoint sh "$PROM_IMAGE" -c \
|
||||
'promtool check config /etc/prometheus/prometheus.yml && promtool check rules /etc/prometheus/rules/*.yml'; then
|
||||
echo "ОШИБКА: конфиг или правила Prometheus не проходят проверку — ничего не применяем."
|
||||
echo "Работающий Prometheus не тронут и остаётся на прежней конфигурации. Чинить и повторять."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
COMPOSE_PROFILES="$PROFILES" \
|
||||
docker compose -p gendesign-metrics -f docker-compose.metrics.yml pull --quiet
|
||||
COMPOSE_PROFILES="$PROFILES" \
|
||||
|
|
@ -317,42 +354,6 @@ jobs:
|
|||
echo "Alertmanager: контейнер пересоздан — иначе читал бы конфиг по старому иноду."
|
||||
fi
|
||||
|
||||
# ── Prometheus: СНАЧАЛА проверить конфигурацию ─────────────────
|
||||
# Тот же порядок, что у Caddy ниже: проверка — здесь, применение —
|
||||
# только после неё (сама перезагрузка стоит в конце, после приёмки:
|
||||
# дёргать ручку раньше, чем процесс отвечает, нечем).
|
||||
#
|
||||
# ЗАЧЕМ ЭТО ВООБЩЕ. Правила подключены КАТАЛОГОМ, поэтому новый
|
||||
# текст контейнер видит сразу — но Prometheus читает правила только
|
||||
# при старте либо по явной перезагрузке, а деплой не делал ни того,
|
||||
# ни другого. 12.09: контейнер работал с 27.08, на диске (и внутри
|
||||
# контейнера) лежали правила из #3464, а /api/v1/rules отдавал
|
||||
# прежние. Зелёный деплой, новый файл на диске, старое поведение —
|
||||
# отказ без единого отрицательного признака. Значит и все прежние
|
||||
# правки ops/metrics/prometheus/** вступали в силу лишь тогда, когда
|
||||
# контейнер пересоздавался по какой-то посторонней причине.
|
||||
#
|
||||
# Проверяем ОДНОРАЗОВЫМ контейнером того же образа по файлам С
|
||||
# ДИСКА, а не `docker exec` в работающий, по двум причинам:
|
||||
# 1) prometheus.yml подключён бинд-маунтом ОДНОГО файла, и
|
||||
# работающий контейнер после `git reset` читает СТАРЫЙ инод —
|
||||
# exec проверял бы не тот текст, который применится;
|
||||
# 2) битый конфиг обязан остановить деплой ДО пересоздания
|
||||
# контейнера, иначе Prometheus не поднимется вовсе.
|
||||
#
|
||||
# Нужны ОБЕ команды: при пустом (или не смонтированном) каталоге
|
||||
# правил `check config` печатает «0 rule files found» и проходит, а
|
||||
# `check rules` по явному шаблону в этом случае падает — проверено
|
||||
# на стенде prom/prometheus:v3.1.0.
|
||||
PROM_IMAGE=$(grep -oE 'prom/prometheus:[^ ]+' docker-compose.metrics.yml | head -1)
|
||||
if ! docker run --rm -v "$PWD/ops/metrics/prometheus:/etc/prometheus:ro" \
|
||||
--entrypoint sh "$PROM_IMAGE" -c \
|
||||
'promtool check config /etc/prometheus/prometheus.yml && promtool check rules /etc/prometheus/rules/*.yml'; then
|
||||
echo "ОШИБКА: конфиг или правила Prometheus не проходят проверку — перезагрузка НЕ выполнена."
|
||||
echo "Работающий Prometheus не тронут и остаётся на прежней конфигурации. Чинить и повторять."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ── Бинд-маунт ОДНОГО файла держит инод ────────────────────────
|
||||
# Та же ловушка, что у Alertmanager выше и у Alloy в джобах агентов:
|
||||
# `git reset --hard` пишет изменившийся файл НОВЫМ инодом, а
|
||||
|
|
|
|||
|
|
@ -49,9 +49,14 @@ WORKFLOW = REPO_ROOT / ".forgejo" / "workflows" / "deploy-metrics.yml"
|
|||
COMPOSE = REPO_ROOT / "docker-compose.metrics.yml"
|
||||
|
||||
# Перезагрузка Prometheus: POST на ручку lifecycle из работающего контейнера.
|
||||
#
|
||||
# Хвост строки смотрим НАРОЧНО: `|| true` после команды глушит её отказ, и шаг
|
||||
# остаётся зелёным с неперечитанной конфигурацией — то есть ровно тот разрыв,
|
||||
# который тут и чинится, только теперь ещё и с «зелёной сводкой». Ревьюер
|
||||
# #3475 этой мутацией прошёл гейт насквозь.
|
||||
RELOAD = re.compile(
|
||||
r"docker exec gendesign-prometheus wget[^\n]*--post-data[^\n]*"
|
||||
r"http://localhost:9090/-/reload"
|
||||
r"http://localhost:9090/-/reload(?![^\n]*\|\|)"
|
||||
)
|
||||
# Проверка конфигурации: обе команды promtool по файлам с диска.
|
||||
CHECK = re.compile(
|
||||
|
|
@ -60,8 +65,10 @@ CHECK = re.compile(
|
|||
)
|
||||
# Ветка «проверка не прошла» — от `if ! ... promtool ...` до её `fi`.
|
||||
CHECK_FAILED_BRANCH = re.compile(r"if ! docker run[^\n]*promtool[^\n]*; then(.+?)\n\s*fi\n", re.S)
|
||||
# Одиночный файловый маунт в серверном стеке: ./ops/metrics/... : /путь/в/контейнере
|
||||
MOUNT = re.compile(r"-\s+\./(ops/metrics/\S+?):(/\S+?):ro")
|
||||
# Одиночный файловый маунт в серверном стеке: ./ops/metrics/... : /путь/в/контейнере.
|
||||
# Суффикс прав (`:ro`) НЕ требуется: инод маунт держит одинаково что на чтение,
|
||||
# что на запись, а требование `:ro` делало гейт слепым к маунту без него.
|
||||
MOUNT = re.compile(r"-\s+\./(ops/metrics/[^\s:]+):(/[^\s:]+)")
|
||||
|
||||
|
||||
def _server_script() -> str:
|
||||
|
|
@ -71,8 +78,13 @@ def _server_script() -> str:
|
|||
«команда целиком» ловил бы только первую строку.
|
||||
"""
|
||||
text = WORKFLOW.read_text(encoding="utf-8")
|
||||
start = text.index("Поднять серверный стек")
|
||||
end = text.index("# ═══ АГЕНТЫ")
|
||||
start = text.find("Поднять серверный стек")
|
||||
end = text.find("# ═══ АГЕНТЫ")
|
||||
assert start != -1 and end != -1, (
|
||||
"в deploy-metrics.yml не найдены границы серверной джобы: шаг «Поднять "
|
||||
"серверный стек» или заголовок «АГЕНТЫ» переименовали — гейт смотрел бы "
|
||||
"не туда, поправь якоря здесь"
|
||||
)
|
||||
return re.sub(r"\s*\\\n\s*", " ", text[start:end])
|
||||
|
||||
|
||||
|
|
@ -83,8 +95,9 @@ def test_rules_are_delivered_by_this_workflow() -> None:
|
|||
запускает: «объявлено» и «исполняется» разошлись бы уже на триггере.
|
||||
"""
|
||||
text = WORKFLOW.read_text(encoding="utf-8")
|
||||
paths = text[text.index("paths:") : text.index("workflow_dispatch")]
|
||||
assert '"ops/metrics/**"' in paths, (
|
||||
start, end = text.find("paths:"), text.find("workflow_dispatch")
|
||||
assert start != -1 and end != -1, "в deploy-metrics.yml не найден блок триггеров paths:"
|
||||
assert '"ops/metrics/**"' in text[start:end], (
|
||||
"деплой не запускается на правку ops/metrics/** — правила меняются мимо него"
|
||||
)
|
||||
|
||||
|
|
@ -118,6 +131,22 @@ def test_reload_comes_after_config_check() -> None:
|
|||
)
|
||||
|
||||
|
||||
def test_config_check_precedes_any_application() -> None:
|
||||
"""Проверка стоит ВЫШЕ `up -d`, а не между подъёмом и перезагрузкой.
|
||||
|
||||
`up -d` пересоздаёт контейнер при смене образа: проверка ниже него не
|
||||
успевает спасти — битый конфиг уже применён стартом нового контейнера, и
|
||||
Prometheus не поднимется вовсе.
|
||||
"""
|
||||
script = _server_script()
|
||||
check = CHECK.search(script)
|
||||
assert check, "в деплое нет проверки promtool (check config + check rules)"
|
||||
assert check.start() < script.index("up -d --remove-orphans"), (
|
||||
"проверка конфигурации стоит после `up -d` — контейнер успеет "
|
||||
"пересоздаться с битым конфигом раньше, чем деплой покраснеет"
|
||||
)
|
||||
|
||||
|
||||
def test_failed_check_stops_deploy_without_reload() -> None:
|
||||
"""Провалившаяся проверка обрывает деплой и НЕ перезагружает Prometheus."""
|
||||
branch = CHECK_FAILED_BRANCH.search(_server_script())
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue