All checks were successful
CI Trade-In / changes (pull_request) Successful in 9s
CI / changes (pull_request) Successful in 10s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Successful in 1m55s
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / backend-tests (pull_request) Successful in 17m25s
Джоба agent-apps упала целиком:
error while interpolating services.postgres-exporter-infra.environment.
DATA_SOURCE_NAME: required variable INFRA_EXPORTER_DSN is missing a value
INFRA_EXPORTER_DSN нужен экспортеру с profiles: ["infra"], который на
продуктовом хосте не поднимается вовсе. Но compose интерполирует ВЕСЬ файл
до фильтрации по профилям, поэтому `${VAR:?}` роняет команду из-за чужой
переменной. Вместе с агентом не поднялись alloy, node-exporter и cadvisor,
которым никакой DSN не нужен. Симметрично упал бы и инфраструктурный агент -
на двух продуктовых переменных.
Второй дефект в той же цепочке: GENDESIGN_EXPORTER_DSN тоже отсутствовал.
setup-metrics-exporter-dsn.sh читает только runtime-файл окружения бэкенда, а
DATABASE_URL и TRADEIN_DATABASE_URL живут в основном. Значит подстановка
всегда была пустой, add_key печатал "нечем заполнить" и выходил с кодом 0 -
мягкий пропуск встречался с жёстким требованием compose.
Стало:
- compose: `:-` вместо `:?` у трёх DSN. Интерполяция больше не может упасть.
- deploy-metrics.yml: профиль экспортеров включается, только если нужные ЭТОЙ
роли DSN заполнены; иначе ::warning и агент поднимается без экспортера.
Громкость не убрана, а перенесена туда, где роль известна. Тот же приём, что
уже применён к Alertmanager в джобе server.
- setup-metrics-exporter-dsn.sh: читает оба файла окружения (базовый, затем
runtime - он перекрывает). Пишет по-прежнему только в runtime, лишних копий
пароля не заводит.
Почему `:-` не ослабление: пустой DATA_SOURCE_NAME поднял бы экспортер,
который молча не отдаёт метрик, - ровно тот тихий отказ, ради которого весь
стек и заводится. Поэтому пустой DSN теперь означает "профиль не включаем",
а не "поднимаем пустым".
Известное следствие, отмеченное в коде: INFRA_EXPORTER_DSN не собирает никто -
скрипт знает только про GENDESIGN_/TRADEIN_ и работает на продуктовом хосте.
Пока это так, инфраструктурный агент будет честно предупреждать, что метрик
Postgres инфры нет, вместо того чтобы падать целиком.
Тесты (3) структурные, проверяют оба конца инварианта: обязательности не
вернулись в compose; каждая DSN-переменная проверяется в деплое; профили не
захардкожены. Фальсификация: на исходных файлах краснеют все три.
93 lines
4.2 KiB
Bash
93 lines
4.2 KiB
Bash
#!/usr/bin/env bash
|
||
# Собирает строки подключения для postgres_exporter на продуктовом хосте из
|
||
# паролей, которые там уже есть. Идемпотентен: заданные значения не трогает.
|
||
#
|
||
# ПОЧЕМУ НЕ ЗАВОДИМ ОТДЕЛЬНЫЕ СЕКРЕТЫ. Каждая новая копия пароля — ещё одно
|
||
# место, откуда он может утечь, и ещё одно, которое забудут повернуть при
|
||
# ротации. Экспортеру нужны те же учётки, что уже лежат в окружении хоста.
|
||
#
|
||
# Запускать на продуктовом хосте. Вызывается из deploy-metrics.yml.
|
||
set -euo pipefail
|
||
|
||
ENVF=/opt/gendesign/backend/.env.runtime
|
||
|
||
if [ ! -f "$ENVF" ]; then
|
||
echo " ОШИБКА: не найден файл окружения бэкенда." >&2
|
||
exit 1
|
||
fi
|
||
|
||
# Источники DSN лежат НЕ здесь. `DATABASE_URL` и `TRADEIN_DATABASE_URL` живут в
|
||
# основном файле окружения бэкенда, а $ENVF (runtime) хранит только то, что
|
||
# дописывается на хосте: SENTRY_RELEASE, METRICS_* и т. п. Скрипт читал лишь
|
||
# runtime, поэтому подстановка `${DATABASE_URL:-}` всегда была пустой, add_key
|
||
# молча писал «нечем заполнить» и выходил с кодом 0 — а compose затем падал на
|
||
# обязательной переменной. Мягкий пропуск встречался с жёстким требованием.
|
||
#
|
||
# Читаем оба, в порядке приоритета: сначала базовый, потом runtime (он должен
|
||
# перекрывать). Пишем по-прежнему ТОЛЬКО в runtime — лишних копий пароля не
|
||
# заводим.
|
||
BASE_ENVF=/opt/gendesign/backend/.env
|
||
|
||
set -a
|
||
if [ -f "$BASE_ENVF" ]; then
|
||
# shellcheck source=/dev/null
|
||
. "$BASE_ENVF"
|
||
fi
|
||
# shellcheck source=/dev/null
|
||
. "$ENVF"
|
||
set +a
|
||
|
||
add_key() {
|
||
local key="$1" value="$2"
|
||
if grep -qE "^${key}=" "$ENVF" 2>/dev/null; then
|
||
echo " $key — уже задан, не трогаю"
|
||
return 0
|
||
fi
|
||
if [ -z "$value" ]; then
|
||
echo " $key — нечем заполнить, пропускаю (метрики этой базы не поедут)"
|
||
return 0
|
||
fi
|
||
printf '%s=%s\n' "$key" "$value" >> "$ENVF"
|
||
echo " $key — записан"
|
||
}
|
||
|
||
# Экспортер ходит по сетевому алиасу, а не по IP: адреса контейнеров меняются
|
||
# при каждом пересоздании, и DSN с IP протух бы на первом же деплое.
|
||
add_key GENDESIGN_EXPORTER_DSN \
|
||
"${GENDESIGN_EXPORTER_DSN:-${DATABASE_URL:-}}"
|
||
|
||
add_key TRADEIN_EXPORTER_DSN \
|
||
"${TRADEIN_EXPORTER_DSN:-${TRADEIN_DATABASE_URL:-}}"
|
||
|
||
# postgres_exporter не понимает схему postgresql+psycopg:// из SQLAlchemy —
|
||
# ему нужна чистая postgresql://. Приводим, если досталась приложенческая форма.
|
||
python3 - "$ENVF" <<'PY'
|
||
import io, re, sys
|
||
|
||
path = sys.argv[1]
|
||
with io.open(path, encoding="utf-8") as fh:
|
||
lines = fh.readlines()
|
||
|
||
changed = False
|
||
out = []
|
||
for line in lines:
|
||
m = re.match(r'^((?:GENDESIGN|TRADEIN)_EXPORTER_DSN)=(.*)$', line.rstrip('\n'))
|
||
if not m:
|
||
out.append(line)
|
||
continue
|
||
key, dsn = m.group(1), m.group(2)
|
||
fixed = re.sub(r'^postgresql\+\w+://', 'postgresql://', dsn)
|
||
fixed = re.sub(r'^postgres\+\w+://', 'postgresql://', fixed)
|
||
if 'sslmode=' not in fixed:
|
||
fixed += ('&' if '?' in fixed else '?') + 'sslmode=disable'
|
||
if fixed != dsn:
|
||
changed = True
|
||
print(" %s — схема приведена к postgresql:// для экспортера" % key)
|
||
out.append("%s=%s\n" % (key, fixed))
|
||
|
||
if changed:
|
||
with io.open(path, "w", encoding="utf-8", newline="\n") as fh:
|
||
fh.writelines(out)
|
||
PY
|
||
|
||
echo " строки подключения экспортеров готовы"
|