Перенесено в файл репозитория (а не только в служебную память VS Code): - результаты полной перегенерации и перепроверки всех трёх стендов; - подводные камни: случайные default от API (детектор дрейфа по побайтовому сравнению не работает); случайный default вшивается в Go-код → drift 15 файлов сразу после перегенерации; generated/<стенд>/go|docs стареют после шага 01; 403 без браузерного User-Agent (DDoS-Guard); - актуальная карта пайплайна, токены, что удалено и что оставлено осознанно.
18 KiB
2026-09-30 — Ужесточение пайплайна генерации YAML (безопасная замена, чистка легаси)
Связанные материалы:
TOOLS/README.md(канонический пайплайн, порядок шагов),TOOLS/ARCHITECTURE.md(спецификация), память репозитория:pipeline-legacy.md.
Задача
- Разобрать, что в генерации YAML устарело.
- Перегенерировать YAML по всем стендам так, чтобы старое удалялось безопасно, а новое создавалось атомарно.
- Задокументировать всё, чтобы история изменений прослеживалась.
Команда пользователя: «делай как ПОЛОЖЕНО, как в best practices».
Что было не так (до правок)
TOOLS/scripts/01_generate_yamls.sh
| Место (до) | Проблема |
|---|---|
стр. 230 rm -f "$output_glob" |
старый YAML удалялся до генерации → при сбое API файл исчезал, новый не создавался (неатомарно per-service) |
| стр. 122 «Полной очистки нет» | YAML исключённого сервиса оставался в каталоге и попадал в сборку |
стр. 91 ls -t "${ROOT_DIR}"/*.token |
легаси-фолбэк токена: в корне токенов нет; поиск «последнего» мог подхватить чужой токен |
стр. 109 API_ENDPOINT="${NUBES_API_ENDPOINT:-https://lk-api-gateway.ngcloud.ru/...}" |
молчаливый уход в PROD, если в профиле нет endpoint |
стр. 167 svc_name="" |
имя всегда пустое, хотя шапка обещала парсинг из списка → лишний HTTP-запрос на каждый сервис (2 запроса вместо 1) |
| стр. 46–50 | дубль SERVICES_FILE_DEFAULT (одинаковое присваивание в if) |
| шапка vs код | «REQUEST_DELAY по умолчанию 0.2», в коде 0.5; путь вывода указан как provider/resources_yaml (устарел) |
TOOLS/yaml-generator/internal/config/config.go
| Место (до) | Проблема |
|---|---|
Load() стр. 33 |
свой дефолт endpoint = PROD gateway |
loadToken() + findLatestToken() |
легаси-фолбэк «последний *.token в корне репо» |
Load() стр. 60 |
путь filepath.Join(repoRoot, "devops", "config", "services_list.txt"), причём FindRepoRoot() возвращает каталог provider/ → путь заведомо не существовал |
Легаси-скрипты
10_yaml_stability_run.sh, 11_yaml_stability_run_latest.sh,
12_generate_yamls_latest.sh, 13_generate_yamls_clean.sh,
02_generate_resources_and_docs_template_v2.sh — мертвы: зовут 01 без
--profile (→ exit 2), ищут *.token в корне репо, а 13 вдобавок делал
rm -f provider/resources_yaml/*.yaml. Ни один рабочий скрипт их не вызывает
(ссылки есть только в исторических HISTORY/, NOTES/).
Что сделано
1. 01_generate_yamls.sh — безопасная запись по принципу staging → атомарная замена
Новый алгоритм:
staging = generated/<stand>/resources_yaml.staging.<pid>
↓ генерация всех сервисов пачкой per-service в staging
↓ при пустом failures:
mv resources_yaml → resources_yaml.bak-<UTC> (бэкап, ротация KEEP_BACKUPS=5)
mv staging → resources_yaml (атомарный rename в том же FS)
↓ при непустом failures:
замена ОТМЕНЯЕТСЯ, рабочий каталог не тронут, staging оставлен для разбора, exit 1
Прочие изменения:
- каталог помечается маркером
.stand; генерация в каталог чужого стенда запрещена (exit 2); embed.goсоздаётся теперь в staging (обязательныйgo:embed *.yaml);NUBES_API_ENDPOINTобязателен, иначеexit 2;- токен берётся только из
NUBES_API_TOKEN/TOKEN_FILE; легаси-поиск удалён; - имя сервиса — из 2-го поля
services_list.txt; лишний python-запрос удалён; - удалён дубль
SERVICES_FILE_DEFAULT; синхронизированы комментарии.
2. yaml-generator/internal/config/config.go
NUBES_API_ENDPOINTобязателен (нет PROD-дефолта);loadToken()больше не ищет*.tokenв корне репо;findLatestTokenиgetenvDefaultудалены как мёртвые;- при отсутствии
NUBES_SERVICE_IDтребуется явныйNUBES_SERVICES_FILE(угадывание пути удалено).
3. Легаси-скрипты отключены (fail-fast)
В начало каждого добавлен guard: сообщение DEPRECATED + exit 2. Файлы не удалены
(удаление — отдельное решение владельца), но теперь они не могут сделать ничего вредного.
4. Документация
TOOLS/README.md— добавлен раздел «Канонический пайплайн (порядок шагов)» и описание безопасной генерации;TOOLS/ARCHITECTURE.md— ссылкиdevops/…заменены наTOOLS/config/<stand>/…;- память репозитория —
pipeline-legacy.mdуточнена.
Прогон по всем стендам (результат)
Токены проверены прямыми запросами к API (с браузерным User-Agent, иначе DDoS-Guard отдаёт 403):
| Стенд | Endpoint | HTTP | YAML после генерации | Stale | Failures |
|---|---|---|---|---|---|
| dev | lk-api-gateway-dev.ngcloud.ru |
200 | 40 | нет | нет |
| test | lk-api-gateway-test.ngcloud.ru |
200 | 36 | нет | нет |
| prod | lk-api-gateway.ngcloud.ru |
200 | 35 | нет | нет |
Команды:
./TOOLS/scripts/01_generate_yamls.sh --profile TOOLS/config/dev
./TOOLS/scripts/01_generate_yamls.sh --profile TOOLS/config/test
./TOOLS/scripts/01_generate_yamls.sh --profile TOOLS/config/prod
Проверка целостности: diff -rq нового каталога dev с бэкапом даёт различия только
в случайных default-суффиксах, которые API генерирует при каждом запросе
(db-ievgpdvu → db-ujama5rb, flask-seqtiq3t → flask-xwfdxdqh и т.п.) —
структурной регрессии нет. Это же объясняет, почему побайтовое сравнение двух
прогонов не может быть использовано как «детектор дрейфа».
Коммиты
12b3932—fix(tools): безопасная генерация YAML — staging + атомарная замена, без легаси-фолбэковad4daab—chore(tools): легаси-скрипты генерации отключены (fail-fast DEPRECATED)
Проверки
bash -nдля01_generate_yamls.shи всех guard-скриптов — OK;go vet ./...+go buildдляyaml-generator— OK;- guard отдаёт
exit 2; - прогон dev/test/prod — 0 failures, stale отсутствует, staging не остаётся;
- бэкапы создаются:
generated/dev/resources_yaml.bak-20260930T063152Zи т.д.
Этап 2 — удаление мёртвого (по команде «удаляй всё старое, аккуратно»)
Удалено (1e796c8) — 100% мёртвый код/данные, ничего их не вызывает:
| Файл | Почему удалён |
|---|---|
TOOLS/scripts/10_yaml_stability_run.sh |
зовёт 01 без --profile (exit 2), ищет *.token в корне |
TOOLS/scripts/11_yaml_stability_run_latest.sh |
цепочка на 10, та же поломка |
TOOLS/scripts/12_generate_yamls_latest.sh |
зовёт 01 без --profile, ищет *.token в корне |
TOOLS/scripts/13_generate_yamls_clean.sh |
цепочка на 12 + делал rm -f provider/resources_yaml/*.yaml |
TOOLS/scripts/02_generate_resources_and_docs_template_v2.sh |
легаси-дубль канонического 02_generate_resources_and_docs_v2.sh |
TOOLS/config/services_list.txt (общий) |
код его не читает; как «объединение» устарел: активный 27 (в test/prod — «нет в UI»), нет 87/88/97/153, которые есть в dev |
Проверка «ничего не вызывает»: grep по всему репо находил ссылки только в
исторических HISTORY/, NOTES/, docs/ (не исполняются).
Правки ссылок (c822ae2): README.md, HOW_TO/README.md,
HOW_TO/DEVOPS_BUILD_PIPELINE.md, HOW_TO/HOWTO_ADD_NEW_SERVICE.md (включая
переписанный блок «Быстрый старт» с devops/ на ./TOOLS/scripts/*),
DOCS_PIPELINE/README.md, scripts/publish-doc-page.sh, .gitignore.
Проверка после удаления: bash -n для всех TOOLS/scripts/*.sh и
scripts/publish-doc-page.sh — OK; smoke-прогон 01 --profile TOOLS/config/dev —
40 YAML, замена атомарная, бэкап создан.
Не удалено (осознанно):
- поддержка легаси-прокси
index.cfmв01иyaml-generator— это совместимость с работающими пользователями старого API (провайдер v5.0.75,secrets/stands.md); DOCS_PIPELINE/publish-docs.sh— сам файл помечен «справочная копия, не подменяет пайплайн»;HISTORY/,NOTES/,docs/— исторические документы (в нихdevops/и легаси-скрипты упоминаются как история, это нормально);scripts/*.pyиs3_notification_example.sh— ручные утилиты, вызываются вручную.
Этап 3 — сверка списков стендов с облачным каталогом (источник истины)
Принято: истина — то, что перечислено в облаке. Определяется эндпоинтом каталога:
# «перечислено в облаке» (продакшен-готовые сервисы стенда)
GET {NUBES_API_ENDPOINT}/services?limit=200&isProductionReady=true
# для сравнения: без фильтра отдаются ВСЕ сервисы платформы, включая
# DEPRECATED и не заявленные в каталоге (48 у prod, 49 у test, 60 у dev)
Требуется браузерный User-Agent (иначе DDoS-Guard отдаёт 403) и Referer.
Результат на 2026-09-30:
| Стенд | Облако (isProductionReady=true) |
Активных в services_list.txt |
Лишние в файле | Не хватало |
|---|---|---|---|---|
| dev | 40 | 40 | нет | нет |
| test | 36 | 36 | нет | нет |
| prod | 36 | 35 → 36 | нет | 151 k8sOpenbao (Vault) |
У остальных 12 закомментированных prod-сервисов, присутствующих в API, isProductionReady=false —
они закомментированы обоснованно. Четыре id в файле отсутствуют в каталоге prod вовсе
(32 vmpostgre, 87 k8svalkey, 153 nifi, 175 k8sGo).
Исправлено коммитом a68a36a: 151 k8sOpenbao раскомментирован (комментарий «нет в PROD UI»
устарел), prod перегенерирован — 36 YAML, ровно как в облаке.
Этап 4 — перепроверка после полной перегенерации + подводные камни
Команда: «сгенери YAML для всех стендов, проследи чтобы старого ничего не осталось,
перепроверь после генерации всё». Выполнено три прогона 01:
for s in dev test prod; do ./TOOLS/scripts/01_generate_yamls.sh --profile TOOLS/config/$s; done
# все три: exit=0
Результат перепроверки (2026-09-30)
| Стенд | YAML | = активных в списке | = облако (isProductionReady=true) |
Stale | Дубли id | Failures |
|---|---|---|---|---|---|---|
| dev | 40 | ✅ 40 | ✅ 40 | нет | нет | пусто |
| test | 36 | ✅ 36 | ✅ 36 | нет | нет | пусто |
| prod | 36 | ✅ 36 | ✅ 36 | нет | нет | пусто |
Дополнительно проверено:
- staging-каталоги (
resources_yaml.staging.*) — не осталось ни одного; - в
resources_yaml/только*.yaml,.stand,embed.go— посторонних файлов нет; .standв каждом каталоге совпадает с профилем (dev/test/prod);- бэкапы прошлых версий: dev 3, test 2, prod 2 (ротация
KEEP_BACKUPS=5); generated/<стенд>/tmp/yaml_gen_failures.txt— пусты;git status— чисто.
⛔ Подводные камни, найденные при перепроверке (важно на будущее)
- API отдаёт случайные
default. Часть параметров приходит со случайным суффиксом (db-ievgpdvu→db-ujama5rb,kvname-grzjes7l→kvname-g3s0uof2,flask-seqtiq3t→flask-xwfdxdqh). Поэтому побайтовое сравнение двух прогонов не является детектором дрейфа — различия в этих строках не регрессия. - Случайный
defaultвшивается в сгенерированный Go-код. Пример:generated/dev/go/151_k8s_openbao_kv_resource.goсодержитDefault: stringdefault.StaticString("kvname-XXXX"). Следствие:check_generated_drift.sh devпоказывает дрейф 15 файлов сразу после любой перегенерации YAML — это не ошибка оператора. - Производные артефакты стареют молча.
generated/<стенд>/goиgenerated/<стенд>/docsсоздаются шагом02и после нового01становятся старше своих источников (на момент проверки:go/docsdev — 08:46, YAML dev — 10:18). Отдельно живёт эфемерная копияprovider/internal/resources_gen+provider/resources_yaml(её кладётdev-materialize.sh, маркер.stand= стенд). Их нужно обновлять шагом02после каждого01. - Прямые HTTP-запросы к API без браузерного
User-Agentполучают 403 (DDoS-Guard). СUser-Agent+Referer— 200.
Актуальная карта пайплайна на 2026-09-30
- Единственный путь генерации YAML:
TOOLS/scripts/01_generate_yamls.sh --profile TOOLS/config/<стенд>(--profileобязателен, без негоexit 2). - Один универсальный движок на все стенды:
TOOLS/bin/yaml-generator, стенд задаётся переменными окружения (NUBES_API_ENDPOINT,NUBES_API_TOKEN,NUBES_SERVICE_ID,NUBES_SERVICE_NAME,NUBES_OUTPUT_DIR); список сервисов — свой у каждого стенда. - Стенд-специфичных хардкодов в коде нет — контролируется
check_hardcoded_service_ids.sh. - Токены:
secrets/{dev,test,prod}.token(валидны на 2026-09-30, срок до 2026-12-27); обновление —TOOLS/scripts/00_token_manager.sh(keycloak refresh,THRESHOLD_MIN=10). - Удалены как мёртвые (
1e796c8):10/11/12/13_yaml_*.sh,02_generate_resources_and_docs_template_v2.sh, общийTOOLS/config/services_list.txt. - Оставлены осознанно: поддержка легаси-прокси
index.cfm, справочная копияDOCS_PIPELINE/publish-docs.sh, историческиеHISTORY//NOTES//docs/, ручные утилитыscripts/*.py.
Открытые вопросы (на решение владельца)
- Поддержка легаси-прокси
index.cfm: оставляем или выпиливаем (README уже помечает закрытые API как «не использовать»)? - Прочие
.gitignore-паттерны мёртвых каталогов (universal_rebuild/*,provider/generated/) — чистить?