fix(observability): стек не поднимался — привилегированную роль спрашиваем у контейнера, а не угадываем (#3078) #3105

Merged
lekss361 merged 1 commit from fix/3078-metrics-role-discovery into main 2026-08-26 09:27:30 +00:00
Owner

Что случилось

После мержа #3099 стек наблюдаемости не поднялся ни на одном хосте — ни одного контейнера ни на Beget, ни на Poincare. Файлы при этом доехали на обе машины, CI был зелёный, PR смержен.

Джоба server в deploy-metrics.yml упала, а agent-apps и agent-infra пропустились как зависимые. Из лога задачи 23657 (26.08 08:55):

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

Секреты были на месте — единственное предупреждение касалось выключенных телеграм-алертов, а падение произошло на создании read-only роли для датасорса Grafana.

Причина

scripts/setup-metrics-grafana-role.sh искал привилегированную роль перебором трёх имён:

for candidate in glitchtip forgejo postgres; do

Ни одно из трёх не совпадает ни с одним реальным кластером проекта — имя задаётся переменной POSTGRES_USER образа postgres:

контейнер POSTGRES_USER
gendesign-infra-postgres infra
gendesign-postgres-1 gendesign
tradein-postgres tradein

То есть скрипт упал бы на любом кластере проекта, а не только на этом.

Отдельная ирония: комментарий прямо над перебором предупреждал, что «угадывать postgres неверно: в образе оно задаётся переменной POSTGRES_USER» — и дальше шло угадывание.

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

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

Что стало

Имя берём у самого контейнера — POSTGRES_USER это ровно та переменная, которой роль создана при initdb, то есть источник истины, а не догадка:

_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

Прежний список оставлен после него запасным путём: он пригодится кластеру, поднятому не из образа postgres, где переменная пуста. Фикс добавляет источник истины, а не отменяет фолбэк.

Попутно: глоб в paths: деплоя

Воркфлоу запускает три setup-скрипта (secrets, grafana-role, exporter-dsn), а в триггере стоял только первый:

- "scripts/setup-metrics-secrets.sh"

Значит правка двух остальных не заводила выкат — на хосте молча продолжала исполняться старая версия. Тот же класс, что #2203 закрыл глобом ops/*.sh, и без этой правки текущий PR не задеплоил бы сам себя. Заменено на scripts/setup-metrics-*.sh.

Тесты

backend/tests/ops/test_3078_metrics_role_discovery.py, 6 штук. Исполняют РЕАЛЬНЫЙ скрипт с подставным docker (отвечает за inspect, отдаёт заданный POSTGRES_USER, принимает psql только от привилегированной роли) и проверяют фактический выбор роли, а не наличие правильных слов в комментарии:

  1. прод-случай infra — роль из контейнера выбрана, скрипт отработал;
  2. gendesign и tradein — два других кластера проекта;
  3. POSTGRES_USER пуст → работает прежний перебор (инвариант: фолбэк цел);
  4. привилегий нет ни у кого → по-прежнему громкая ошибка, а не тихий успех;
  5. структурный гард: каждый запускаемый деплоем setup-скрипт покрыт шаблоном из paths:.

Фальсификация: на исходном скрипте краснеют 3 из 5 ролевых тестов — проходят только те два, что фиксируют сохранённое поведение. Гард по paths: на исходном воркфлоу тоже краснеет и указывает пальцем на setup-metrics-exporter-dsn.sh.

Проверено помимо тестов

  • bash -n изменённого скрипта; YAML воркфлоу парсится;
  • в коммите нет CR (правил под Windows — для shell-скрипта CRLF смертелен);
  • tests/ops целиком — 28 passed.

Что этот PR НЕ чинит

Алерты останутся выключенными. В том же логе: METRICS_TELEGRAM_BOT_TOKEN/CHAT_ID не заданы. Метрики и логи собираются, но предупреждать некому. Alertmanager без канала — ровно тот же класс «механизм есть, вывода нет», ради которого #3078 и заводился. Переменные секретные, задать их — за владельцем.

TRADEIN_EXPORTER_DSN будет пуст на Poincare. setup-metrics-exporter-dsn.sh выводит его из TRADEIN_DATABASE_URL, а той переменной там нет — это #3094. То есть экспортер базы Меры не заведётся, пока не закрыт #3094.

Refs #3078, #3099

## Что случилось После мержа #3099 стек наблюдаемости **не поднялся ни на одном хосте** — ни одного контейнера ни на Beget, ни на Poincare. Файлы при этом доехали на обе машины, CI был зелёный, PR смержен. Джоба `server` в `deploy-metrics.yml` упала, а `agent-apps` и `agent-infra` пропустились как зависимые. Из лога задачи 23657 (26.08 08:55): ``` err: ОШИБКА: не нашёл роль с правом CREATE ROLE в gendesign-infra-postgres ``` Секреты были на месте — единственное предупреждение касалось выключенных телеграм-алертов, а падение произошло на создании read-only роли для датасорса Grafana. ## Причина `scripts/setup-metrics-grafana-role.sh` искал привилегированную роль **перебором трёх имён**: ```bash for candidate in glitchtip forgejo postgres; do ``` Ни одно из трёх не совпадает ни с одним реальным кластером проекта — имя задаётся переменной `POSTGRES_USER` образа postgres: | контейнер | POSTGRES_USER | |---|---| | `gendesign-infra-postgres` | **infra** | | `gendesign-postgres-1` | **gendesign** | | `tradein-postgres` | **tradein** | То есть скрипт упал бы на любом кластере проекта, а не только на этом. Отдельная ирония: комментарий прямо над перебором предупреждал, что «угадывать `postgres` неверно: в образе оно задаётся переменной `POSTGRES_USER`» — и дальше шло угадывание. **Замер на живом контейнере 26.08** (read-only, ничего не создавалось): ``` POSTGRES_USER изнутри контейнера: infra кандидат glitchtip → отказ кандидат forgejo → отказ кандидат postgres → отказ кандидат infra → 1 ``` ## Что стало Имя берём у самого контейнера — `POSTGRES_USER` это ровно та переменная, которой роль создана при `initdb`, то есть источник истины, а не догадка: ```bash _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 ``` Прежний список оставлен **после** него запасным путём: он пригодится кластеру, поднятому не из образа postgres, где переменная пуста. Фикс добавляет источник истины, а не отменяет фолбэк. ## Попутно: глоб в `paths:` деплоя Воркфлоу запускает **три** setup-скрипта (`secrets`, `grafana-role`, `exporter-dsn`), а в триггере стоял только первый: ```yaml - "scripts/setup-metrics-secrets.sh" ``` Значит правка двух остальных **не заводила выкат** — на хосте молча продолжала исполняться старая версия. Тот же класс, что #2203 закрыл глобом `ops/*.sh`, и без этой правки текущий PR не задеплоил бы сам себя. Заменено на `scripts/setup-metrics-*.sh`. ## Тесты `backend/tests/ops/test_3078_metrics_role_discovery.py`, 6 штук. Исполняют **РЕАЛЬНЫЙ** скрипт с подставным `docker` (отвечает за `inspect`, отдаёт заданный `POSTGRES_USER`, принимает `psql` только от привилегированной роли) и проверяют **фактический выбор роли**, а не наличие правильных слов в комментарии: 1. прод-случай `infra` — роль из контейнера выбрана, скрипт отработал; 2. `gendesign` и `tradein` — два других кластера проекта; 3. `POSTGRES_USER` пуст → работает прежний перебор (инвариант: фолбэк цел); 4. привилегий нет ни у кого → по-прежнему громкая ошибка, а не тихий успех; 5. структурный гард: каждый запускаемый деплоем setup-скрипт покрыт шаблоном из `paths:`. **Фальсификация:** на исходном скрипте краснеют **3 из 5** ролевых тестов — проходят только те два, что фиксируют сохранённое поведение. Гард по `paths:` на исходном воркфлоу тоже краснеет и указывает пальцем на `setup-metrics-exporter-dsn.sh`. ## Проверено помимо тестов - `bash -n` изменённого скрипта; YAML воркфлоу парсится; - в коммите нет CR (правил под Windows — для shell-скрипта CRLF смертелен); - `tests/ops` целиком — 28 passed. ## Что этот PR НЕ чинит **Алерты останутся выключенными.** В том же логе: `METRICS_TELEGRAM_BOT_TOKEN/CHAT_ID не заданы. Метрики и логи собираются, но предупреждать некому`. Alertmanager без канала — ровно тот же класс «механизм есть, вывода нет», ради которого #3078 и заводился. Переменные секретные, задать их — за владельцем. **`TRADEIN_EXPORTER_DSN` будет пуст на Poincare.** `setup-metrics-exporter-dsn.sh` выводит его из `TRADEIN_DATABASE_URL`, а той переменной там нет — это [#3094](https://git.gendsgn.ru/lekss361/gendesign/issues/3094). То есть экспортер базы Меры не заведётся, пока не закрыт #3094. Refs #3078, #3099
lekss361 added 1 commit 2026-08-26 09:09:22 +00:00
fix(observability): привилегированную роль спрашиваем у контейнера, а не угадываем (#3078)
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
33bc7e4acf
Стек наблюдаемости не поднялся ни на одном хосте после мержа #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.
lekss361 merged commit 1da2eee142 into main 2026-08-26 09:27:30 +00:00
lekss361 deleted branch fix/3078-metrics-role-discovery 2026-08-26 09:27:30 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#3105
No description provided.