gendesign/scripts/setup-metrics-grafana-role.sh
bot-backend 33bc7e4acf
All checks were successful
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 12s
CI / frontend-tests (pull_request) Has been skipped
CI Trade-In / changes (pull_request) Successful in 10s
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Successful in 2m2s
CI / backend-tests (pull_request) Successful in 17m21s
fix(observability): привилегированную роль спрашиваем у контейнера, а не угадываем (#3078)
Стек наблюдаемости не поднялся ни на одном хосте после мержа #3099: джоба
server упала, agent-apps и agent-infra пропустились как зависимые.

Причина (задача 23657, 26.08 08:55):

    err:   ОШИБКА: не нашёл роль с правом CREATE ROLE в gendesign-infra-postgres

setup-metrics-grafana-role.sh искал привилегированную роль перебором трёх
имён - glitchtip, forgejo, postgres. Ни одно не совпадает ни с одним реальным
кластером проекта: infra-postgres -> infra, gendesign-postgres-1 -> gendesign,
tradein-postgres -> tradein. Комментарий над перебором сам предупреждал, что
"угадывать postgres неверно", и дальше шло угадывание.

Замер на живом контейнере 26.08 (read-only, ничего не создавалось):

    POSTGRES_USER изнутри контейнера: infra
    glitchtip - отказ, forgejo - отказ, postgres - отказ, infra - 1

Стало: имя берём из POSTGRES_USER самого контейнера - это та переменная,
которой роль и создана при initdb, то есть источник истины. Прежний список
оставлен ПОСЛЕ него запасным путём для кластера не из образа postgres.

Попутно - глоб в paths деплоя. Воркфлоу запускает ТРИ setup-скрипта, а в
триггере стоял только setup-metrics-secrets.sh: правка двух остальных не
заводила выкат, и на хосте молча оставалась старая версия. Тот же класс, что
#2203 закрыл глобом ops/*.sh. Добавлен тест, который сверяет запускаемые
скрипты с шаблонами paths - на исходном воркфлоу он краснеет, указывая на
setup-metrics-exporter-dsn.sh.

Тесты (6) исполняют РЕАЛЬНЫЙ скрипт с подставным docker и проверяют
фактический выбор роли, а не наличие правильных слов в комментарии.
Фальсификация: на исходном коде краснеют 3 из 5 ролевых тестов; проходят
только те два, что фиксируют сохранённое поведение (запасной перебор и
громкая ошибка при отсутствии привилегий). tests/ops целиком - 28 passed.
2026-08-26 12:08:37 +03:00

95 lines
5.2 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
# Заводит read-only роль grafana_ro в базе glitchtip — под датасорс Grafana.
# Идемпотентен: повторный запуск только досогласует права.
#
# ПОЧЕМУ ОТДЕЛЬНАЯ РОЛЬ, А НЕ ВЛАДЕЛЕЦ БАЗЫ. Датасорс Grafana доступен всякому,
# кто вошёл в интерфейс, и позволяет выполнять произвольный SQL в панелях.
# Владельческая роль сделала бы витрину способом уронить трекер ошибок. Здесь
# прав на запись нет вовсе, поэтому худшее, что можно сделать через панель, —
# медленный SELECT.
#
# Запускать на инфраструктурном хосте. Вызывается из deploy-metrics.yml.
set -euo pipefail
CONT=gendesign-infra-postgres
DB=glitchtip
ROLE=grafana_ro
: "${GLITCHTIP_RO_PASSWORD:?GLITCHTIP_RO_PASSWORD не задан — запусти scripts/setup-metrics-secrets.sh}"
if ! docker inspect "$CONT" >/dev/null 2>&1; then
echo " $CONT не найден — пропускаю создание роли."
exit 0
fi
# Ищем роль с правом заводить других. Имя суперпользователя в этом кластере
# нигде не зафиксировано, а угадывать «postgres» неверно: в образе оно задаётся
# переменной POSTGRES_USER и здесь ею не является.
#
# Поэтому НЕ угадываем, а спрашиваем сам контейнер: POSTGRES_USER — это та самая
# переменная, которой роль и была создана при initdb, то есть источник истины.
# Прежний список был перебором трёх имён (glitchtip/forgejo/postgres), и ни одно
# из них не совпадало ни с одним реальным кластером проекта:
#
# gendesign-infra-postgres → infra
# gendesign-postgres-1 → gendesign
# tradein-postgres → tradein
#
# Из-за этого деплой стека падал на первом же прогоне (#3099, задача 23657):
# ОШИБКА: не нашёл роль с правом CREATE ROLE в gendesign-infra-postgres
# Замер 26.08 на живом контейнере: glitchtip/forgejo/postgres — отказ,
# infra — проходит. Список имён оставлен ПОСЛЕ него запасным путём: он
# пригодится кластеру, поднятому не из образа postgres, где POSTGRES_USER пуст.
SUPER=""
_pg_user="$(docker exec "$CONT" sh -c 'printf %s "${POSTGRES_USER:-}"' 2>/dev/null || true)"
for candidate in $_pg_user glitchtip forgejo postgres; do
[ -n "$candidate" ] || continue
if docker exec "$CONT" psql -U "$candidate" -d postgres -tAc \
"SELECT 1 FROM pg_roles WHERE rolname = CURRENT_USER AND (rolsuper OR rolcreaterole)" 2>/dev/null \
| grep -q 1; then
SUPER="$candidate"
break
fi
done
if [ -z "$SUPER" ]; then
echo " ОШИБКА: не нашёл роль с правом CREATE ROLE в $CONT." >&2
echo " Проверь вручную: docker exec $CONT psql -U <роль> -c '\\du'" >&2
exit 1
fi
echo " привилегированная роль: $SUPER"
# Пароль передаётся через переменную окружения psql, а не в тексте запроса —
# иначе он осел бы в pg_stat_statements и в логе запросов.
docker exec -e RO_PASS="$GLITCHTIP_RO_PASSWORD" -i "$CONT" \
psql -U "$SUPER" -d "$DB" -v ON_ERROR_STOP=1 <<'SQL'
\set ro_pass `echo "$RO_PASS"`
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'grafana_ro') THEN
CREATE ROLE grafana_ro LOGIN;
RAISE NOTICE 'роль grafana_ro создана';
ELSE
RAISE NOTICE 'роль grafana_ro уже есть';
END IF;
END
$$;
ALTER ROLE grafana_ro WITH PASSWORD :'ro_pass';
-- Ограничение на число соединений: панель с автообновлением способна открыть
-- их десятками, а это тот же кластер, где живёт Forgejo.
ALTER ROLE grafana_ro CONNECTION LIMIT 8;
GRANT CONNECT ON DATABASE glitchtip TO grafana_ro;
GRANT USAGE ON SCHEMA public TO grafana_ro;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO grafana_ro;
-- Таблицы событий партиционированы по неделям: новые партиции появляются сами,
-- и без этой строки датасорс начал бы отдавать пустоту на свежих данных,
-- оставаясь при этом «рабочим». Тихий отказ ровно того сорта, который мы ловим.
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO grafana_ro;
SQL
echo " grafana_ro: права выданы на $DB"