#!/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 " строки подключения экспортеров готовы"