docs: Opus analysis split into 3 files + follow-up questions
This commit is contained in:
@@ -0,0 +1,305 @@
|
||||
Что сказать Opus — кратко
|
||||
Задача: глубокий анализ проекта, найти слабые места, предложить улучшения.
|
||||
|
||||
10 вопросов:
|
||||
Два провайдера (Legacy + Universal) — как и когда выпилить Legacy?
|
||||
Генерационный конвейер — API→YAML→Go→S3. Надёжен ли? Что если API поменяет формат?
|
||||
API Flow V6 — 6 шагов без транзакционности. Что при обрыве посредине?
|
||||
CRUD/Adopt — сложнейшая логика в adoptExistingInstanceOnCreate. Все ли краевые кейзы покрыты?
|
||||
Генератор gen_v2 — шаблоны вшиты в бинарь. Как расширять? Что с новыми kind'ами операций?
|
||||
Баги — 23 файла истории. Какие системные? Фильтрация deleted — главная проблема.
|
||||
Матрица состояний — 16 состояний, все ли обрабатываются?
|
||||
Тесты — crud_test.go, client_test.go. Достаточно ли?
|
||||
Безопасность — токены, GPG-ключ в репозитории, InsecureSkipVerify.
|
||||
Конкурентность — два terraform apply одновременно?
|
||||
Что читать (приоритет):
|
||||
ARCHITECTURE.md
|
||||
client.go
|
||||
crud.go
|
||||
generate_resources_v2.go
|
||||
|
||||
|
||||
Searched for regex `.*` (`tf_provider` · `universal_rebuild/internal/resources_core/crud_test.go`), 56 results
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/universal_rebuild/tools/gen_v2/generate_resources_v2.go#210-210), lines 210 to 350
|
||||
|
||||
Searched for regex `buildSubresourceForceNewCodes|computeCreateOnly|analyzeJsonPlanMod` (`tf_provider` · `universal_rebuild/tools/gen_v2/generate_resources_v2.go`), 10 results
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/universal_rebuild/internal/core/client_test.go)
|
||||
|
||||
Searched for regex `func.*IsJson|func.*RefSvc|func.*computeCreateOnly|func.*analyzeJsonPlanMod` (`tf_provider` · `universal_rebuild/tools/gen_v2/generate_resources_v2.go`), 3 results
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/universal_rebuild/tools/gen_v2/generate_resources_v2.go#2040-2040), lines 2040 to 2150
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/.gitignore)
|
||||
|
||||
Read [](file:///home/naeel/.vscode-server/data/User/workspaceStorage/358cf615179442f06eab7245b983b753/GitHub.copilot-chat/chat-session-resources/2c7367dd-6d7f-48b4-84d0-25bbb06f7cfb/toolu_01RcjLgQg9SVXsgzGtT1p8Wa__vscode-1782817912278/content.txt)
|
||||
|
||||
Изучил архитектуру, ядро, генератор, историю багов и тесты. Сохраню анализ и представлю его.
|
||||
|
||||
Created memory file [](file:///home/naeel/.vscode-server/data/User/workspaceStorage/358cf615179442f06eab7245b983b753/GitHub.copilot-chat/memory-tool/memories/MmM3MzY3ZGQtNmQ3Zi00OGI0LTg0ZDAtMjViYmIwNmY3Y2Zi/plan.md)
|
||||
|
||||
# Анализ Terraform Provider для Nubes Cloud
|
||||
|
||||
Изучил архитектуру (ARCHITECTURE.md), ядро (client.go, crud.go), генератор (generate_resources_v2.go), транспорт (provider.go), 24 файла истории и тесты. Ниже — ответы по всем 10 пунктам: текущее состояние → плюсы → минусы → предложения → риски.
|
||||
|
||||
---
|
||||
|
||||
## 1. Два провайдера: Legacy + Universal
|
||||
|
||||
**Состояние.** Legacy (internal, v5.0.52, реестр `registry.terraform.io/nubes/nubes`, 13 ручных ресурсов) и Universal (universal_rebuild, v5.0.51, реестр `terra.k8c.ru/nubes/nubes`, ~50 генерируемых). Оба на `terraform-plugin-framework`, но это **полностью независимые деревья кода** с дублирующимся ядром (core существует в обоих).
|
||||
|
||||
**Хорошо.** Universal — целевая архитектура, чёткие принципы в ARCHITECTURE.md (YAML = source of truth). Разные реестры → можно мигрировать без коллизий.
|
||||
|
||||
**Плохо.** Дублирование ядра (client.go, `instance_lookup.go`) — баги фиксятся в двух местах (см. баг #1: правка в client.go И instance_lookup.go). Версии разъехались (5.0.52 vs 5.0.51) — непонятно, какая «свежее». Нет формального deprecation-плана с датой.
|
||||
|
||||
**Предложения.**
|
||||
- Зафиксировать **матрицу соответствия ресурсов** Legacy→Universal: какие 13 ресурсов уже перекрыты Universal, какие нет.
|
||||
- Объявить Legacy *frozen* (только critical-фиксы), завести `DEPRECATED.md` с целевой версией снятия.
|
||||
- Перенести уникальную логику Legacy (VM/vApp/edge/vdc) в YAML-спеки, проверить паритет, затем archive Legacy в отдельную ветку/тег.
|
||||
|
||||
**Риск.** VM/vApp в Legacy содержат ручную логику (FW-rules, 500-фикс из 04_vm_hang_fix_and_500_error), которую генератор может не воспроизвести. Нужен паритетный прогон на тестовом стенде до снятия Legacy.
|
||||
|
||||
---
|
||||
|
||||
## 2. Генерационный конвейер (API→YAML→Go→S3)
|
||||
|
||||
**Состояние.** 4 шага: `service_spec_gen` (API→YAML, ATTEMPTS=3, REQUEST_DELAY=0.5) → `gen_v2` (YAML→Go) → build+GPG+S3 → mkdocs. Включение сервиса = строка в `services_list.txt`.
|
||||
|
||||
**Хорошо.** Чёткое разделение, профили dev/test/prod изолируют артефакты, retry на шаге сбора YAML.
|
||||
|
||||
**Плохо — главное расхождение с собственными принципами.** ARCHITECTURE.md декларирует «*The generator must enforce these rules and fail fast on drift*», но фактически:
|
||||
- **Неизвестный `kind` операции тихо игнорируется** (generate_resources_v2.go — три `if op.Kind != "..." { continue }`). Если API введёт новый kind — ресурс молча пропадёт из провайдера, без ошибки.
|
||||
- **YAML почти не валидируется**: проверяется только синтаксис (`yaml.Unmarshal`). Отсутствие `op.Kind`/`op.Action` → zero-value → тихое игнорирование. Нет проверки уникальности param ID, наличия required-полей, валидности `RefSvcId`.
|
||||
- При ошибке `format.Source` генератор пишет **неформатированный (возможно битый) код** как fallback вместо остановки.
|
||||
- Исключение сервиса только комментированием в `services_list.txt` → риск рассинхрона (закомментировали в test, забыли в prod).
|
||||
|
||||
**Предложения.**
|
||||
- Добавить фазу `validateSpec()` перед генерацией: required-поля, уникальность ID, известность `kind`/`action`, ссылочная целостность `RefSvcId`. **Fail fast** на неизвестном kind.
|
||||
- Убрать fallback на неформатированный код — при `format.Source` error → паника с понятным сообщением.
|
||||
- CI-шаг «генерация без diff»: прогон генератора → `git diff --exit-code` (детект дрейфа, как требует ARCHITECTURE.md).
|
||||
- Go для генераторов оправдан (один язык со сгенерированным кодом, `text/template`, `format.Source`); bash-обёртки — лишь оркестрация. Менять не нужно.
|
||||
|
||||
**Риск.** Изменение формата ответа API на шаге 1 не обнаружится до runtime у пользователя. Сейчас единственная защита — `ATTEMPTS=3`, что не ловит *семантический* дрейф (поле переименовали, а не пропало).
|
||||
|
||||
---
|
||||
|
||||
## 3. API Flow V6 — отсутствие транзакционности
|
||||
|
||||
**Состояние.** 6 шагов в `CreateGenericInstanceUniversalV6`: `POST /instances` → `POST /instanceOperations` → `GET cfsParams` → resolve refs → `POST` каждого param → `validate-cfs` → `run` → `waitForOperationFinish`.
|
||||
|
||||
**Хорошо.** Завершение по `dtFinish` — надёжный контракт (выстрадан в 02, 05). Нормализация пустых map/json/array есть.
|
||||
|
||||
**Плохо.**
|
||||
- **Orphan при обрыве.** Если процесс упал/таймаут после `POST /instances`, но до `run` — в облаке остаётся инстанс в состоянии `not_created`/`creating`, **не попавший в Terraform state**. Следующий apply найдёт его через `FindInstanceByDisplayName` и упрётся в ошибку «найден в состоянии Not Created; удалите вручную». То есть пользователь обязан чистить руками.
|
||||
- **`doRequest` без retry** (client.go) — нет обработки 429/503/сетевых сбоев. Любой transient-сбой на шаге 4–5 рвёт create.
|
||||
- `req.Close = true` — новое TCP+TLS соединение на каждый запрос (на длинном поллинге дорого).
|
||||
|
||||
**Предложения.**
|
||||
- Retry с экспоненциальным backoff в `doRequest` для идемпотентных GET и для 429/503/сетевых ошибок (с уважением `Retry-After`).
|
||||
- Идемпотентность create: перед `POST /instances` делать `FindInstanceByDisplayName` (уже есть в `CreateResource`) — но также **обрабатывать «недосозданный» инстанс**: предлагать авто-cleanup `not_created`-инстанса при `adopt_existing_on_create=true`, а не только ручное удаление.
|
||||
- Рассмотреть keep-alive (убрать `req.Close=true`) для поллинга — меньше TLS-handshake.
|
||||
|
||||
**Риск.** Авто-cleanup `not_created` — операция удаления, требует явного флага и подтверждения семантики (нельзя удалять то, что пользователь мог создавать вручную параллельно).
|
||||
|
||||
---
|
||||
|
||||
## 4. CRUD / Adopt — `adoptExistingInstanceOnCreate`
|
||||
|
||||
**Состояние.** Покрытые ветки в crud.go:
|
||||
1. `operation_in_progress`/`pending` → ошибка «дождитесь».
|
||||
2. `!adopt_existing_on_create` → ошибка с подсказкой про import.
|
||||
3. `not_created` → ошибка.
|
||||
4. `running` → ref-валидация → adopt (возврат UUID).
|
||||
5. `suspended` → required-params check → resume → проверка статуса после resume → ref-валидация → adopt.
|
||||
6. Иначе (`creating`/`failed`/`error`) → общая ошибка «статус не подходит для авто-усыновления».
|
||||
|
||||
**Хорошо.** Логика соответствует decision-matrix из ARCHITECTURE.md. Ref-валидация при adopt (баг 22) закрыта. Диагностики подробные.
|
||||
|
||||
**Плохо.**
|
||||
- **Конкурентность не покрыта** (см. п.10): между `FindInstanceByDisplayName` и `CreateGenericInstanceUniversalV6` нет блокировки.
|
||||
- `creating`/`failed` падают в *общую* ветку с менее информативным сообщением, чем требует ARCHITECTURE.md (там для `creating`/`pending`/`failed` предписана отдельная диагностика).
|
||||
- Функция ~100 строк, глубокая вложенность, ref-валидация дублируется в двух ветках (running и после resume) — риск рассинхрона при правках.
|
||||
- `state_only`/`detach` destroy → `return nil` без API-вызова: инстанс остаётся в облаке (**это by design** из 20, но «orphaned» с т.з. биллинга — пользователь должен понимать).
|
||||
|
||||
**Предложения.**
|
||||
- Вынести классификацию статуса в один `switch` с явными ветками для каждого из 16 состояний (см. п.7), убрать дублирование ref-валидации в helper.
|
||||
- Для `creating`/`failed` — отдельные сообщения по контракту ARCHITECTURE.md.
|
||||
- Покрыть adopt-матрицу таблично-управляемыми тестами (сейчас 0 тестов на adopt, п.8).
|
||||
|
||||
**Риск.** Рефакторинг самой сложной функции без тестов опасен — сначала тесты, потом рефакторинг.
|
||||
|
||||
---
|
||||
|
||||
## 5. Генератор gen_v2 — шаблоны вшиты в бинарь
|
||||
|
||||
**Состояние.** 3 inline-шаблона `text/template`: `instanceTemplate` (569 строк), `subresourceTemplate` (443), `actionTemplate` (174). `computeCreateOnly` = эвристика (param в create, но не в modify). Identity подресурса: нет modify → все params ForceNew; есть modify → createOnly+delete params ForceNew.
|
||||
|
||||
**Хорошо.** Эвристика createOnly опирается на данные API (не хардкод), `format.Source` гарантирует валидный Go при успехе, восстановление регистра UUID решает «inconsistent result».
|
||||
|
||||
**Плохо.**
|
||||
- Шаблоны как строковые константы внутри `.go` (1186 строк шаблонов) — тяжело поддерживать, нет подсветки/линтинга шаблонов, любая правка = пересборка генератора.
|
||||
- **Новый kind → тихое выпадение ресурса** (см. п.2).
|
||||
- `data_type: json` → `IsJson`+`JsonNormalize` plan-modifier — но **0 тестов** на это, а нормализация JSON исторически проблемная (V2→V6 цикл, баг 13).
|
||||
- Immutable определяется только через отсутствие в modify — если API *временно* не отдаёт modify-параметр (сбой/неполный YAML), параметр ошибочно станет ForceNew → пересоздание ресурса.
|
||||
|
||||
**Предложения.**
|
||||
- Вынести шаблоны в `embed.FS` (`//go:embed templates/*.tmpl`) — поддерживаемость без потери single-binary.
|
||||
- Fail fast на неизвестном kind + лог числа сгенерированных ресурсов на сервис (детект «пропал ресурс»).
|
||||
- Снапшот-тесты генератора: эталонный YAML → ожидаемый `.go` (golden files).
|
||||
- Защита от «исчезнувшего modify-параметра»: предупреждать, если у сервиса есть create-params, но 0 modify-params (подозрительно).
|
||||
|
||||
**Риск.** Если `gen_v2` сломается — ломается **весь** Universal-провайдер. Сейчас единственная страховка — `format.Source`, который при ошибке всё равно пишет битый код.
|
||||
|
||||
---
|
||||
|
||||
## 6. Баги из истории — что системное, что осталось
|
||||
|
||||
**Системные классы (порождали серии багов):**
|
||||
1. **Фильтрация deleted** (23, 22, 21) — API возвращает deleted-инстансы, провайдер их не отсеивал. **Закрыт 5.0.50** (`isDeleted=false` + `isInstanceDeleted()` + ошибка при >1 совпадении).
|
||||
2. **Определение конца операции** (05, 02, 04) — зависание поллинга. **Закрыт** (критерий `dtFinish`).
|
||||
3. **Динамические param ID** (create ID ≠ modify ID, 06) — **закрыт** runtime-discovery.
|
||||
4. **Create-only параметры** (16) — **закрыт 5.0.38**.
|
||||
|
||||
**Не до конца решённые / ограничения:**
|
||||
- **Нормализация map/json/list** — потребовала 5 итераций (V2→V6), помечена как *частично*; новые типы параметров могут снова всплыть.
|
||||
- **Realm-валидация отключена** (21) из-за бага бэкенда `/resourceRealms/available` — валидации realm до деплоя нет.
|
||||
- **FW-rules 500** (04) — **platform-side bug**, воспроизводится и в Cloud Console; провайдер не может починить.
|
||||
- **Disk shrink** (06) — ограничение платформы (только увеличение), провайдер корректно прокидывает ошибку.
|
||||
|
||||
**Главная системная проблема** — именно фильтрация deleted была корнем 3+ багов. Сейчас закрыта, но **отсутствие тестов** означает, что регрессия не будет поймана автоматически.
|
||||
|
||||
**Предложения.** Regression-тесты на deleted-фильтрацию и multi-match; превратить known-limitations в явные диагностики (например, предупреждать про realm «валидация недоступна»).
|
||||
|
||||
---
|
||||
|
||||
## 7. Матрица состояний — 16 состояний
|
||||
|
||||
**Состояние.** `INSTANCE_STATES.md` / `STATE_TRANSITIONS.md` описывают полную матрицу. В коде adopt обрабатывает: `not_created`, `running`, `suspended`, `in_progress`/`pending`; остальные → общая ошибка.
|
||||
|
||||
**Плохо.** Хелперы `isStatusSuspended`/`isStatusNonAdoptable`/`isStatusNotCreated` работают через `strings.Contains` по тексту `explainedStatus` — **хрупко**: изменение формулировки статуса в API сломает классификацию молча. Промежуточные (`creating`, `failed`, `error`) сваливаются в одну ветку без индивидуальных подсказок, хотя ARCHITECTURE.md требует разные диагностики.
|
||||
|
||||
**Предложения.** Завести enum состояний и единую функцию `classifyStatus(raw) → State`, маппинг raw→enum в одном месте, exhaustive `switch` по всем 16 (с `default → явная ошибка «неизвестный статус X»`). Тесты на каждый статус.
|
||||
|
||||
**Риск.** Строковое сопоставление — самое уязвимое место к молчаливому дрейфу API.
|
||||
|
||||
---
|
||||
|
||||
## 8. Тесты — достаточно ли
|
||||
|
||||
**Состояние.** **8 unit-тестов всего**: crud_test.go (3: `isStatusSuspended`, `isStatusNonAdoptable`, delete-default) и client_test.go (5: нормализация значений). Без моков, без integration.
|
||||
|
||||
**Не покрыто (критично):** adopt-логика, ref-валидация, polling/`waitForOperationFinish`, `FindInstanceByDisplayName` (deleted+multi-match), генератор целиком, JSON plan-modifier, suspend/resume, required-params compare.
|
||||
|
||||
**Предложения.**
|
||||
- `httptest.Server` мок Nubes API → тесты Flow V6, поллинга по `dtFinish`, обрыва на шаге N, 429/503.
|
||||
- Табличные тесты adopt-матрицы (все 16 состояний × `adopt_existing_on_create` true/false).
|
||||
- Golden-тесты генератора (YAML→Go).
|
||||
- Контракт-тесты на основе HAR (см. п. ниже).
|
||||
|
||||
**Риск.** Все закрытые системные баги (deleted, polling, param-ID) **не защищены от регрессии**. Любой рефакторинг ядра/генератора — рулетка.
|
||||
|
||||
---
|
||||
|
||||
## 9. Безопасность
|
||||
|
||||
**Состояние.**
|
||||
- Токены: `*.token` в .gitignore ✅; private_key.asc в .gitignore ✅; id_ed25519.txt ✅.
|
||||
- `InsecureSkipVerify` default = **false** ✅, включается только явно/`NUBES_INSECURE=true`. TLS 1.2 min ✅.
|
||||
- `api_token` помечен `Sensitive: true` ✅.
|
||||
|
||||
**Хорошо.** Базовая гигиена соблюдена — ключи и токены не коммитятся, TLS-проверка по умолчанию включена.
|
||||
|
||||
**Плохо / проверить.**
|
||||
- **GPG-ключ физически лежит в secrets** — да, в .gitignore, но стоит проверить `git log --all -- secrets/private_key.asc`, что он не попал в историю ранее. .gitignore не вычищает уже закоммиченное.
|
||||
- public_key.asc, id_ed25519.pub — публичные, ок; но prod.token/`dev.token`/`test.token` существуют локально — убедиться, что покрыты `*.token` (да) и не было коммита до добавления правила.
|
||||
- **Утечка токена в логи**: `formatAPIError` форматирует тело ответа API в ошибку — если API эхает заголовки/токен в body ошибки, он попадёт в диагностику Terraform. `doRequest` сам токен не логирует. Стоит маскировать `Bearer ...` в любых сообщениях.
|
||||
- `ttyOut()` пишет напрямую в tty минуя Terraform — в debug-режиме `StageMsg` может содержать чувствительные данные; они идут в терминал в обход TF-логирования.
|
||||
|
||||
**Предложения.** `git log` аудит секретов; явная маскировка токена в `formatAPIError`/диагностиках; политика ротации `*.token`; вынести секреты из репо в внешний secret-store (для CI).
|
||||
|
||||
**Риск.** Если ключ/токен попал в git-историю до .gitignore — он уже скомпрометирован, .gitignore не поможет. Это надо проверить первым делом.
|
||||
|
||||
---
|
||||
|
||||
## 10. Конкурентность — два `terraform apply`
|
||||
|
||||
**Состояние.** Никаких блокировок. `FindInstanceByDisplayName` + `CreateGenericInstanceUniversalV6` — **не атомарны**. `waitForInstanceIdle` ждёт `operationIsPending/InProgress`, но это не защищает от гонки create.
|
||||
|
||||
**Плохо.**
|
||||
- Два apply с одинаковым `displayName` одновременно: оба проходят `FindInstanceByDisplayName` (никого нет) → оба `POST /instances` → **два инстанса с одним именем**. После этого `FindInstanceByDisplayName` начнёт возвращать ошибку «найдено 2 инстанса» (баг 22 только *детектирует* это, но не предотвращает).
|
||||
- Между modify из двух окружений — гонка на `instanceOperations`; частично гасится `waitForInstanceIdle`, но окно остаётся.
|
||||
|
||||
**Предложения.**
|
||||
- Полагаться на **Terraform state locking** (backend lock) как первичную защиту — это ответственность пользователя, задокументировать.
|
||||
- На стороне API — проверить, есть ли уникальность `displayName` на бэкенде; если нет, провайдер не может гарантировать атомарность.
|
||||
- Минимально: после `POST /instances` сразу повторный `FindInstanceByDisplayName` и, если найдено >1, откатить свой (требует delete — осторожно).
|
||||
|
||||
**Риск.** Полноценная защита возможна только при поддержке со стороны API (уникальность имени или conditional create). Провайдер в одиночку гонку не закрывает.
|
||||
|
||||
---
|
||||
|
||||
## Сводный план улучшений (по приоритету)
|
||||
|
||||
**Steps**
|
||||
|
||||
**P0 — Надёжность runtime (блокеры для прода)**
|
||||
1. Retry + backoff в `doRequest` для 429/503/сетевых сбоев (уважать `Retry-After`).
|
||||
2. Обработка orphan-инстанса `not_created`: авто-cleanup при явном флаге вместо «удалите вручную».
|
||||
3. Документировать и протестировать поведение при конкурентном apply (state-lock + повторная проверка после create).
|
||||
|
||||
**P1 — Защита от дрейфа и регрессий** (*parallel с P0*)
|
||||
4. `validateSpec()` в генераторе + **fail fast на неизвестном kind**; убрать fallback на неформатированный код.
|
||||
5. CI-шаг «генерация без diff» (детект дрейфа per ARCHITECTURE.md).
|
||||
6. Тесты: `httptest` мок API (Flow V6, polling, обрыв, 429), табличные тесты adopt-матрицы, golden-тесты генератора, regression на deleted-фильтрацию.
|
||||
|
||||
**P2 — Снижение тех-долга**
|
||||
7. Единый `classifyStatus → enum` для 16 состояний, exhaustive switch, убрать `strings.Contains`-классификацию.
|
||||
8. Рефакторинг `adoptExistingInstanceOnCreate` (после тестов): вынести ref-валидацию в helper, отдельные диагностики для `creating`/`failed`.
|
||||
9. Шаблоны генератора в `embed.FS`.
|
||||
10. План вывода Legacy: матрица паритета ресурсов, `DEPRECATED.md`, целевая версия снятия.
|
||||
|
||||
**P3 — Безопасность и наблюдаемость**
|
||||
11. `git log` аудит secrets; маскировка `Bearer`-токена в `formatAPIError`/диагностиках.
|
||||
12. Realm-валидация — включить, когда бэкенд починит `/resourceRealms/available`.
|
||||
13. HAR-based контракт-тесты (верификация соответствия провайдера реальному API).
|
||||
|
||||
**Relevant files**
|
||||
- client.go — `doRequest` (retry), `CreateGenericInstanceUniversalV6` (orphan/идемпотентность), `FindInstanceByDisplayName` (конкурентность).
|
||||
- crud.go — `adoptExistingInstanceOnCreate` (рефакторинг + тесты), статус-хелперы.
|
||||
- generate_resources_v2.go — валидация YAML, fail-fast на kind, вынос шаблонов.
|
||||
- provider.go — транспорт (keep-alive, маскировка токена).
|
||||
- Тесты: рядом с crud.go и client.go.
|
||||
|
||||
**Decisions / scope**
|
||||
- Это аналитический отчёт + план; **код не менялся** (режим Plan, и правила проекта запрещают изменения без «делай»).
|
||||
- Большинство критических багов из истории **уже закрыты** (5.0.50). Главный системный риск сейчас — **отсутствие тестов** (регрессии не ловятся) и **дрейф API** (генератор/статусы молча ломаются).
|
||||
|
||||
**Further Considerations**
|
||||
1. С чего начать реализацию? **A)** P0 (надёжность runtime) / **B)** P1 (тесты+защита от дрейфа — фундамент для безопасного рефакторинга) / **C)** аудит безопасности секретов (быстро, но критично, если ключ в истории git).
|
||||
2. Вывод Legacy — делать сейчас параллельно или после стабилизации Universal тестами? Рекомендую **после** (нужен паритет VM/vApp, который без тестов рискован).
|
||||
3. Нужен ли вам этот анализ как отдельный markdown-файл в docs (например `docs/50_history/25_opus_deep_analysis.md`), или достаточно ответа в чате?
|
||||
|
||||
Какой блок развернуть детальнее или с какого приоритета начинать?
|
||||
|
||||
Создано задач: 5
|
||||
|
||||
Список задач обновлен
|
||||
|
||||
## Общее мнение
|
||||
|
||||
**Архитектурно — сильное, зрелое решение.** Идея «YAML как source of truth → генерация Go-кода и доков» правильная и редко встречается в self-made провайдерах. Универсальное ядро без сервис-специфичной логики + декларативные спеки = можно добавить сервис без программиста. Это уровень выше типичного «руками написанного» провайдера.
|
||||
|
||||
**Что реально хорошо:**
|
||||
- Выстраданный контракт с API (`dtFinish` для конца операции, динамические param-ID, фильтрация deleted) — видно, что прошли через боль и закрыли системные баги.
|
||||
- Чистое разделение pipeline (API→YAML→Go→S3), профили dev/test/prod.
|
||||
- Базовая безопасность в порядке (токены/ключи в .gitignore, TLS-проверка по умолчанию).
|
||||
|
||||
**Главные слабости (по убыванию):**
|
||||
1. **Тесты почти отсутствуют** (8 unit-тестов, без моков, без integration). Все закрытые баги не защищены от регрессии — это самый большой риск.
|
||||
2. **Генератор молча ломается** при дрейфе API (неизвестный kind тихо игнорируется, YAML не валидируется) — при этом сам же ARCHITECTURE.md требует «fail fast on drift». Декларация ≠ реализация.
|
||||
3. **Нет транзакционности create и retry** — обрыв посередине оставляет orphan, transient-сбой рвёт apply.
|
||||
4. **Два провайдера с дублированным ядром** — тех-долг, баги фиксятся в двух местах.
|
||||
|
||||
**Вердикт:** концепция и инженерная зрелость ядра — выше среднего, видно опытную руку. Но проект держится на «героике» (ручное тестирование на реальных стендах, комментарии «НЕ МЕНЯТЬ»), а не на автоматических гарантиях. Один слой тестов + fail-fast в генераторе превратили бы его из «работает, потому что мы знаем где не трогать» в по-настоящему надёжный. Сейчас это **крепкий прототип на пути к продакшн-grade**, а не законченный продукт.
|
||||
|
||||
@@ -0,0 +1,352 @@
|
||||
# Ответы на дополнительные вопросы (сессия 2)
|
||||
|
||||
Read memory [](file:///home/naeel/.vscode-server/data/User/globalStorage/github.copilot-chat/memory-tool/memories/naeel/.vscode-server/data/User/workspaceStorage/358cf615179442f06eab7245b983b753/GitHub.copilot-chat/memory-tool/memories/MmM3MzY3ZGQtNmQ3Zi00OGI0LTg0ZDAtMjViYmIwNmY3Y2Zi/plan.md)
|
||||
|
||||
Created [](file:///home/naeel/tf_provider/HISTORY/OPUS/3006_1_questions.md)
|
||||
|
||||
Готово: 3006_1_questions.md
|
||||
|
||||
## 8 дополнительных вопросов
|
||||
|
||||
| # | Тема | Что нового (после его анализа) |
|
||||
|---|------|-------------------------------|
|
||||
| 1 | **Расхождение версий** | Регистр отдаёт 5.0.55, код — 5.0.51. Откуда 53-55? |
|
||||
| 2 | **Полный аудит isDeleted** | Все ли GET /instances фильтруют deleted? Особенно `findInstanceUidByDisplayNameRefSvc` |
|
||||
| 3 | **Gitea 413** | Где nginx-конфиг Gitea в репо? Можно ли увеличить лимит через чарты? |
|
||||
| 4 | **Suspend-сервисы** | Полный список кто поддерживает suspend/resume. Есть ли баги (suspend без resume)? |
|
||||
| 5 | **Terraform Operator** | Почему перезапустился 2 дня назад? Хватает ли компонентов? Где GPG/S3 конфиг? |
|
||||
| 6 | **Тестовые стенды** | Какие реально используются? Legacy или Universal? Дубликаты? |
|
||||
| 7 | **auth.k8s.ngcloud.ru** | OIDC-конфиг, обновление токенов, Keycloak HAR |
|
||||
| 8 | **Orphan PVC/STS** | Откуда `storage-check-*` PVC? PostgreSQL 0/0 28 дней — почему? |
|
||||
|
||||
Все вопросы — **только чтение файлов в репо**, никаких правок. Opus может ответить на большинство из них. Вопросы 3 и 8 частично требуют данных из кластера (не из репо) — он это честно отметит.
|
||||
|
||||
Внесены изменения.
|
||||
|
||||
Список задач обновлен
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/HISTORY/OPUS/3006_1_questions.md)
|
||||
|
||||
Исследую все 8 вопросов через чтение файлов репозитория. Запущу параллельный сбор данных.
|
||||
|
||||
Searched for regex `/instances\?|/instances"|GET.*instances|page=|isDeleted` (`**/universal_rebuild/internal/core/*.go`), 20 results
|
||||
|
||||
Searched for regex `findInstanceUidByDisplayNameRefSvc|ListRefServiceInstances`, 20 results
|
||||
|
||||
Searched for files matching `**/devops/profiles/**/profile.env`, 3 matches
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/universal_rebuild/internal/core/refsvc_resolve.go)
|
||||
|
||||
Нашёл ключевое для вопроса 2 — прочитаю функцию `findInstanceUidByDisplayNameRefSvc` и файлы версий.
|
||||
|
||||
Searched for regex `VERSION|version|5\.0\.` (`**/devops/03_build_and_upload_provider.sh`), 13 results
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/universal_rebuild/internal/core/client.go#900-900), lines 900 to 1010
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/devops/profiles/prod/profile.env)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/devops/profiles/test/profile.env)
|
||||
|
||||
Вопросы 1 и 2 почти закрыты. Соберу данные по остальным: инфраструктура, suspend-аудит, стенды, auth, orphan-ресурсы.
|
||||
|
||||
Searched for regex `auth\.k8s\.ngcloud\.ru|keycloak|shturval|client_max_body_size|gitea|storage-check`, 20 results
|
||||
|
||||
Searched for regex `getInstanceDisplayNameByUidRefSvc|findInstanceDisplayNameByUidRefSvc` (`**/universal_rebuild/internal/core/*.go`), 6 results
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/charts)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/cloud-dashboard/k8s)
|
||||
|
||||
Searched for regex `storage-check|terraform-operator|registry-server|operator`, 20 results
|
||||
|
||||
Searched for files matching `**/{operator,registry-server,registry-server-build,k8s}/**`, 3 matches
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/universal_rebuild/internal/core/client.go#1057-1057), lines 1057 to 1140
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/cloud-dashboard/k8s/ingress.yaml)
|
||||
|
||||
Аудит suspend/resume сервисов
|
||||
|
||||
Аудит тестовых стендов
|
||||
|
||||
Read [](file:///home/naeel/tf_provider)
|
||||
|
||||
Searched for regex `kubeconfig|OIDC|oidc|kube-apiserver|kubectl|HOW_TO_SSH|auth\.k8s`, 20 results
|
||||
|
||||
Searched for files matching `**/TEST_STAND/**/*.tf`, 11 matches
|
||||
|
||||
Searched for files matching `**/PROD_STAND/**/*.tf`, 10 matches
|
||||
|
||||
Searched for files matching `**/RABBIT/**/*.tf`, 6 matches
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/TEST_STAND)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/PROD_STAND)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/RABBIT)
|
||||
|
||||
Searched for files matching `tf_provider` · `universal_rebuild/resources_yaml/**/*.yaml`, no matches
|
||||
|
||||
Searched for files matching `tf_provider` · `devops/profiles/prod/generated/resources_yaml/**/*.yaml`, no matches
|
||||
|
||||
Searched for files matching `tf_provider` · `devops/profiles/test/generated/**/*.yaml`, no matches
|
||||
|
||||
Searched for files matching `tf_provider` · `universal_rebuild/resources_yaml/embed.go`, 1 match
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/universal_rebuild/resources_yaml)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/devops/profiles/prod/generated)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/devops/profiles/prod/generated/resources_yaml)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/devops/profiles/test/generated)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/universal_rebuild/resources_yaml/embed.go)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/devops/profiles/test/generated/resources_yaml)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/TEST_STAND/LUCEE/resources.tf#1-1), lines 1 to 50
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/TEST_STAND/MARIA_DB/resources.tf#1-1), lines 1 to 50
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/TEST_STAND/POSTGRES/main.tf)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/TEST_STAND/POSTGRES/resources.tf)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/PROD_STAND/PG1/resources.tf#1-1), lines 1 to 100
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/PROD_STAND/POSTGRES/resources.tf)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/PROD_STAND/RABBIT/main.tf)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/PROD_STAND/RABBIT/resources.tf#1-1), lines 1 to 100
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/RABBIT/main.tf)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/RABBIT/resources.tf)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/TEST_STAND/LUCEE/main.tf)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/TEST_STAND/MARIA_DB/main.tf)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/TEST_STAND/S3_EVENT_FUNCTION_POC/versions.tf)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/PROD_STAND/PG1/main.tf)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/PROD_STAND/POSTGRES/main.tf)
|
||||
|
||||
Searched for regex `suspend|resume` (`tf_provider` · `devops/profiles/prod/generated/resources_yaml/**/*.yaml`), 20 results
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/TEST_STAND/S3_EVENT_FUNCTION_POC/main.tf#1-1), lines 1 to 50
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/TEST_STAND/POSTGRES/luceUNDnode.tf#1-1), lines 1 to 60
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/PROD_STAND/POSTGRES/flask_disabled.tf)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/PROD_STAND/PG1/vm.tf)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/PROD_STAND/RABBIT/rabbit_worker_disabled.tf)
|
||||
|
||||
Searched for regex `suspend|resume` (`tf_provider` · `devops/profiles/prod/generated/resources_yaml/**/*.yaml`), 187 results
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/devops/profiles/prod/generated/resources_yaml/90_postgres.yaml#1-1), lines 1 to 20
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/devops/profiles/prod/generated/resources_yaml/90_postgres.yaml#459-459), lines 459 to 469
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/devops/profiles/prod/generated/resources_yaml/115_mariadb.yaml#1-1), lines 1 to 20
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/devops/profiles/prod/generated/resources_yaml/115_mariadb.yaml#238-238), lines 238 to 248
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/devops/profiles/prod/generated/resources_yaml/91_redis.yaml#1-1), lines 1 to 20
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/devops/profiles/prod/generated/resources_yaml/117_nifi.yaml#1-1), lines 1 to 20
|
||||
|
||||
Searched for regex `- name:|kind:|action:` (`tf_provider` · `devops/profiles/prod/generated/resources_yaml/91_redis.yaml`), 6 results
|
||||
|
||||
Searched for regex `- name:|kind:|action:` (`tf_provider` · `devops/profiles/prod/generated/resources_yaml/117_nifi.yaml`), 9 results
|
||||
|
||||
Searched for regex `- name:|kind:|action:` (`tf_provider` · `devops/profiles/prod/generated/resources_yaml/112_tenant.yaml`), 9 results
|
||||
|
||||
Searched for regex `- name:|action:` (`tf_provider` · `devops/profiles/prod/generated/resources_yaml/22_vc_nsxt.yaml`), 8 results
|
||||
|
||||
Searched for regex `- name:|action:` (`tf_provider` · `devops/profiles/prod/generated/resources_yaml/29_vc_vdc_group.yaml`), 10 results
|
||||
|
||||
Searched for regex `- name:|action:` (`tf_provider` · `devops/profiles/prod/generated/resources_yaml/100_openwhisk.yaml`), 4 results
|
||||
|
||||
Проверю наличие `operator/`/`k8s/`, прочитаю отчёт по gitea/registry и README про реестр.
|
||||
|
||||
Searched for files matching `**/operator/**`, no matches
|
||||
|
||||
Searched for files matching `**/k8s/**`, 3 matches
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/README.md#40-40), lines 40 to 127
|
||||
|
||||
Read [](file:///home/naeel/.vscode-server/data/User/workspaceStorage/358cf615179442f06eab7245b983b753/GitHub.copilot-chat/chat-session-resources/2c7367dd-6d7f-48b4-84d0-25bbb06f7cfb/toolu_01RGa2BMVk2W3EiaCRQTaSMg__vscode-1782817912438/content.txt)
|
||||
|
||||
Финальная проверка: наличие `registry-server-build/`, детали оператора/S3/GPG в кластере, keycloak HAR.
|
||||
|
||||
Searched for files matching `**/registry-server-build/**`, no matches
|
||||
|
||||
Searched for files matching `**/HAR/*keycloak*`, no matches
|
||||
|
||||
Searched for regex `S3_ENDPOINT|s3.msk-1|terraform-registry|S3_BUCKET|\.s3cfg`, 20 results
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/docs/50_history/00_system_mechanics.md)
|
||||
|
||||
Все 8 вопросов исследованы. Ниже ответы в формате **находка → файлы → вывод → рекомендация**.
|
||||
|
||||
---
|
||||
|
||||
## Вопрос 1: Откуда версии 5.0.53–5.0.55?
|
||||
|
||||
**Находка.** Версия билда берётся **не из main.go**, а из профиля стенда:
|
||||
- 03_build_and_upload_provider.sh: `VERSION = $1 → иначе $PROVIDER_VERSION/$RELEASE_VERSION (из profile.env) → иначе grep из main.go (fallback)`.
|
||||
- profile.env: `PROVIDER_VERSION="5.0.55"`.
|
||||
- profile.env: `PROVIDER_VERSION="2.1.23"` (!).
|
||||
- main.go хранит `5.0.51` — это лишь **последний fallback**, который почти никогда не срабатывает.
|
||||
|
||||
**Вывод.** 5.0.53/54/55 — это **ручные билды с тестового стенда**: оператор вручную bump-ит `PROVIDER_VERSION` в `profiles/test/profile.env` и запускает `03_build...sh`. main.go при этом не обновляется (README прямо просит обновлять, но на практике это не делается — отсюда рассинхрон). Важно: **prod-профиль застрял на 2.1.23**, а test ушёл на 5.0.55 — это две независимые линейки версий. Мой прошлый анализ (3006_0.md) цитировал версии из кода (5.0.51/5.0.52), а регистр отдаёт версию из profile.env (5.0.55) — поэтому они не совпадают.
|
||||
|
||||
**Рекомендация.** Сделать profile.env единственным источником версии и автоматически прописывать её в main.go при сборке (или вычислять main.go-версию из env через ldflags `-X`). Сейчас 3 источника версии (main.go, test, prod) живут врозь.
|
||||
|
||||
---
|
||||
|
||||
## Вопрос 2: Полный аудит фильтрации `isDeleted`
|
||||
|
||||
**Находка.** Все функции, делающие GET к `/instances`:
|
||||
|
||||
| Функция | Файл | `isDeleted=false` в URL | Проверка в коде | Статус |
|
||||
|---|---|---|---|---|
|
||||
| `FindInstanceByDisplayName` | client.go | ✅ (search) / ❌ (fallback) | ✅ `isInstanceDeleted()` | OK |
|
||||
| `ListRefServiceInstances` | refsvc_resolve.go | ✅ | ✅ `IsDeleted` + только running | OK |
|
||||
| `findInstanceUidByDisplayNameRefSvc` | client.go | ❌ | ✅ `if item.IsDeleted {continue}` + `status=="deleted"` | **OK (исправлен!)** |
|
||||
| `findInstanceDisplayNameByUidRefSvc` | client.go | ❌ | ❌ нет | приемлемо |
|
||||
| `getInstanceDisplayNameByUidRefSvc` | client.go | — (GET по UID) | — | n/a |
|
||||
| `GetInstanceState/Raw/Details` | client.go / instance_outputs.go | — (GET по UID) | ✅/частично | n/a |
|
||||
|
||||
**Вывод.**
|
||||
- **`findInstanceUidByDisplayNameRefSvc` УЖЕ исправлен** — он пропускает deleted в коде (строки client.go) и предпочитает running>suspended. Замечание в 24_ai_analysis_pipeline_architecture_2026_06_30.md («НЕ ИСПРАВЛЕН?») **устарело** — баг класса #23 здесь закрыт. Единственный недочёт — нет `isDeleted=false` в URL (лишний трафик, но не баг корректности).
|
||||
- **`findInstanceDisplayNameByUidRefSvc` (1058)** — единственная функция БЕЗ фильтра deleted ни в URL, ни в коде. Но она ищет по **точному `instanceUid`** (уникальному) и возвращает displayName — это обратный маппинг для чтения state, не выбор «того/не того» инстанса. Класс багов #23 здесь не применим. Риск минимальный: вернёт имя deleted-инстанса, если в state остался его UID.
|
||||
|
||||
**Рекомендация.** Косметика: добавить `&isDeleted=false` в URL `findInstanceUidByDisplayNameRefSvc` (1057) и `findInstanceDisplayNameByUidRefSvc` (1060) для экономии трафика. Корректность уже обеспечена. Обновить вывод в файле 24 (он сеет ложную тревогу).
|
||||
|
||||
---
|
||||
|
||||
## Вопрос 3: Gitea 413 (`client_max_body_size`)
|
||||
|
||||
**Находка.**
|
||||
- charts — **пустая** (list_dir: folder empty).
|
||||
- Манифесты в репо есть только для dashboard: ingress.yaml — ingress для `terra.k8c.ru/dashboard`, аннотаций `client_max_body_size`/`proxy-body-size` нет, и это **не Gitea**.
|
||||
- `gitea-naeel.giteak8s.services.ngcloud.ru` упоминается только как `git_path` в resources.tf и закомментированно в luceUNDnode.tf. Сам Gitea — это **управляемый сервис Nubes** (см. svcs.json: «Gitea», «Комплексная услуга по созданию gitea», service_id 99/114), развёрнутый в кластере `giteak8s`, а не из этого репо.
|
||||
|
||||
**Вывод.** **Конфигурация nginx Gitea в этом репозитории отсутствует.** `client_max_body_size=1MB` задаётся на ingress управляемого Gitea в кластере `giteak8s.services.ngcloud.ru` — **внешняя конфигурация**, недоступная для правки из этого репо.
|
||||
|
||||
**Рекомендация.** Лимит правится за пределами репо — на ingress Gitea-инстанса: аннотация `nginx.ingress.kubernetes.io/proxy-body-size: "0"` (или, например, `512m`). Это требует доступа к namespace Gitea в кластере giteak8s. Из репо проблему не решить. *(Требует данных кластера — отмечаю честно.)*
|
||||
|
||||
---
|
||||
|
||||
## Вопрос 4: Полный список suspend/resume-сервисов
|
||||
|
||||
**Находка.** YAML-спеки в devops/profiles/prod/generated/resources_yaml/ (43 файла, test идентичен), встроены через `//go:embed *` в embed.go.
|
||||
|
||||
**26 сервисов с suspend И resume** (service_id): `1 dummy, 2 template, 12 s3, 19 vc_org, 20 vc_org_saas, 21 vc_vdc, 23 vc_vm, 26 vapp, 27 vc_vm_v2, 28 vc_vm_v3, 50 nextcloud, 89 flask, 90 postgres, 92 mongodb, 93 rabbitmq, 94 lucee, 95 nodejs, 96 pgadmin, 98 http, 99 gitea, 115 mariadb, 116 kafka, 119 akhq, 120 clickhouse, 149 valo_tenant, 150 k8s_shturval`.
|
||||
|
||||
**17 сервисов без suspend/resume**: `13 s3bucket, 22 vc_nsxt, 25 vcexternalip, 29 vc_vdc_group, 32 vmpostgre, 81 superset, 82 harbor, 88 ziti, 91 redis, 97 nodered, 100 openwhisk, 110 dnszone, 111 dnsrecord, 112 tenant, 113 vc_complex, 114 gitea_complex, 117 nifi`.
|
||||
|
||||
**Вывод.**
|
||||
- **Асимметрии нет**: suspend и resume всегда идут парой (26/26). Сервисов «suspend без resume» — **0**.
|
||||
- `suspend_on_destroy_default` **идеально совпадает** с правилом `hasSuspend → true`: 26 suspend-сервисов = `true`, 17 = `false`. Расхождений нет.
|
||||
- Логика разделения здравая: stateless (DNS, external IP, tenant) — без suspend; stateful (БД, приложения, VM) — с suspend.
|
||||
|
||||
**Рекомендация.** По этому пункту всё чисто. Стоит лишь добавить в генератор `validateSpec()`-проверку «если есть suspend, обязан быть resume» как защиту на будущее (сейчас инвариант соблюдён случайно — генератор его не enforce-ит).
|
||||
|
||||
---
|
||||
|
||||
## Вопрос 5: Terraform Operator — состояние и health
|
||||
|
||||
**Находка — ключевая.** Директорий `operator/`, `registry-server-build/`, корневого `k8s/` **НЕТ в этом checkout** (file_search: «No files found»), хотя они активно упоминаются в README.md, REPO_CONTENTS.md, build-registry-image.yml (`operator/build/Dockerfile.registry`) и deploy-dev.sh (`k8s/overlays/dev`).
|
||||
|
||||
Что есть в репо — описание в 00_system_mechanics.md:
|
||||
- 3 компонента: **Operator** (watch CRD → spawn Job), **Registry API** (Discovery protocol over S3), **Builder Job** (`golang:1.24-alpine`: clone→build→upload S3). Это совпадает с тремя deployment'ами, что вы видели.
|
||||
- **S3**: бакет артефактов `terraform-providers`, схема пути `{hostname}/{namespace}/{provider}/{version}/{file}`, `REGISTRY_HOSTNAME=terra.k8c.ru`. Эндпойнт `s3.msk-1.ngcloud.ru` (upload_provider_s3.py, REPO_CONTENTS.md).
|
||||
- **GPG в кластере — ФЕЙКОВЫЙ**: 00_system_mechanics.md — «GPG Signing: Сейчас фейковое (создаётся пустой `.sig`)». Реальная подпись private_key.asc используется только в **локальном** `03_build...sh`, а не в operator-Job.
|
||||
- **Перезапуск оператора — штатная операция**: в cheat-sheet прямо есть `kubectl rollout restart deploy/terraform-operator -n terra`.
|
||||
|
||||
**Выводы по вопросам:**
|
||||
1. **Перезапуск 2 дня назад** — скорее всего ручной `rollout restart` (документированная команда подхвата изменений `manifests/03-build-script.yaml`), а не краш. Подтвердить можно только по `kubectl describe pod`/`--previous` логам в кластере. *(Требует кластера.)*
|
||||
2. **Трёх deployment'ов достаточно** для registry+CI (operator+registry-server+builder-job). Отдельного docs-server нет и не нужен: доки — статика в S3 (`terraform-registry/docs/...`), publish-docs.sh. cloud-dashboard — отдельный UI, не часть registry.
|
||||
3. **GPG-ключи**: в кластере подпись фейковая (tech debt из system_mechanics); реальный ключ только локально в secrets. **S3-доступ** оператора — через ENV `S3_*`/`REGISTRY_HOSTNAME` в Deployment (должны быть в Secret `terraform-operator`).
|
||||
4. **S3-конфиг бакета в репо** — только переменные и схема путей; манифест Secret/Deployment отсутствует (он в недостающей папке `operator/`/`k8s/`).
|
||||
|
||||
**Рекомендация.** Критично: **внедрить реальную GPG-подпись в operator-Job** (смонтировать private_key.asc как K8s Secret) — сейчас артефакты из кластера подписаны пустышкой, Terraform может ругаться `authentication signature from unknown issuer`. Также — вернуть `operator/`+`k8s/` в этот репо или явно задокументировать, что они в отдельном репозитории (сейчас CI ссылается на отсутствующие пути).
|
||||
|
||||
---
|
||||
|
||||
## Вопрос 6: Тестовые стенды — что реально используется
|
||||
|
||||
**Находка.** Все `.tf` используют **только Universal** (`source = "terra.k8c.ru/nubes/nubes"`), Legacy (`registry.terraform.io/nubes/nubes`) **не используется нигде**.
|
||||
|
||||
| Стенд | Version | Endpoint | Активные ресурсы |
|
||||
|---|---|---|---|
|
||||
| TEST_STAND/LUCEE | 2.0.6 | test | postgres, lucee |
|
||||
| TEST_STAND/MARIA_DB | 2.0.8 | test | mariadb, lucee, flask |
|
||||
| TEST_STAND/POSTGRES | **5.0.52** | test | postgres + user + database |
|
||||
| TEST_STAND/S3_EVENT_POC | 2.1.23 | — | s3bucket ×2 |
|
||||
| PROD_STAND/PG1 | 2.1.26 | prod | postgres, lucee, nodejs, s3bucket |
|
||||
| PROD_STAND/POSTGRES | 2.1.12 | prod | postgres, lucee, nodejs |
|
||||
| PROD_STAND/RABBIT | 5.0.19 | **test ⚠️** | rabbitmq |
|
||||
| RABBIT/ (корень) | 2.1.10 | prod | rabbitmq, lucee, nodejs, s3bucket |
|
||||
|
||||
**Выводы:**
|
||||
1. Все стенды активны (с реальными ресурсами); часть компонентов закомментирована (VM в PG1, flask в PROD/POSTGRES, http-worker в RABBIT).
|
||||
2. **Сильный разброс версий**: test — 2.0.6…5.0.52, prod — 2.1.10…2.1.26. Каждый стенд пинит свою версию.
|
||||
3. **TEST/POSTGRES не дублирует PROD/POSTGRES**: разные версии (5.0.52 vs 2.1.12), имена (`pg4tf033` vs `pg-tst0`), TEST модульный (только БД+user+database с `var.realm`), PROD связный (БД+lucee+nodejs, хардкод realm). Это разные конфигурации, не дубль.
|
||||
4. **Аномалия**: PROD_STAND/RABBIT/main.tf указывает на **test-endpoint** (`deck-api-test.ngcloud.ru`), хотя лежит в PROD_STAND — вероятно ошибка/недо-миграция.
|
||||
|
||||
**Рекомендация.** Привести версии стендов к двум канонам (одна test, одна prod), исправить endpoint в RABBIT, удалить мёртвые закомментированные `.tf` или вынести в `examples/`.
|
||||
|
||||
---
|
||||
|
||||
## Вопрос 7: auth.k8s.ngcloud.ru / Keycloak / shturval
|
||||
|
||||
**Находка.** Поиск `auth.k8s.ngcloud.ru`, `shturval`, `OIDC`, `kubeconfig` по репо: совпадения **только в самом файле вопросов** 3006_1_questions.md. В коде/конфигах — ничего.
|
||||
- HAR `keycloak.nubes.ru.har` существует локально, но **gitignored** (`HAR/*.har` в .gitignore) — и это Keycloak **`keycloak.nubes.ru`** (для управляемых сервисов Nubes), а **не** `auth.k8s.ngcloud.ru`.
|
||||
- Инструкции по доступу — только HOW_TO_SSH.md (SSH, не kubeconfig/OIDC).
|
||||
- `kubectl`-команды в docs (deploy-dev.sh, ops/*.md) предполагают **готовый** kubeconfig, способ его получения не описан.
|
||||
|
||||
**Вывод.** **Конфигурации OIDC кластера, скриптов обновления токена и инструкций по kubeconfig в репо НЕТ.** `auth.k8s.ngcloud.ru` (realm `shturval`) — внешняя система аутентификации кластера, никак не отражённая в репозитории. HAR относится к другому Keycloak (сервисный, `keycloak.nubes.ru`).
|
||||
|
||||
**Рекомендация.** Если доступ к кластеру через `auth.k8s.ngcloud.ru` нужен регулярно — добавить в secrets (gitignored) инструкцию/скрипт `kubelogin`/OIDC token refresh, по аналогии с `HOW_TO_SSH.md`. Сейчас это «племенное знание» вне репо. *(Детали OIDC-эндпойнта — за пределами репо.)*
|
||||
|
||||
---
|
||||
|
||||
## Вопрос 8: Orphan PVC `storage-check-*` и PostgreSQL 0/0
|
||||
|
||||
**Находка.** Поиск `storage-check` по всему репо: совпадения **только в файле вопросов**. Ни одного `.tf`, манифеста или упоминания в docs/HISTORY, создающего `storage-check-local`/`storage-check-vcd`. Никаких упоминаний проблемных PostgreSQL-тенантов `509145c3`/`dc5db45d`.
|
||||
|
||||
**Вывод.**
|
||||
- `storage-check-*` PVC **не создаются Terraform-конфигами из этого репо**. Имена (`storage-check-local`/`storage-check-vcd` = local/vCD storage classes) характерны для **тестов доступности StorageClass** — это типичные probe-PVC, которые создаёт **облачная платформа/CSI или сам terraform-operator-Job** при проверке хранилища, а не пользовательский TF. Подтвердить, что их делает именно operator, по репо нельзя (код оператора отсутствует в checkout, см. Q5).
|
||||
- PostgreSQL `0/0 28 дней` с висящими PVC/Service — это **suspended/недо-удалённые инстансы**: StatefulSet масштабирован в 0 (suspend), но PVC и Service остаются (платформа не чистит при suspend). Это согласуется с семантикой `suspend_on_destroy` (Q4) и багом 20_destroy_detach_semantics: `state_only`/suspend оставляет ресурсы в облаке.
|
||||
|
||||
**Рекомендация.** Источник `storage-check-*` и orphan-StatefulSet'ов нужно искать **в кластере**: `kubectl get pvc storage-check-local -o yaml` → смотреть `ownerReferences`/`annotations` (кто создатель). Если owner — `terraform-operator` SA или CSI healthcheck, чистить через него. Из репозитория эти ресурсы не управляются. *(Требует доступа к кластеру.)*
|
||||
|
||||
---
|
||||
|
||||
## Сводка
|
||||
|
||||
| # | Вопрос | Ответ из репо? | Главный вывод |
|
||||
|---|---|---|---|
|
||||
| 1 | Версии 53-55 | ✅ Полностью | Из `profiles/*/profile.env`, ручной bump; main.go — fallback. prod=2.1.23, test=5.0.55 |
|
||||
| 2 | Аудит isDeleted | ✅ Полностью | Все критичные функции фильтруют; `findInstanceUidByDisplayNameRefSvc` **уже исправлен** (файл 24 врёт) |
|
||||
| 3 | Gitea 413 | ⚠️ Частично | Конфига nginx Gitea в репо нет — внешняя config кластера giteak8s |
|
||||
| 4 | Suspend-сервисы | ✅ Полностью | 26 suspend+resume, 0 асимметрий, default совпадает идеально |
|
||||
| 5 | Operator health | ⚠️ Частично | `operator/`+`k8s/` отсутствуют в checkout; GPG в кластере фейковый; рестарт = штатный redeploy |
|
||||
| 6 | Стенды | ✅ Полностью | Везде Universal, Legacy не используется; разброс версий; RABBIT prod→test endpoint |
|
||||
| 7 | auth.k8s | ❌ Нет в репо | Ни OIDC, ни kubeconfig-скриптов; HAR — другой Keycloak (nubes.ru) |
|
||||
| 8 | Orphan PVC | ❌ Нет в репо | `storage-check-*` не из TF; orphan PG = suspended-остатки; искать в кластере |
|
||||
|
||||
**Требуют данных вне репо** (честно): Q3 (ingress Gitea в giteak8s), Q5 (логи рестарта оператора, Secret S3/GPG), Q7 (OIDC-эндпойнт auth.k8s), Q8 (`ownerReferences` PVC в кластере).
|
||||
|
||||
Сохранить эти ответы в `HISTORY/OPUS/3006_1.md` или оставить в чате?
|
||||
|
||||
Создано задач: 6
|
||||
@@ -0,0 +1,802 @@
|
||||
Что сказать Opus — кратко
|
||||
Задача: глубокий анализ проекта, найти слабые места, предложить улучшения.
|
||||
|
||||
10 вопросов:
|
||||
Два провайдера (Legacy + Universal) — как и когда выпилить Legacy?
|
||||
Генерационный конвейер — API→YAML→Go→S3. Надёжен ли? Что если API поменяет формат?
|
||||
API Flow V6 — 6 шагов без транзакционности. Что при обрыве посредине?
|
||||
CRUD/Adopt — сложнейшая логика в adoptExistingInstanceOnCreate. Все ли краевые кейзы покрыты?
|
||||
Генератор gen_v2 — шаблоны вшиты в бинарь. Как расширять? Что с новыми kind'ами операций?
|
||||
Баги — 23 файла истории. Какие системные? Фильтрация deleted — главная проблема.
|
||||
Матрица состояний — 16 состояний, все ли обрабатываются?
|
||||
Тесты — crud_test.go, client_test.go. Достаточно ли?
|
||||
Безопасность — токены, GPG-ключ в репозитории, InsecureSkipVerify.
|
||||
Конкурентность — два terraform apply одновременно?
|
||||
Что читать (приоритет):
|
||||
ARCHITECTURE.md
|
||||
client.go
|
||||
crud.go
|
||||
generate_resources_v2.go
|
||||
docs/50_history/23_*.md (последние баги)
|
||||
Полный промпт: prompt_for_opus48.md
|
||||
|
||||
Список задач обновлен
|
||||
|
||||
Изучу проект. Начну с чтения ключевых файлов и параллельного исследования.
|
||||
|
||||
Searched for files matching `**/ARCHITECTURE.md`, 1 match
|
||||
|
||||
Searched for files matching `**/internal/**/*.go`, 20 matches
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/prompt_for_opus48.md)
|
||||
|
||||
Прочитаю ключевые файлы и запущу параллельное исследование по разным областям.
|
||||
|
||||
Searched for files matching `**/universal_rebuild/internal/core/client.go`, 1 match
|
||||
|
||||
Searched for files matching `**/universal_rebuild/internal/resources_core/crud.go`, 1 match
|
||||
|
||||
Searched for files matching `**/universal_rebuild/tools/gen_v2/generate_resources_v2.go`, 1 match
|
||||
|
||||
Searched for files matching `**/docs/50_history/*.md`, 20 matches
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/devops/ARCHITECTURE.md)
|
||||
|
||||
Прочитаю основные файлы ядра и генератора, параллельно запущу исследование истории багов.
|
||||
|
||||
История багов проекта
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/universal_rebuild/internal/core/client.go#1-1), lines 1 to 450
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/docs/50_history)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/docs/50_history/24_ai_analysis_pipeline_architecture_2026_06_30.md#1-1), lines 1 to 200
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/docs/50_history/23_vapp_uid_inconsistency_displayname_resolve_bug.md)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/docs/50_history/22_adopt_ref_validation_duplicate_detection_5_0_50.md)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/docs/50_history/21_plan_validation_ref_svc_filter_5_0_38.md)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/docs/50_history/20_destroy_detach_semantics_5_0_8.md)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/docs/50_history/16_universal_provider_create_only_params.md)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/docs/50_history/13_universal_flow_param_normalization.md)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/docs/50_history/06_postgres_update_and_immutable_params.md)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/docs/50_history/05_postgres_implementation_and_polling_fixes.md)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/docs/50_history/00_system_mechanics.md)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/docs/50_history/01_initial_operator_setup.md#1-1), lines 1 to 100
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/docs/50_history/02_registry_protocol_fix.md)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/docs/50_history/03_domain_migration_and_hotfix.md#1-1), lines 1 to 80
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/docs/50_history/04_vm_hang_fix_and_500_error.md)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/docs/50_history/07_operation_based_polling_and_negative_testing.md)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/docs/50_history/08_natural_language_infrastructure_ai.md#1-1), lines 1 to 80
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/docs/50_history/09_s3_migration_and_branded_docs.md)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/docs/50_history/10_postgres_modify_tests_and_external_ip.md)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/docs/50_history/12_vm_resource_hardening.md#1-1), lines 1 to 80
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/docs/50_history/11_postgres_import_implementation.md)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/docs/50_history/15_universal_provider_lifecycle_tests.md)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/docs/50_history/17_subresource_generation_rebuild_and_5_0_1_publish.md)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/docs/50_history/18_docs_publish_pipeline_hardening_5_0_4.md)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/docs/50_history/14_universal_provider_separate.md)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/docs/50_history/19_subresource_update_id_fix_and_5_0_7.md)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/docs/50_history/02_tubulus_stabilization.md)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/universal_rebuild/internal/core/client.go#450-450), lines 450 to 900
|
||||
|
||||
Read [](file:///home/naeel/.vscode-server/data/User/workspaceStorage/358cf615179442f06eab7245b983b753/GitHub.copilot-chat/chat-session-resources/2c7367dd-6d7f-48b4-84d0-25bbb06f7cfb/toolu_01Gk8LXuMrG8gDUMRXKhjs2J__vscode-1782817912242/content.txt)
|
||||
|
||||
Прочитаю crud.go (включая `adoptExistingInstanceOnCreate`) и начало генератора, а также тесты.
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/universal_rebuild/internal/resources_core/crud.go)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/universal_rebuild/internal/provider/provider.go#1-1), lines 1 to 120
|
||||
|
||||
Изучу генератор, тесты, транспорт и состояние secrets в gitignore.
|
||||
|
||||
Генератор gen_v2 и тесты
|
||||
|
||||
Searched for files matching `**/.gitignore`, 6 matches
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/universal_rebuild/internal/provider/provider.go#120-120), lines 120 to 178
|
||||
|
||||
Searched for files matching `tf_provider` · `**/*_test.go`, 2 matches
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/universal_rebuild/tools/gen_v2/generate_resources_v2.go#1-1), lines 1 to 100
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/universal_rebuild/internal/resources_core/crud_test.go#1-1), lines 1 to 50
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/universal_rebuild/internal/core/client_test.go)
|
||||
|
||||
Searched for regex `func \(.*\) main\(\)|func.*Generate` (`tf_provider` · `universal_rebuild/tools/gen_v2/generate_resources_v2.go`), no results
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/universal_rebuild/tools/gen_v2/generate_resources_v2.go#100-100), lines 100 to 300
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/universal_rebuild/internal/resources_core/crud_test.go#50-50), lines 50 to 57
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/universal_rebuild/internal/core/client_test.go#46-46), lines 46 to 46
|
||||
|
||||
Searched for text `template.Must|var.*=|const.*=` (`tf_provider` · `universal_rebuild/tools/gen_v2/generate_resources_v2.go`), 8 results
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/universal_rebuild/tools/gen_v2)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/universal_rebuild/tools/gen_v2/generate_resources_v2.go#300-300), lines 300 to 500
|
||||
|
||||
Searched for regex `func write|func.*Kind|if.*Kind|switch.*op\.Kind` (`tf_provider` · `universal_rebuild/tools/gen_v2/generate_resources_v2.go`), 7 results
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/universal_rebuild/tools/gen_v2/generate_resources_v2.go#500-500), lines 500 to 700
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/universal_rebuild/tools/gen_v2/generate_resources_v2.go#800-800), lines 800 to 900
|
||||
|
||||
Searched for regex `panic|error|yaml\.Unmarshal|unknown|default|fallback` (`tf_provider` · `universal_rebuild/tools/gen_v2/generate_resources_v2.go`), 20 results
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/universal_rebuild/tools/gen_v2/generate_resources_v2.go#850-850), lines 850 to 1200
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/universal_rebuild/internal/resources_core/crud_test.go)
|
||||
|
||||
Searched for regex `` ^const.*Template = |^const.*= ` `` (`tf_provider` · `universal_rebuild/tools/gen_v2/generate_resources_v2.go`), 3 results
|
||||
|
||||
Searched for regex `^func Test` (`tf_provider` · `universal_rebuild/internal/resources_core/crud_test.go`), 3 results
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/universal_rebuild/internal/core/client_test.go)
|
||||
|
||||
Searched for regex ``^`$`` (`tf_provider` · `universal_rebuild/tools/gen_v2/generate_resources_v2.go`), 3 results
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/universal_rebuild/tools/gen_v2/generate_resources_v2.go#1420-1420), lines 1420 to 1430
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/universal_rebuild/tools/gen_v2/generate_resources_v2.go#1865-1865), lines 1865 to 1880
|
||||
|
||||
Searched for regex `.*` (`tf_provider` · `universal_rebuild/internal/resources_core/crud_test.go`), 56 results
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/universal_rebuild/tools/gen_v2/generate_resources_v2.go#210-210), lines 210 to 350
|
||||
|
||||
Searched for regex `buildSubresourceForceNewCodes|computeCreateOnly|analyzeJsonPlanMod` (`tf_provider` · `universal_rebuild/tools/gen_v2/generate_resources_v2.go`), 10 results
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/universal_rebuild/internal/core/client_test.go)
|
||||
|
||||
Searched for regex `func.*IsJson|func.*RefSvc|func.*computeCreateOnly|func.*analyzeJsonPlanMod` (`tf_provider` · `universal_rebuild/tools/gen_v2/generate_resources_v2.go`), 3 results
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/universal_rebuild/tools/gen_v2/generate_resources_v2.go#2040-2040), lines 2040 to 2150
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/.gitignore)
|
||||
|
||||
Read [](file:///home/naeel/.vscode-server/data/User/workspaceStorage/358cf615179442f06eab7245b983b753/GitHub.copilot-chat/chat-session-resources/2c7367dd-6d7f-48b4-84d0-25bbb06f7cfb/toolu_01RcjLgQg9SVXsgzGtT1p8Wa__vscode-1782817912278/content.txt)
|
||||
|
||||
Изучил архитектуру, ядро, генератор, историю багов и тесты. Сохраню анализ и представлю его.
|
||||
|
||||
Created memory file [](file:///home/naeel/.vscode-server/data/User/workspaceStorage/358cf615179442f06eab7245b983b753/GitHub.copilot-chat/memory-tool/memories/MmM3MzY3ZGQtNmQ3Zi00OGI0LTg0ZDAtMjViYmIwNmY3Y2Zi/plan.md)
|
||||
|
||||
# Анализ Terraform Provider для Nubes Cloud
|
||||
|
||||
Изучил архитектуру (ARCHITECTURE.md), ядро (client.go, crud.go), генератор (generate_resources_v2.go), транспорт (provider.go), 24 файла истории и тесты. Ниже — ответы по всем 10 пунктам: текущее состояние → плюсы → минусы → предложения → риски.
|
||||
|
||||
---
|
||||
|
||||
## 1. Два провайдера: Legacy + Universal
|
||||
|
||||
**Состояние.** Legacy (internal, v5.0.52, реестр `registry.terraform.io/nubes/nubes`, 13 ручных ресурсов) и Universal (universal_rebuild, v5.0.51, реестр `terra.k8c.ru/nubes/nubes`, ~50 генерируемых). Оба на `terraform-plugin-framework`, но это **полностью независимые деревья кода** с дублирующимся ядром (core существует в обоих).
|
||||
|
||||
**Хорошо.** Universal — целевая архитектура, чёткие принципы в ARCHITECTURE.md (YAML = source of truth). Разные реестры → можно мигрировать без коллизий.
|
||||
|
||||
**Плохо.** Дублирование ядра (client.go, `instance_lookup.go`) — баги фиксятся в двух местах (см. баг #1: правка в client.go И instance_lookup.go). Версии разъехались (5.0.52 vs 5.0.51) — непонятно, какая «свежее». Нет формального deprecation-плана с датой.
|
||||
|
||||
**Предложения.**
|
||||
- Зафиксировать **матрицу соответствия ресурсов** Legacy→Universal: какие 13 ресурсов уже перекрыты Universal, какие нет.
|
||||
- Объявить Legacy *frozen* (только critical-фиксы), завести `DEPRECATED.md` с целевой версией снятия.
|
||||
- Перенести уникальную логику Legacy (VM/vApp/edge/vdc) в YAML-спеки, проверить паритет, затем archive Legacy в отдельную ветку/тег.
|
||||
|
||||
**Риск.** VM/vApp в Legacy содержат ручную логику (FW-rules, 500-фикс из 04_vm_hang_fix_and_500_error), которую генератор может не воспроизвести. Нужен паритетный прогон на тестовом стенде до снятия Legacy.
|
||||
|
||||
---
|
||||
|
||||
## 2. Генерационный конвейер (API→YAML→Go→S3)
|
||||
|
||||
**Состояние.** 4 шага: `service_spec_gen` (API→YAML, ATTEMPTS=3, REQUEST_DELAY=0.5) → `gen_v2` (YAML→Go) → build+GPG+S3 → mkdocs. Включение сервиса = строка в `services_list.txt`.
|
||||
|
||||
**Хорошо.** Чёткое разделение, профили dev/test/prod изолируют артефакты, retry на шаге сбора YAML.
|
||||
|
||||
**Плохо — главное расхождение с собственными принципами.** ARCHITECTURE.md декларирует «*The generator must enforce these rules and fail fast on drift*», но фактически:
|
||||
- **Неизвестный `kind` операции тихо игнорируется** (generate_resources_v2.go — три `if op.Kind != "..." { continue }`). Если API введёт новый kind — ресурс молча пропадёт из провайдера, без ошибки.
|
||||
- **YAML почти не валидируется**: проверяется только синтаксис (`yaml.Unmarshal`). Отсутствие `op.Kind`/`op.Action` → zero-value → тихое игнорирование. Нет проверки уникальности param ID, наличия required-полей, валидности `RefSvcId`.
|
||||
- При ошибке `format.Source` генератор пишет **неформатированный (возможно битый) код** как fallback вместо остановки.
|
||||
- Исключение сервиса только комментированием в `services_list.txt` → риск рассинхрона (закомментировали в test, забыли в prod).
|
||||
|
||||
**Предложения.**
|
||||
- Добавить фазу `validateSpec()` перед генерацией: required-поля, уникальность ID, известность `kind`/`action`, ссылочная целостность `RefSvcId`. **Fail fast** на неизвестном kind.
|
||||
- Убрать fallback на неформатированный код — при `format.Source` error → паника с понятным сообщением.
|
||||
- CI-шаг «генерация без diff»: прогон генератора → `git diff --exit-code` (детект дрейфа, как требует ARCHITECTURE.md).
|
||||
- Go для генераторов оправдан (один язык со сгенерированным кодом, `text/template`, `format.Source`); bash-обёртки — лишь оркестрация. Менять не нужно.
|
||||
|
||||
**Риск.** Изменение формата ответа API на шаге 1 не обнаружится до runtime у пользователя. Сейчас единственная защита — `ATTEMPTS=3`, что не ловит *семантический* дрейф (поле переименовали, а не пропало).
|
||||
|
||||
---
|
||||
|
||||
## 3. API Flow V6 — отсутствие транзакционности
|
||||
|
||||
**Состояние.** 6 шагов в `CreateGenericInstanceUniversalV6`: `POST /instances` → `POST /instanceOperations` → `GET cfsParams` → resolve refs → `POST` каждого param → `validate-cfs` → `run` → `waitForOperationFinish`.
|
||||
|
||||
**Хорошо.** Завершение по `dtFinish` — надёжный контракт (выстрадан в 02, 05). Нормализация пустых map/json/array есть.
|
||||
|
||||
**Плохо.**
|
||||
- **Orphan при обрыве.** Если процесс упал/таймаут после `POST /instances`, но до `run` — в облаке остаётся инстанс в состоянии `not_created`/`creating`, **не попавший в Terraform state**. Следующий apply найдёт его через `FindInstanceByDisplayName` и упрётся в ошибку «найден в состоянии Not Created; удалите вручную». То есть пользователь обязан чистить руками.
|
||||
- **`doRequest` без retry** (client.go) — нет обработки 429/503/сетевых сбоев. Любой transient-сбой на шаге 4–5 рвёт create.
|
||||
- `req.Close = true` — новое TCP+TLS соединение на каждый запрос (на длинном поллинге дорого).
|
||||
|
||||
**Предложения.**
|
||||
- Retry с экспоненциальным backoff в `doRequest` для идемпотентных GET и для 429/503/сетевых ошибок (с уважением `Retry-After`).
|
||||
- Идемпотентность create: перед `POST /instances` делать `FindInstanceByDisplayName` (уже есть в `CreateResource`) — но также **обрабатывать «недосозданный» инстанс**: предлагать авто-cleanup `not_created`-инстанса при `adopt_existing_on_create=true`, а не только ручное удаление.
|
||||
- Рассмотреть keep-alive (убрать `req.Close=true`) для поллинга — меньше TLS-handshake.
|
||||
|
||||
**Риск.** Авто-cleanup `not_created` — операция удаления, требует явного флага и подтверждения семантики (нельзя удалять то, что пользователь мог создавать вручную параллельно).
|
||||
|
||||
---
|
||||
|
||||
## 4. CRUD / Adopt — `adoptExistingInstanceOnCreate`
|
||||
|
||||
**Состояние.** Покрытые ветки в crud.go:
|
||||
1. `operation_in_progress`/`pending` → ошибка «дождитесь».
|
||||
2. `!adopt_existing_on_create` → ошибка с подсказкой про import.
|
||||
3. `not_created` → ошибка.
|
||||
4. `running` → ref-валидация → adopt (возврат UUID).
|
||||
5. `suspended` → required-params check → resume → проверка статуса после resume → ref-валидация → adopt.
|
||||
6. Иначе (`creating`/`failed`/`error`) → общая ошибка «статус не подходит для авто-усыновления».
|
||||
|
||||
**Хорошо.** Логика соответствует decision-matrix из ARCHITECTURE.md. Ref-валидация при adopt (баг 22) закрыта. Диагностики подробные.
|
||||
|
||||
**Плохо.**
|
||||
- **Конкурентность не покрыта** (см. п.10): между `FindInstanceByDisplayName` и `CreateGenericInstanceUniversalV6` нет блокировки.
|
||||
- `creating`/`failed` падают в *общую* ветку с менее информативным сообщением, чем требует ARCHITECTURE.md (там для `creating`/`pending`/`failed` предписана отдельная диагностика).
|
||||
- Функция ~100 строк, глубокая вложенность, ref-валидация дублируется в двух ветках (running и после resume) — риск рассинхрона при правках.
|
||||
- `state_only`/`detach` destroy → `return nil` без API-вызова: инстанс остаётся в облаке (**это by design** из 20, но «orphaned» с т.з. биллинга — пользователь должен понимать).
|
||||
|
||||
**Предложения.**
|
||||
- Вынести классификацию статуса в один `switch` с явными ветками для каждого из 16 состояний (см. п.7), убрать дублирование ref-валидации в helper.
|
||||
- Для `creating`/`failed` — отдельные сообщения по контракту ARCHITECTURE.md.
|
||||
- Покрыть adopt-матрицу таблично-управляемыми тестами (сейчас 0 тестов на adopt, п.8).
|
||||
|
||||
**Риск.** Рефакторинг самой сложной функции без тестов опасен — сначала тесты, потом рефакторинг.
|
||||
|
||||
---
|
||||
|
||||
## 5. Генератор gen_v2 — шаблоны вшиты в бинарь
|
||||
|
||||
**Состояние.** 3 inline-шаблона `text/template`: `instanceTemplate` (569 строк), `subresourceTemplate` (443), `actionTemplate` (174). `computeCreateOnly` = эвристика (param в create, но не в modify). Identity подресурса: нет modify → все params ForceNew; есть modify → createOnly+delete params ForceNew.
|
||||
|
||||
**Хорошо.** Эвристика createOnly опирается на данные API (не хардкод), `format.Source` гарантирует валидный Go при успехе, восстановление регистра UUID решает «inconsistent result».
|
||||
|
||||
**Плохо.**
|
||||
- Шаблоны как строковые константы внутри `.go` (1186 строк шаблонов) — тяжело поддерживать, нет подсветки/линтинга шаблонов, любая правка = пересборка генератора.
|
||||
- **Новый kind → тихое выпадение ресурса** (см. п.2).
|
||||
- `data_type: json` → `IsJson`+`JsonNormalize` plan-modifier — но **0 тестов** на это, а нормализация JSON исторически проблемная (V2→V6 цикл, баг 13).
|
||||
- Immutable определяется только через отсутствие в modify — если API *временно* не отдаёт modify-параметр (сбой/неполный YAML), параметр ошибочно станет ForceNew → пересоздание ресурса.
|
||||
|
||||
**Предложения.**
|
||||
- Вынести шаблоны в `embed.FS` (`//go:embed templates/*.tmpl`) — поддерживаемость без потери single-binary.
|
||||
- Fail fast на неизвестном kind + лог числа сгенерированных ресурсов на сервис (детект «пропал ресурс»).
|
||||
- Снапшот-тесты генератора: эталонный YAML → ожидаемый `.go` (golden files).
|
||||
- Защита от «исчезнувшего modify-параметра»: предупреждать, если у сервиса есть create-params, но 0 modify-params (подозрительно).
|
||||
|
||||
**Риск.** Если `gen_v2` сломается — ломается **весь** Universal-провайдер. Сейчас единственная страховка — `format.Source`, который при ошибке всё равно пишет битый код.
|
||||
|
||||
---
|
||||
|
||||
## 6. Баги из истории — что системное, что осталось
|
||||
|
||||
**Системные классы (порождали серии багов):**
|
||||
1. **Фильтрация deleted** (23, 22, 21) — API возвращает deleted-инстансы, провайдер их не отсеивал. **Закрыт 5.0.50** (`isDeleted=false` + `isInstanceDeleted()` + ошибка при >1 совпадении).
|
||||
2. **Определение конца операции** (05, 02, 04) — зависание поллинга. **Закрыт** (критерий `dtFinish`).
|
||||
3. **Динамические param ID** (create ID ≠ modify ID, 06) — **закрыт** runtime-discovery.
|
||||
4. **Create-only параметры** (16) — **закрыт 5.0.38**.
|
||||
|
||||
**Не до конца решённые / ограничения:**
|
||||
- **Нормализация map/json/list** — потребовала 5 итераций (V2→V6), помечена как *частично*; новые типы параметров могут снова всплыть.
|
||||
- **Realm-валидация отключена** (21) из-за бага бэкенда `/resourceRealms/available` — валидации realm до деплоя нет.
|
||||
- **FW-rules 500** (04) — **platform-side bug**, воспроизводится и в Cloud Console; провайдер не может починить.
|
||||
- **Disk shrink** (06) — ограничение платформы (только увеличение), провайдер корректно прокидывает ошибку.
|
||||
|
||||
**Главная системная проблема** — именно фильтрация deleted была корнем 3+ багов. Сейчас закрыта, но **отсутствие тестов** означает, что регрессия не будет поймана автоматически.
|
||||
|
||||
**Предложения.** Regression-тесты на deleted-фильтрацию и multi-match; превратить known-limitations в явные диагностики (например, предупреждать про realm «валидация недоступна»).
|
||||
|
||||
---
|
||||
|
||||
## 7. Матрица состояний — 16 состояний
|
||||
|
||||
**Состояние.** `INSTANCE_STATES.md` / `STATE_TRANSITIONS.md` описывают полную матрицу. В коде adopt обрабатывает: `not_created`, `running`, `suspended`, `in_progress`/`pending`; остальные → общая ошибка.
|
||||
|
||||
**Плохо.** Хелперы `isStatusSuspended`/`isStatusNonAdoptable`/`isStatusNotCreated` работают через `strings.Contains` по тексту `explainedStatus` — **хрупко**: изменение формулировки статуса в API сломает классификацию молча. Промежуточные (`creating`, `failed`, `error`) сваливаются в одну ветку без индивидуальных подсказок, хотя ARCHITECTURE.md требует разные диагностики.
|
||||
|
||||
**Предложения.** Завести enum состояний и единую функцию `classifyStatus(raw) → State`, маппинг raw→enum в одном месте, exhaustive `switch` по всем 16 (с `default → явная ошибка «неизвестный статус X»`). Тесты на каждый статус.
|
||||
|
||||
**Риск.** Строковое сопоставление — самое уязвимое место к молчаливому дрейфу API.
|
||||
|
||||
---
|
||||
|
||||
## 8. Тесты — достаточно ли
|
||||
|
||||
**Состояние.** **8 unit-тестов всего**: crud_test.go (3: `isStatusSuspended`, `isStatusNonAdoptable`, delete-default) и client_test.go (5: нормализация значений). Без моков, без integration.
|
||||
|
||||
**Не покрыто (критично):** adopt-логика, ref-валидация, polling/`waitForOperationFinish`, `FindInstanceByDisplayName` (deleted+multi-match), генератор целиком, JSON plan-modifier, suspend/resume, required-params compare.
|
||||
|
||||
**Предложения.**
|
||||
- `httptest.Server` мок Nubes API → тесты Flow V6, поллинга по `dtFinish`, обрыва на шаге N, 429/503.
|
||||
- Табличные тесты adopt-матрицы (все 16 состояний × `adopt_existing_on_create` true/false).
|
||||
- Golden-тесты генератора (YAML→Go).
|
||||
- Контракт-тесты на основе HAR (см. п. ниже).
|
||||
|
||||
**Риск.** Все закрытые системные баги (deleted, polling, param-ID) **не защищены от регрессии**. Любой рефакторинг ядра/генератора — рулетка.
|
||||
|
||||
---
|
||||
|
||||
## 9. Безопасность
|
||||
|
||||
**Состояние.**
|
||||
- Токены: `*.token` в .gitignore ✅; private_key.asc в .gitignore ✅; id_ed25519.txt ✅.
|
||||
- `InsecureSkipVerify` default = **false** ✅, включается только явно/`NUBES_INSECURE=true`. TLS 1.2 min ✅.
|
||||
- `api_token` помечен `Sensitive: true` ✅.
|
||||
|
||||
**Хорошо.** Базовая гигиена соблюдена — ключи и токены не коммитятся, TLS-проверка по умолчанию включена.
|
||||
|
||||
**Плохо / проверить.**
|
||||
- **GPG-ключ физически лежит в secrets** — да, в .gitignore, но стоит проверить `git log --all -- secrets/private_key.asc`, что он не попал в историю ранее. .gitignore не вычищает уже закоммиченное.
|
||||
- public_key.asc, id_ed25519.pub — публичные, ок; но prod.token/`dev.token`/`test.token` существуют локально — убедиться, что покрыты `*.token` (да) и не было коммита до добавления правила.
|
||||
- **Утечка токена в логи**: `formatAPIError` форматирует тело ответа API в ошибку — если API эхает заголовки/токен в body ошибки, он попадёт в диагностику Terraform. `doRequest` сам токен не логирует. Стоит маскировать `Bearer ...` в любых сообщениях.
|
||||
- `ttyOut()` пишет напрямую в tty минуя Terraform — в debug-режиме `StageMsg` может содержать чувствительные данные; они идут в терминал в обход TF-логирования.
|
||||
|
||||
**Предложения.** `git log` аудит секретов; явная маскировка токена в `formatAPIError`/диагностиках; политика ротации `*.token`; вынести секреты из репо в внешний secret-store (для CI).
|
||||
|
||||
**Риск.** Если ключ/токен попал в git-историю до .gitignore — он уже скомпрометирован, .gitignore не поможет. Это надо проверить первым делом.
|
||||
|
||||
---
|
||||
|
||||
## 10. Конкурентность — два `terraform apply`
|
||||
|
||||
**Состояние.** Никаких блокировок. `FindInstanceByDisplayName` + `CreateGenericInstanceUniversalV6` — **не атомарны**. `waitForInstanceIdle` ждёт `operationIsPending/InProgress`, но это не защищает от гонки create.
|
||||
|
||||
**Плохо.**
|
||||
- Два apply с одинаковым `displayName` одновременно: оба проходят `FindInstanceByDisplayName` (никого нет) → оба `POST /instances` → **два инстанса с одним именем**. После этого `FindInstanceByDisplayName` начнёт возвращать ошибку «найдено 2 инстанса» (баг 22 только *детектирует* это, но не предотвращает).
|
||||
- Между modify из двух окружений — гонка на `instanceOperations`; частично гасится `waitForInstanceIdle`, но окно остаётся.
|
||||
|
||||
**Предложения.**
|
||||
- Полагаться на **Terraform state locking** (backend lock) как первичную защиту — это ответственность пользователя, задокументировать.
|
||||
- На стороне API — проверить, есть ли уникальность `displayName` на бэкенде; если нет, провайдер не может гарантировать атомарность.
|
||||
- Минимально: после `POST /instances` сразу повторный `FindInstanceByDisplayName` и, если найдено >1, откатить свой (требует delete — осторожно).
|
||||
|
||||
**Риск.** Полноценная защита возможна только при поддержке со стороны API (уникальность имени или conditional create). Провайдер в одиночку гонку не закрывает.
|
||||
|
||||
---
|
||||
|
||||
## Сводный план улучшений (по приоритету)
|
||||
|
||||
**Steps**
|
||||
|
||||
**P0 — Надёжность runtime (блокеры для прода)**
|
||||
1. Retry + backoff в `doRequest` для 429/503/сетевых сбоев (уважать `Retry-After`).
|
||||
2. Обработка orphan-инстанса `not_created`: авто-cleanup при явном флаге вместо «удалите вручную».
|
||||
3. Документировать и протестировать поведение при конкурентном apply (state-lock + повторная проверка после create).
|
||||
|
||||
**P1 — Защита от дрейфа и регрессий** (*parallel с P0*)
|
||||
4. `validateSpec()` в генераторе + **fail fast на неизвестном kind**; убрать fallback на неформатированный код.
|
||||
5. CI-шаг «генерация без diff» (детект дрейфа per ARCHITECTURE.md).
|
||||
6. Тесты: `httptest` мок API (Flow V6, polling, обрыв, 429), табличные тесты adopt-матрицы, golden-тесты генератора, regression на deleted-фильтрацию.
|
||||
|
||||
**P2 — Снижение тех-долга**
|
||||
7. Единый `classifyStatus → enum` для 16 состояний, exhaustive switch, убрать `strings.Contains`-классификацию.
|
||||
8. Рефакторинг `adoptExistingInstanceOnCreate` (после тестов): вынести ref-валидацию в helper, отдельные диагностики для `creating`/`failed`.
|
||||
9. Шаблоны генератора в `embed.FS`.
|
||||
10. План вывода Legacy: матрица паритета ресурсов, `DEPRECATED.md`, целевая версия снятия.
|
||||
|
||||
**P3 — Безопасность и наблюдаемость**
|
||||
11. `git log` аудит secrets; маскировка `Bearer`-токена в `formatAPIError`/диагностиках.
|
||||
12. Realm-валидация — включить, когда бэкенд починит `/resourceRealms/available`.
|
||||
13. HAR-based контракт-тесты (верификация соответствия провайдера реальному API).
|
||||
|
||||
**Relevant files**
|
||||
- client.go — `doRequest` (retry), `CreateGenericInstanceUniversalV6` (orphan/идемпотентность), `FindInstanceByDisplayName` (конкурентность).
|
||||
- crud.go — `adoptExistingInstanceOnCreate` (рефакторинг + тесты), статус-хелперы.
|
||||
- generate_resources_v2.go — валидация YAML, fail-fast на kind, вынос шаблонов.
|
||||
- provider.go — транспорт (keep-alive, маскировка токена).
|
||||
- Тесты: рядом с crud.go и client.go.
|
||||
|
||||
**Decisions / scope**
|
||||
- Это аналитический отчёт + план; **код не менялся** (режим Plan, и правила проекта запрещают изменения без «делай»).
|
||||
- Большинство критических багов из истории **уже закрыты** (5.0.50). Главный системный риск сейчас — **отсутствие тестов** (регрессии не ловятся) и **дрейф API** (генератор/статусы молча ломаются).
|
||||
|
||||
**Further Considerations**
|
||||
1. С чего начать реализацию? **A)** P0 (надёжность runtime) / **B)** P1 (тесты+защита от дрейфа — фундамент для безопасного рефакторинга) / **C)** аудит безопасности секретов (быстро, но критично, если ключ в истории git).
|
||||
2. Вывод Legacy — делать сейчас параллельно или после стабилизации Universal тестами? Рекомендую **после** (нужен паритет VM/vApp, который без тестов рискован).
|
||||
3. Нужен ли вам этот анализ как отдельный markdown-файл в docs (например `docs/50_history/25_opus_deep_analysis.md`), или достаточно ответа в чате?
|
||||
|
||||
Какой блок развернуть детальнее или с какого приоритета начинать?
|
||||
|
||||
Создано задач: 5
|
||||
|
||||
Список задач обновлен
|
||||
|
||||
## Общее мнение
|
||||
|
||||
**Архитектурно — сильное, зрелое решение.** Идея «YAML как source of truth → генерация Go-кода и доков» правильная и редко встречается в self-made провайдерах. Универсальное ядро без сервис-специфичной логики + декларативные спеки = можно добавить сервис без программиста. Это уровень выше типичного «руками написанного» провайдера.
|
||||
|
||||
**Что реально хорошо:**
|
||||
- Выстраданный контракт с API (`dtFinish` для конца операции, динамические param-ID, фильтрация deleted) — видно, что прошли через боль и закрыли системные баги.
|
||||
- Чистое разделение pipeline (API→YAML→Go→S3), профили dev/test/prod.
|
||||
- Базовая безопасность в порядке (токены/ключи в .gitignore, TLS-проверка по умолчанию).
|
||||
|
||||
**Главные слабости (по убыванию):**
|
||||
1. **Тесты почти отсутствуют** (8 unit-тестов, без моков, без integration). Все закрытые баги не защищены от регрессии — это самый большой риск.
|
||||
2. **Генератор молча ломается** при дрейфе API (неизвестный kind тихо игнорируется, YAML не валидируется) — при этом сам же ARCHITECTURE.md требует «fail fast on drift». Декларация ≠ реализация.
|
||||
3. **Нет транзакционности create и retry** — обрыв посередине оставляет orphan, transient-сбой рвёт apply.
|
||||
4. **Два провайдера с дублированным ядром** — тех-долг, баги фиксятся в двух местах.
|
||||
|
||||
**Вердикт:** концепция и инженерная зрелость ядра — выше среднего, видно опытную руку. Но проект держится на «героике» (ручное тестирование на реальных стендах, комментарии «НЕ МЕНЯТЬ»), а не на автоматических гарантиях. Один слой тестов + fail-fast в генераторе превратили бы его из «работает, потому что мы знаем где не трогать» в по-настоящему надёжный. Сейчас это **крепкий прототип на пути к продакшн-grade**, а не законченный продукт.
|
||||
|
||||
Read memory [](file:///home/naeel/.vscode-server/data/User/globalStorage/github.copilot-chat/memory-tool/memories/naeel/.vscode-server/data/User/workspaceStorage/358cf615179442f06eab7245b983b753/GitHub.copilot-chat/memory-tool/memories/MmM3MzY3ZGQtNmQ3Zi00OGI0LTg0ZDAtMjViYmIwNmY3Y2Zi/plan.md)
|
||||
|
||||
Created [](file:///home/naeel/tf_provider/HISTORY/OPUS/3006_1_questions.md)
|
||||
|
||||
Готово: 3006_1_questions.md
|
||||
|
||||
## 8 дополнительных вопросов
|
||||
|
||||
| # | Тема | Что нового (после его анализа) |
|
||||
|---|------|-------------------------------|
|
||||
| 1 | **Расхождение версий** | Регистр отдаёт 5.0.55, код — 5.0.51. Откуда 53-55? |
|
||||
| 2 | **Полный аудит isDeleted** | Все ли GET /instances фильтруют deleted? Особенно `findInstanceUidByDisplayNameRefSvc` |
|
||||
| 3 | **Gitea 413** | Где nginx-конфиг Gitea в репо? Можно ли увеличить лимит через чарты? |
|
||||
| 4 | **Suspend-сервисы** | Полный список кто поддерживает suspend/resume. Есть ли баги (suspend без resume)? |
|
||||
| 5 | **Terraform Operator** | Почему перезапустился 2 дня назад? Хватает ли компонентов? Где GPG/S3 конфиг? |
|
||||
| 6 | **Тестовые стенды** | Какие реально используются? Legacy или Universal? Дубликаты? |
|
||||
| 7 | **auth.k8s.ngcloud.ru** | OIDC-конфиг, обновление токенов, Keycloak HAR |
|
||||
| 8 | **Orphan PVC/STS** | Откуда `storage-check-*` PVC? PostgreSQL 0/0 28 дней — почему? |
|
||||
|
||||
Все вопросы — **только чтение файлов в репо**, никаких правок. Opus может ответить на большинство из них. Вопросы 3 и 8 частично требуют данных из кластера (не из репо) — он это честно отметит.
|
||||
|
||||
Внесены изменения.
|
||||
|
||||
Список задач обновлен
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/HISTORY/OPUS/3006_1_questions.md)
|
||||
|
||||
Исследую все 8 вопросов через чтение файлов репозитория. Запущу параллельный сбор данных.
|
||||
|
||||
Searched for regex `/instances\?|/instances"|GET.*instances|page=|isDeleted` (`**/universal_rebuild/internal/core/*.go`), 20 results
|
||||
|
||||
Searched for regex `findInstanceUidByDisplayNameRefSvc|ListRefServiceInstances`, 20 results
|
||||
|
||||
Searched for files matching `**/devops/profiles/**/profile.env`, 3 matches
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/universal_rebuild/internal/core/refsvc_resolve.go)
|
||||
|
||||
Нашёл ключевое для вопроса 2 — прочитаю функцию `findInstanceUidByDisplayNameRefSvc` и файлы версий.
|
||||
|
||||
Searched for regex `VERSION|version|5\.0\.` (`**/devops/03_build_and_upload_provider.sh`), 13 results
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/universal_rebuild/internal/core/client.go#900-900), lines 900 to 1010
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/devops/profiles/prod/profile.env)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/devops/profiles/test/profile.env)
|
||||
|
||||
Вопросы 1 и 2 почти закрыты. Соберу данные по остальным: инфраструктура, suspend-аудит, стенды, auth, orphan-ресурсы.
|
||||
|
||||
Searched for regex `auth\.k8s\.ngcloud\.ru|keycloak|shturval|client_max_body_size|gitea|storage-check`, 20 results
|
||||
|
||||
Searched for regex `getInstanceDisplayNameByUidRefSvc|findInstanceDisplayNameByUidRefSvc` (`**/universal_rebuild/internal/core/*.go`), 6 results
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/charts)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/cloud-dashboard/k8s)
|
||||
|
||||
Searched for regex `storage-check|terraform-operator|registry-server|operator`, 20 results
|
||||
|
||||
Searched for files matching `**/{operator,registry-server,registry-server-build,k8s}/**`, 3 matches
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/universal_rebuild/internal/core/client.go#1057-1057), lines 1057 to 1140
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/cloud-dashboard/k8s/ingress.yaml)
|
||||
|
||||
Аудит suspend/resume сервисов
|
||||
|
||||
Аудит тестовых стендов
|
||||
|
||||
Read [](file:///home/naeel/tf_provider)
|
||||
|
||||
Searched for regex `kubeconfig|OIDC|oidc|kube-apiserver|kubectl|HOW_TO_SSH|auth\.k8s`, 20 results
|
||||
|
||||
Searched for files matching `**/TEST_STAND/**/*.tf`, 11 matches
|
||||
|
||||
Searched for files matching `**/PROD_STAND/**/*.tf`, 10 matches
|
||||
|
||||
Searched for files matching `**/RABBIT/**/*.tf`, 6 matches
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/TEST_STAND)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/PROD_STAND)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/RABBIT)
|
||||
|
||||
Searched for files matching `tf_provider` · `universal_rebuild/resources_yaml/**/*.yaml`, no matches
|
||||
|
||||
Searched for files matching `tf_provider` · `devops/profiles/prod/generated/resources_yaml/**/*.yaml`, no matches
|
||||
|
||||
Searched for files matching `tf_provider` · `devops/profiles/test/generated/**/*.yaml`, no matches
|
||||
|
||||
Searched for files matching `tf_provider` · `universal_rebuild/resources_yaml/embed.go`, 1 match
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/universal_rebuild/resources_yaml)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/devops/profiles/prod/generated)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/devops/profiles/prod/generated/resources_yaml)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/devops/profiles/test/generated)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/universal_rebuild/resources_yaml/embed.go)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/devops/profiles/test/generated/resources_yaml)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/TEST_STAND/LUCEE/resources.tf#1-1), lines 1 to 50
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/TEST_STAND/MARIA_DB/resources.tf#1-1), lines 1 to 50
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/TEST_STAND/POSTGRES/main.tf)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/TEST_STAND/POSTGRES/resources.tf)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/PROD_STAND/PG1/resources.tf#1-1), lines 1 to 100
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/PROD_STAND/POSTGRES/resources.tf)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/PROD_STAND/RABBIT/main.tf)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/PROD_STAND/RABBIT/resources.tf#1-1), lines 1 to 100
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/RABBIT/main.tf)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/RABBIT/resources.tf)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/TEST_STAND/LUCEE/main.tf)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/TEST_STAND/MARIA_DB/main.tf)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/TEST_STAND/S3_EVENT_FUNCTION_POC/versions.tf)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/PROD_STAND/PG1/main.tf)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/PROD_STAND/POSTGRES/main.tf)
|
||||
|
||||
Searched for regex `suspend|resume` (`tf_provider` · `devops/profiles/prod/generated/resources_yaml/**/*.yaml`), 20 results
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/TEST_STAND/S3_EVENT_FUNCTION_POC/main.tf#1-1), lines 1 to 50
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/TEST_STAND/POSTGRES/luceUNDnode.tf#1-1), lines 1 to 60
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/PROD_STAND/POSTGRES/flask_disabled.tf)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/PROD_STAND/PG1/vm.tf)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/PROD_STAND/RABBIT/rabbit_worker_disabled.tf)
|
||||
|
||||
Searched for regex `suspend|resume` (`tf_provider` · `devops/profiles/prod/generated/resources_yaml/**/*.yaml`), 187 results
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/devops/profiles/prod/generated/resources_yaml/90_postgres.yaml#1-1), lines 1 to 20
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/devops/profiles/prod/generated/resources_yaml/90_postgres.yaml#459-459), lines 459 to 469
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/devops/profiles/prod/generated/resources_yaml/115_mariadb.yaml#1-1), lines 1 to 20
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/devops/profiles/prod/generated/resources_yaml/115_mariadb.yaml#238-238), lines 238 to 248
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/devops/profiles/prod/generated/resources_yaml/91_redis.yaml#1-1), lines 1 to 20
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/devops/profiles/prod/generated/resources_yaml/117_nifi.yaml#1-1), lines 1 to 20
|
||||
|
||||
Searched for regex `- name:|kind:|action:` (`tf_provider` · `devops/profiles/prod/generated/resources_yaml/91_redis.yaml`), 6 results
|
||||
|
||||
Searched for regex `- name:|kind:|action:` (`tf_provider` · `devops/profiles/prod/generated/resources_yaml/117_nifi.yaml`), 9 results
|
||||
|
||||
Searched for regex `- name:|kind:|action:` (`tf_provider` · `devops/profiles/prod/generated/resources_yaml/112_tenant.yaml`), 9 results
|
||||
|
||||
Searched for regex `- name:|action:` (`tf_provider` · `devops/profiles/prod/generated/resources_yaml/22_vc_nsxt.yaml`), 8 results
|
||||
|
||||
Searched for regex `- name:|action:` (`tf_provider` · `devops/profiles/prod/generated/resources_yaml/29_vc_vdc_group.yaml`), 10 results
|
||||
|
||||
Searched for regex `- name:|action:` (`tf_provider` · `devops/profiles/prod/generated/resources_yaml/100_openwhisk.yaml`), 4 results
|
||||
|
||||
Проверю наличие `operator/`/`k8s/`, прочитаю отчёт по gitea/registry и README про реестр.
|
||||
|
||||
Searched for files matching `**/operator/**`, no matches
|
||||
|
||||
Searched for files matching `**/k8s/**`, 3 matches
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/README.md#40-40), lines 40 to 127
|
||||
|
||||
Read [](file:///home/naeel/.vscode-server/data/User/workspaceStorage/358cf615179442f06eab7245b983b753/GitHub.copilot-chat/chat-session-resources/2c7367dd-6d7f-48b4-84d0-25bbb06f7cfb/toolu_01RGa2BMVk2W3EiaCRQTaSMg__vscode-1782817912438/content.txt)
|
||||
|
||||
Финальная проверка: наличие `registry-server-build/`, детали оператора/S3/GPG в кластере, keycloak HAR.
|
||||
|
||||
Searched for files matching `**/registry-server-build/**`, no matches
|
||||
|
||||
Searched for files matching `**/HAR/*keycloak*`, no matches
|
||||
|
||||
Searched for regex `S3_ENDPOINT|s3.msk-1|terraform-registry|S3_BUCKET|\.s3cfg`, 20 results
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/docs/50_history/00_system_mechanics.md)
|
||||
|
||||
Все 8 вопросов исследованы. Ниже ответы в формате **находка → файлы → вывод → рекомендация**.
|
||||
|
||||
---
|
||||
|
||||
## Вопрос 1: Откуда версии 5.0.53–5.0.55?
|
||||
|
||||
**Находка.** Версия билда берётся **не из main.go**, а из профиля стенда:
|
||||
- 03_build_and_upload_provider.sh: `VERSION = $1 → иначе $PROVIDER_VERSION/$RELEASE_VERSION (из profile.env) → иначе grep из main.go (fallback)`.
|
||||
- profile.env: `PROVIDER_VERSION="5.0.55"`.
|
||||
- profile.env: `PROVIDER_VERSION="2.1.23"` (!).
|
||||
- main.go хранит `5.0.51` — это лишь **последний fallback**, который почти никогда не срабатывает.
|
||||
|
||||
**Вывод.** 5.0.53/54/55 — это **ручные билды с тестового стенда**: оператор вручную bump-ит `PROVIDER_VERSION` в `profiles/test/profile.env` и запускает `03_build...sh`. main.go при этом не обновляется (README прямо просит обновлять, но на практике это не делается — отсюда рассинхрон). Важно: **prod-профиль застрял на 2.1.23**, а test ушёл на 5.0.55 — это две независимые линейки версий. Мой прошлый анализ (3006_0.md) цитировал версии из кода (5.0.51/5.0.52), а регистр отдаёт версию из profile.env (5.0.55) — поэтому они не совпадают.
|
||||
|
||||
**Рекомендация.** Сделать profile.env единственным источником версии и автоматически прописывать её в main.go при сборке (или вычислять main.go-версию из env через ldflags `-X`). Сейчас 3 источника версии (main.go, test, prod) живут врозь.
|
||||
|
||||
---
|
||||
|
||||
## Вопрос 2: Полный аудит фильтрации `isDeleted`
|
||||
|
||||
**Находка.** Все функции, делающие GET к `/instances`:
|
||||
|
||||
| Функция | Файл | `isDeleted=false` в URL | Проверка в коде | Статус |
|
||||
|---|---|---|---|---|
|
||||
| `FindInstanceByDisplayName` | client.go | ✅ (search) / ❌ (fallback) | ✅ `isInstanceDeleted()` | OK |
|
||||
| `ListRefServiceInstances` | refsvc_resolve.go | ✅ | ✅ `IsDeleted` + только running | OK |
|
||||
| `findInstanceUidByDisplayNameRefSvc` | client.go | ❌ | ✅ `if item.IsDeleted {continue}` + `status=="deleted"` | **OK (исправлен!)** |
|
||||
| `findInstanceDisplayNameByUidRefSvc` | client.go | ❌ | ❌ нет | приемлемо |
|
||||
| `getInstanceDisplayNameByUidRefSvc` | client.go | — (GET по UID) | — | n/a |
|
||||
| `GetInstanceState/Raw/Details` | client.go / instance_outputs.go | — (GET по UID) | ✅/частично | n/a |
|
||||
|
||||
**Вывод.**
|
||||
- **`findInstanceUidByDisplayNameRefSvc` УЖЕ исправлен** — он пропускает deleted в коде (строки client.go) и предпочитает running>suspended. Замечание в 24_ai_analysis_pipeline_architecture_2026_06_30.md («НЕ ИСПРАВЛЕН?») **устарело** — баг класса #23 здесь закрыт. Единственный недочёт — нет `isDeleted=false` в URL (лишний трафик, но не баг корректности).
|
||||
- **`findInstanceDisplayNameByUidRefSvc` (1058)** — единственная функция БЕЗ фильтра deleted ни в URL, ни в коде. Но она ищет по **точному `instanceUid`** (уникальному) и возвращает displayName — это обратный маппинг для чтения state, не выбор «того/не того» инстанса. Класс багов #23 здесь не применим. Риск минимальный: вернёт имя deleted-инстанса, если в state остался его UID.
|
||||
|
||||
**Рекомендация.** Косметика: добавить `&isDeleted=false` в URL `findInstanceUidByDisplayNameRefSvc` (1057) и `findInstanceDisplayNameByUidRefSvc` (1060) для экономии трафика. Корректность уже обеспечена. Обновить вывод в файле 24 (он сеет ложную тревогу).
|
||||
|
||||
---
|
||||
|
||||
## Вопрос 3: Gitea 413 (`client_max_body_size`)
|
||||
|
||||
**Находка.**
|
||||
- charts — **пустая** (list_dir: folder empty).
|
||||
- Манифесты в репо есть только для dashboard: ingress.yaml — ingress для `terra.k8c.ru/dashboard`, аннотаций `client_max_body_size`/`proxy-body-size` нет, и это **не Gitea**.
|
||||
- `gitea-naeel.giteak8s.services.ngcloud.ru` упоминается только как `git_path` в resources.tf и закомментированно в luceUNDnode.tf. Сам Gitea — это **управляемый сервис Nubes** (см. svcs.json: «Gitea», «Комплексная услуга по созданию gitea», service_id 99/114), развёрнутый в кластере `giteak8s`, а не из этого репо.
|
||||
|
||||
**Вывод.** **Конфигурация nginx Gitea в этом репозитории отсутствует.** `client_max_body_size=1MB` задаётся на ingress управляемого Gitea в кластере `giteak8s.services.ngcloud.ru` — **внешняя конфигурация**, недоступная для правки из этого репо.
|
||||
|
||||
**Рекомендация.** Лимит правится за пределами репо — на ingress Gitea-инстанса: аннотация `nginx.ingress.kubernetes.io/proxy-body-size: "0"` (или, например, `512m`). Это требует доступа к namespace Gitea в кластере giteak8s. Из репо проблему не решить. *(Требует данных кластера — отмечаю честно.)*
|
||||
|
||||
---
|
||||
|
||||
## Вопрос 4: Полный список suspend/resume-сервисов
|
||||
|
||||
**Находка.** YAML-спеки в devops/profiles/prod/generated/resources_yaml/ (43 файла, test идентичен), встроены через `//go:embed *` в embed.go.
|
||||
|
||||
**26 сервисов с suspend И resume** (service_id): `1 dummy, 2 template, 12 s3, 19 vc_org, 20 vc_org_saas, 21 vc_vdc, 23 vc_vm, 26 vapp, 27 vc_vm_v2, 28 vc_vm_v3, 50 nextcloud, 89 flask, 90 postgres, 92 mongodb, 93 rabbitmq, 94 lucee, 95 nodejs, 96 pgadmin, 98 http, 99 gitea, 115 mariadb, 116 kafka, 119 akhq, 120 clickhouse, 149 valo_tenant, 150 k8s_shturval`.
|
||||
|
||||
**17 сервисов без suspend/resume**: `13 s3bucket, 22 vc_nsxt, 25 vcexternalip, 29 vc_vdc_group, 32 vmpostgre, 81 superset, 82 harbor, 88 ziti, 91 redis, 97 nodered, 100 openwhisk, 110 dnszone, 111 dnsrecord, 112 tenant, 113 vc_complex, 114 gitea_complex, 117 nifi`.
|
||||
|
||||
**Вывод.**
|
||||
- **Асимметрии нет**: suspend и resume всегда идут парой (26/26). Сервисов «suspend без resume» — **0**.
|
||||
- `suspend_on_destroy_default` **идеально совпадает** с правилом `hasSuspend → true`: 26 suspend-сервисов = `true`, 17 = `false`. Расхождений нет.
|
||||
- Логика разделения здравая: stateless (DNS, external IP, tenant) — без suspend; stateful (БД, приложения, VM) — с suspend.
|
||||
|
||||
**Рекомендация.** По этому пункту всё чисто. Стоит лишь добавить в генератор `validateSpec()`-проверку «если есть suspend, обязан быть resume» как защиту на будущее (сейчас инвариант соблюдён случайно — генератор его не enforce-ит).
|
||||
|
||||
---
|
||||
|
||||
## Вопрос 5: Terraform Operator — состояние и health
|
||||
|
||||
**Находка — ключевая.** Директорий `operator/`, `registry-server-build/`, корневого `k8s/` **НЕТ в этом checkout** (file_search: «No files found»), хотя они активно упоминаются в README.md, REPO_CONTENTS.md, build-registry-image.yml (`operator/build/Dockerfile.registry`) и deploy-dev.sh (`k8s/overlays/dev`).
|
||||
|
||||
Что есть в репо — описание в 00_system_mechanics.md:
|
||||
- 3 компонента: **Operator** (watch CRD → spawn Job), **Registry API** (Discovery protocol over S3), **Builder Job** (`golang:1.24-alpine`: clone→build→upload S3). Это совпадает с тремя deployment'ами, что вы видели.
|
||||
- **S3**: бакет артефактов `terraform-providers`, схема пути `{hostname}/{namespace}/{provider}/{version}/{file}`, `REGISTRY_HOSTNAME=terra.k8c.ru`. Эндпойнт `s3.msk-1.ngcloud.ru` (upload_provider_s3.py, REPO_CONTENTS.md).
|
||||
- **GPG в кластере — ФЕЙКОВЫЙ**: 00_system_mechanics.md — «GPG Signing: Сейчас фейковое (создаётся пустой `.sig`)». Реальная подпись private_key.asc используется только в **локальном** `03_build...sh`, а не в operator-Job.
|
||||
- **Перезапуск оператора — штатная операция**: в cheat-sheet прямо есть `kubectl rollout restart deploy/terraform-operator -n terra`.
|
||||
|
||||
**Выводы по вопросам:**
|
||||
1. **Перезапуск 2 дня назад** — скорее всего ручной `rollout restart` (документированная команда подхвата изменений `manifests/03-build-script.yaml`), а не краш. Подтвердить можно только по `kubectl describe pod`/`--previous` логам в кластере. *(Требует кластера.)*
|
||||
2. **Трёх deployment'ов достаточно** для registry+CI (operator+registry-server+builder-job). Отдельного docs-server нет и не нужен: доки — статика в S3 (`terraform-registry/docs/...`), publish-docs.sh. cloud-dashboard — отдельный UI, не часть registry.
|
||||
3. **GPG-ключи**: в кластере подпись фейковая (tech debt из system_mechanics); реальный ключ только локально в secrets. **S3-доступ** оператора — через ENV `S3_*`/`REGISTRY_HOSTNAME` в Deployment (должны быть в Secret `terraform-operator`).
|
||||
4. **S3-конфиг бакета в репо** — только переменные и схема путей; манифест Secret/Deployment отсутствует (он в недостающей папке `operator/`/`k8s/`).
|
||||
|
||||
**Рекомендация.** Критично: **внедрить реальную GPG-подпись в operator-Job** (смонтировать private_key.asc как K8s Secret) — сейчас артефакты из кластера подписаны пустышкой, Terraform может ругаться `authentication signature from unknown issuer`. Также — вернуть `operator/`+`k8s/` в этот репо или явно задокументировать, что они в отдельном репозитории (сейчас CI ссылается на отсутствующие пути).
|
||||
|
||||
---
|
||||
|
||||
## Вопрос 6: Тестовые стенды — что реально используется
|
||||
|
||||
**Находка.** Все `.tf` используют **только Universal** (`source = "terra.k8c.ru/nubes/nubes"`), Legacy (`registry.terraform.io/nubes/nubes`) **не используется нигде**.
|
||||
|
||||
| Стенд | Version | Endpoint | Активные ресурсы |
|
||||
|---|---|---|---|
|
||||
| TEST_STAND/LUCEE | 2.0.6 | test | postgres, lucee |
|
||||
| TEST_STAND/MARIA_DB | 2.0.8 | test | mariadb, lucee, flask |
|
||||
| TEST_STAND/POSTGRES | **5.0.52** | test | postgres + user + database |
|
||||
| TEST_STAND/S3_EVENT_POC | 2.1.23 | — | s3bucket ×2 |
|
||||
| PROD_STAND/PG1 | 2.1.26 | prod | postgres, lucee, nodejs, s3bucket |
|
||||
| PROD_STAND/POSTGRES | 2.1.12 | prod | postgres, lucee, nodejs |
|
||||
| PROD_STAND/RABBIT | 5.0.19 | **test ⚠️** | rabbitmq |
|
||||
| RABBIT/ (корень) | 2.1.10 | prod | rabbitmq, lucee, nodejs, s3bucket |
|
||||
|
||||
**Выводы:**
|
||||
1. Все стенды активны (с реальными ресурсами); часть компонентов закомментирована (VM в PG1, flask в PROD/POSTGRES, http-worker в RABBIT).
|
||||
2. **Сильный разброс версий**: test — 2.0.6…5.0.52, prod — 2.1.10…2.1.26. Каждый стенд пинит свою версию.
|
||||
3. **TEST/POSTGRES не дублирует PROD/POSTGRES**: разные версии (5.0.52 vs 2.1.12), имена (`pg4tf033` vs `pg-tst0`), TEST модульный (только БД+user+database с `var.realm`), PROD связный (БД+lucee+nodejs, хардкод realm). Это разные конфигурации, не дубль.
|
||||
4. **Аномалия**: PROD_STAND/RABBIT/main.tf указывает на **test-endpoint** (`deck-api-test.ngcloud.ru`), хотя лежит в PROD_STAND — вероятно ошибка/недо-миграция.
|
||||
|
||||
**Рекомендация.** Привести версии стендов к двум канонам (одна test, одна prod), исправить endpoint в RABBIT, удалить мёртвые закомментированные `.tf` или вынести в `examples/`.
|
||||
|
||||
---
|
||||
|
||||
## Вопрос 7: auth.k8s.ngcloud.ru / Keycloak / shturval
|
||||
|
||||
**Находка.** Поиск `auth.k8s.ngcloud.ru`, `shturval`, `OIDC`, `kubeconfig` по репо: совпадения **только в самом файле вопросов** 3006_1_questions.md. В коде/конфигах — ничего.
|
||||
- HAR `keycloak.nubes.ru.har` существует локально, но **gitignored** (`HAR/*.har` в .gitignore) — и это Keycloak **`keycloak.nubes.ru`** (для управляемых сервисов Nubes), а **не** `auth.k8s.ngcloud.ru`.
|
||||
- Инструкции по доступу — только HOW_TO_SSH.md (SSH, не kubeconfig/OIDC).
|
||||
- `kubectl`-команды в docs (deploy-dev.sh, ops/*.md) предполагают **готовый** kubeconfig, способ его получения не описан.
|
||||
|
||||
**Вывод.** **Конфигурации OIDC кластера, скриптов обновления токена и инструкций по kubeconfig в репо НЕТ.** `auth.k8s.ngcloud.ru` (realm `shturval`) — внешняя система аутентификации кластера, никак не отражённая в репозитории. HAR относится к другому Keycloak (сервисный, `keycloak.nubes.ru`).
|
||||
|
||||
**Рекомендация.** Если доступ к кластеру через `auth.k8s.ngcloud.ru` нужен регулярно — добавить в secrets (gitignored) инструкцию/скрипт `kubelogin`/OIDC token refresh, по аналогии с `HOW_TO_SSH.md`. Сейчас это «племенное знание» вне репо. *(Детали OIDC-эндпойнта — за пределами репо.)*
|
||||
|
||||
---
|
||||
|
||||
## Вопрос 8: Orphan PVC `storage-check-*` и PostgreSQL 0/0
|
||||
|
||||
**Находка.** Поиск `storage-check` по всему репо: совпадения **только в файле вопросов**. Ни одного `.tf`, манифеста или упоминания в docs/HISTORY, создающего `storage-check-local`/`storage-check-vcd`. Никаких упоминаний проблемных PostgreSQL-тенантов `509145c3`/`dc5db45d`.
|
||||
|
||||
**Вывод.**
|
||||
- `storage-check-*` PVC **не создаются Terraform-конфигами из этого репо**. Имена (`storage-check-local`/`storage-check-vcd` = local/vCD storage classes) характерны для **тестов доступности StorageClass** — это типичные probe-PVC, которые создаёт **облачная платформа/CSI или сам terraform-operator-Job** при проверке хранилища, а не пользовательский TF. Подтвердить, что их делает именно operator, по репо нельзя (код оператора отсутствует в checkout, см. Q5).
|
||||
- PostgreSQL `0/0 28 дней` с висящими PVC/Service — это **suspended/недо-удалённые инстансы**: StatefulSet масштабирован в 0 (suspend), но PVC и Service остаются (платформа не чистит при suspend). Это согласуется с семантикой `suspend_on_destroy` (Q4) и багом 20_destroy_detach_semantics: `state_only`/suspend оставляет ресурсы в облаке.
|
||||
|
||||
**Рекомендация.** Источник `storage-check-*` и orphan-StatefulSet'ов нужно искать **в кластере**: `kubectl get pvc storage-check-local -o yaml` → смотреть `ownerReferences`/`annotations` (кто создатель). Если owner — `terraform-operator` SA или CSI healthcheck, чистить через него. Из репозитория эти ресурсы не управляются. *(Требует доступа к кластеру.)*
|
||||
|
||||
---
|
||||
|
||||
## Сводка
|
||||
|
||||
| # | Вопрос | Ответ из репо? | Главный вывод |
|
||||
|---|---|---|---|
|
||||
| 1 | Версии 53-55 | ✅ Полностью | Из `profiles/*/profile.env`, ручной bump; main.go — fallback. prod=2.1.23, test=5.0.55 |
|
||||
| 2 | Аудит isDeleted | ✅ Полностью | Все критичные функции фильтруют; `findInstanceUidByDisplayNameRefSvc` **уже исправлен** (файл 24 врёт) |
|
||||
| 3 | Gitea 413 | ⚠️ Частично | Конфига nginx Gitea в репо нет — внешняя config кластера giteak8s |
|
||||
| 4 | Suspend-сервисы | ✅ Полностью | 26 suspend+resume, 0 асимметрий, default совпадает идеально |
|
||||
| 5 | Operator health | ⚠️ Частично | `operator/`+`k8s/` отсутствуют в checkout; GPG в кластере фейковый; рестарт = штатный redeploy |
|
||||
| 6 | Стенды | ✅ Полностью | Везде Universal, Legacy не используется; разброс версий; RABBIT prod→test endpoint |
|
||||
| 7 | auth.k8s | ❌ Нет в репо | Ни OIDC, ни kubeconfig-скриптов; HAR — другой Keycloak (nubes.ru) |
|
||||
| 8 | Orphan PVC | ❌ Нет в репо | `storage-check-*` не из TF; orphan PG = suspended-остатки; искать в кластере |
|
||||
|
||||
**Требуют данных вне репо** (честно): Q3 (ingress Gitea в giteak8s), Q5 (логи рестарта оператора, Secret S3/GPG), Q7 (OIDC-эндпойнт auth.k8s), Q8 (`ownerReferences` PVC в кластере).
|
||||
|
||||
Сохранить эти ответы в `HISTORY/OPUS/3006_1.md` или оставить в чате?
|
||||
|
||||
Создано задач: 6
|
||||
@@ -0,0 +1,158 @@
|
||||
Что сказать Opus — кратко
|
||||
Задача: глубокий анализ проекта, найти слабые места, предложить улучшения.
|
||||
|
||||
|
||||
# Трассировка чтения файлов Оpus'ом
|
||||
|
||||
<details><summary>Раскрыть лог чтения</summary>
|
||||
|
||||
docs/50_history/23_*.md (последние баги)
|
||||
Полный промпт: prompt_for_opus48.md
|
||||
|
||||
Список задач обновлен
|
||||
|
||||
Изучу проект. Начну с чтения ключевых файлов и параллельного исследования.
|
||||
|
||||
Searched for files matching `**/ARCHITECTURE.md`, 1 match
|
||||
|
||||
Searched for files matching `**/internal/**/*.go`, 20 matches
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/prompt_for_opus48.md)
|
||||
|
||||
Прочитаю ключевые файлы и запущу параллельное исследование по разным областям.
|
||||
|
||||
Searched for files matching `**/universal_rebuild/internal/core/client.go`, 1 match
|
||||
|
||||
Searched for files matching `**/universal_rebuild/internal/resources_core/crud.go`, 1 match
|
||||
|
||||
Searched for files matching `**/universal_rebuild/tools/gen_v2/generate_resources_v2.go`, 1 match
|
||||
|
||||
Searched for files matching `**/docs/50_history/*.md`, 20 matches
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/devops/ARCHITECTURE.md)
|
||||
|
||||
Прочитаю основные файлы ядра и генератора, параллельно запущу исследование истории багов.
|
||||
|
||||
История багов проекта
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/universal_rebuild/internal/core/client.go#1-1), lines 1 to 450
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/docs/50_history)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/docs/50_history/24_ai_analysis_pipeline_architecture_2026_06_30.md#1-1), lines 1 to 200
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/docs/50_history/23_vapp_uid_inconsistency_displayname_resolve_bug.md)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/docs/50_history/22_adopt_ref_validation_duplicate_detection_5_0_50.md)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/docs/50_history/21_plan_validation_ref_svc_filter_5_0_38.md)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/docs/50_history/20_destroy_detach_semantics_5_0_8.md)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/docs/50_history/16_universal_provider_create_only_params.md)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/docs/50_history/13_universal_flow_param_normalization.md)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/docs/50_history/06_postgres_update_and_immutable_params.md)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/docs/50_history/05_postgres_implementation_and_polling_fixes.md)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/docs/50_history/00_system_mechanics.md)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/docs/50_history/01_initial_operator_setup.md#1-1), lines 1 to 100
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/docs/50_history/02_registry_protocol_fix.md)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/docs/50_history/03_domain_migration_and_hotfix.md#1-1), lines 1 to 80
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/docs/50_history/04_vm_hang_fix_and_500_error.md)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/docs/50_history/07_operation_based_polling_and_negative_testing.md)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/docs/50_history/08_natural_language_infrastructure_ai.md#1-1), lines 1 to 80
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/docs/50_history/09_s3_migration_and_branded_docs.md)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/docs/50_history/10_postgres_modify_tests_and_external_ip.md)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/docs/50_history/12_vm_resource_hardening.md#1-1), lines 1 to 80
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/docs/50_history/11_postgres_import_implementation.md)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/docs/50_history/15_universal_provider_lifecycle_tests.md)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/docs/50_history/17_subresource_generation_rebuild_and_5_0_1_publish.md)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/docs/50_history/18_docs_publish_pipeline_hardening_5_0_4.md)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/docs/50_history/14_universal_provider_separate.md)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/docs/50_history/19_subresource_update_id_fix_and_5_0_7.md)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/docs/50_history/02_tubulus_stabilization.md)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/universal_rebuild/internal/core/client.go#450-450), lines 450 to 900
|
||||
|
||||
Read [](file:///home/naeel/.vscode-server/data/User/workspaceStorage/358cf615179442f06eab7245b983b753/GitHub.copilot-chat/chat-session-resources/2c7367dd-6d7f-48b4-84d0-25bbb06f7cfb/toolu_01Gk8LXuMrG8gDUMRXKhjs2J__vscode-1782817912242/content.txt)
|
||||
|
||||
Прочитаю crud.go (включая `adoptExistingInstanceOnCreate`) и начало генератора, а также тесты.
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/universal_rebuild/internal/resources_core/crud.go)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/universal_rebuild/internal/provider/provider.go#1-1), lines 1 to 120
|
||||
|
||||
Изучу генератор, тесты, транспорт и состояние secrets в gitignore.
|
||||
|
||||
Генератор gen_v2 и тесты
|
||||
|
||||
Searched for files matching `**/.gitignore`, 6 matches
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/universal_rebuild/internal/provider/provider.go#120-120), lines 120 to 178
|
||||
|
||||
Searched for files matching `tf_provider` · `**/*_test.go`, 2 matches
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/universal_rebuild/tools/gen_v2/generate_resources_v2.go#1-1), lines 1 to 100
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/universal_rebuild/internal/resources_core/crud_test.go#1-1), lines 1 to 50
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/universal_rebuild/internal/core/client_test.go)
|
||||
|
||||
Searched for regex `func \(.*\) main\(\)|func.*Generate` (`tf_provider` · `universal_rebuild/tools/gen_v2/generate_resources_v2.go`), no results
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/universal_rebuild/tools/gen_v2/generate_resources_v2.go#100-100), lines 100 to 300
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/universal_rebuild/internal/resources_core/crud_test.go#50-50), lines 50 to 57
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/universal_rebuild/internal/core/client_test.go#46-46), lines 46 to 46
|
||||
|
||||
Searched for text `template.Must|var.*=|const.*=` (`tf_provider` · `universal_rebuild/tools/gen_v2/generate_resources_v2.go`), 8 results
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/universal_rebuild/tools/gen_v2)
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/universal_rebuild/tools/gen_v2/generate_resources_v2.go#300-300), lines 300 to 500
|
||||
|
||||
Searched for regex `func write|func.*Kind|if.*Kind|switch.*op\.Kind` (`tf_provider` · `universal_rebuild/tools/gen_v2/generate_resources_v2.go`), 7 results
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/universal_rebuild/tools/gen_v2/generate_resources_v2.go#500-500), lines 500 to 700
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/universal_rebuild/tools/gen_v2/generate_resources_v2.go#800-800), lines 800 to 900
|
||||
|
||||
Searched for regex `panic|error|yaml\.Unmarshal|unknown|default|fallback` (`tf_provider` · `universal_rebuild/tools/gen_v2/generate_resources_v2.go`), 20 results
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/universal_rebuild/tools/gen_v2/generate_resources_v2.go#850-850), lines 850 to 1200
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/universal_rebuild/internal/resources_core/crud_test.go)
|
||||
|
||||
Searched for regex `` ^const.*Template = |^const.*= ` `` (`tf_provider` · `universal_rebuild/tools/gen_v2/generate_resources_v2.go`), 3 results
|
||||
|
||||
Searched for regex `^func Test` (`tf_provider` · `universal_rebuild/internal/resources_core/crud_test.go`), 3 results
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/universal_rebuild/internal/core/client_test.go)
|
||||
|
||||
Searched for regex ``^`$`` (`tf_provider` · `universal_rebuild/tools/gen_v2/generate_resources_v2.go`), 3 results
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/universal_rebuild/tools/gen_v2/generate_resources_v2.go#1420-1420), lines 1420 to 1430
|
||||
|
||||
Read [](file:///home/naeel/tf_provider/universal_rebuild/tools/gen_v2/generate_resources_v2.go#1865-1865), lines 1865 to 1880
|
||||
|
||||
</details>
|
||||
@@ -0,0 +1,122 @@
|
||||
# Дополнительные вопросы к Opus 4.8
|
||||
|
||||
> **Контекст:** ты уже проанализировал проект (3006_0.md). Ниже — уточняющие вопросы на основе новых данных, полученных после твоего анализа. Всё в режиме Plan — только чтение файлов, никаких правок.
|
||||
|
||||
---
|
||||
|
||||
## Вопрос 1: Расхождение версий — откуда 5.0.55?
|
||||
|
||||
Ты отметил что кодовая база содержит версии 5.0.51 (universal_rebuild/main.go) и 5.0.52 (main.go Legacy). Но **регистр terra.k8c.ru отдаёт 93 версии до 5.0.55**.
|
||||
|
||||
**Задача:** прочитай `devops/profiles/prod/profile.env`, `devops/profiles/test/profile.env`, `devops/03_build_and_upload_provider.sh`. Проверь — откуда брались версии 5.0.53, 5.0.54, 5.0.55? Это ручные билды? Или автоматические из CI? Где в коде хранится текущая версия universal-провайдера (кроме main.go)?
|
||||
|
||||
Связанный вопрос: `HISTORY/OPUS/3006_0.md` — твой анализ — какая версия в нём указана? Соответствует ли она версии на регистре?
|
||||
|
||||
---
|
||||
|
||||
## Вопрос 2: Полный аудит фильтрации isDeleted
|
||||
|
||||
Ты верно заметил что `FindInstanceByDisplayName` починен в 5.0.50, но **все ли** функции, обращающиеся к `/instances`, фильтруют deleted?
|
||||
|
||||
**Задача:** прочитай `universal_rebuild/internal/core/refsvc_resolve.go` и `universal_rebuild/internal/core/client.go`. Составь полный список ВСЕХ функций, которые делают GET-запросы к `/instances` (любым способом). Для каждой проверь:
|
||||
1. Есть ли `isDeleted=false` в URL
|
||||
2. Есть ли проверка `isDeleted` в коде после ответа
|
||||
3. Если нет ни того ни другого — это потенциальный баг класса #21/22/23
|
||||
|
||||
Особое внимание функции `findInstanceUidByDisplayNameRefSvc` — ты её упомянул в анализе. Проверь её сигнатуру и тело.
|
||||
|
||||
---
|
||||
|
||||
## Вопрос 3: Gitea 413 — проблема с пушем
|
||||
|
||||
Мы сегодня обнаружили что `git push` на `gitea-naeel.giteak8s.services.ngcloud.ru` падает с HTTP 413 (nginx `client_max_body_size` = 1 MB). Даже пакет в 2 MB режется.
|
||||
|
||||
**Задача:** проверь в репозитории:
|
||||
- `charts/` — есть ли там Helm-чарт для Gitea? Где конфигурируется nginx?
|
||||
- `k8s/` — есть ли манифесты Gitea с ingress/nginx аннотациями?
|
||||
- `.github/` — есть ли CI-пайплайны, которые деплоят Gitea?
|
||||
- `devops/ci/` — конфигурация CI
|
||||
|
||||
Можно ли увеличить `client_max_body_size` через существующие манифесты/чарты в репо? Или это внешняя конфигурация?
|
||||
|
||||
---
|
||||
|
||||
## Вопрос 4: Suspend-способные сервисы — полный список
|
||||
|
||||
Твой анализ упоминает что `hasSuspend` определяет `suspendOnDestroyDefault`. Но какие именно сервисы поддерживают suspend?
|
||||
|
||||
**Задача:** прочитай `universal_rebuild/resources_yaml/embed.go` и все YAML-файлы в `universal_rebuild/resources_yaml/` (или в `devops/profiles/prod/generated/resources_yaml/` если есть). Составь:
|
||||
|
||||
1. Полный список сервисов, у которых есть операция `kind: instance, action: suspend`
|
||||
2. Полный список сервисов, у которых есть операция `kind: instance, action: resume`
|
||||
3. Есть ли сервисы, где suspend есть, а resume нет (или наоборот)? Это баг?
|
||||
4. Для каждого suspend-способного сервиса проверь: совпадает ли `suspend_on_destroy_default` в YAML с тем что вычисляется в `service_spec_gen` (hasSuspend → true)?
|
||||
|
||||
---
|
||||
|
||||
## Вопрос 5: Terraform Operator — состояние и health
|
||||
|
||||
Мы сегодня проверили кластер `iot-naeel` (через ВМ 5.172.178.213). В namespace `terra` три deployment'а:
|
||||
- `registry-server` — Running 81d
|
||||
- `terraform-operator` — Running 2d (перезапускался!)
|
||||
- `cloud-dashboard` — Running 74d
|
||||
|
||||
**Задача:** прочитай в репозитории:
|
||||
- `k8s/` — манифесты для registry-server и operator'а
|
||||
- `charts/` — Helm-чарты если есть
|
||||
- `operator/` — код оператора
|
||||
- `registry-server-build/` — код registry-сервера
|
||||
|
||||
Ответь:
|
||||
1. Почему `terraform-operator` перезапустился 2 дня назад (30 июня), а остальные 81 день? Он крашился?
|
||||
2. Достаточно ли трёх deployment'ов для полноценного registry? Не хватает ли docs-server'а (судя по `devops/04_build_and_publish_docs.sh`)?
|
||||
3. Где хранятся GPG-ключи для подписи в кластере? Как operator получает доступ к S3?
|
||||
4. Есть ли в репо конфигурация S3-бакета для хранения артефактов?
|
||||
|
||||
---
|
||||
|
||||
## Вопрос 6: Тестовые стенды — что реально используется?
|
||||
|
||||
В репозитории есть:
|
||||
- `TEST_STAND/` — LUCEE, MARIA_DB, POSTGRES, S3_EVENT_FUNCTION_POC
|
||||
- `PROD_STAND/` — PG1, POSTGRES, RABBIT
|
||||
- `RABBIT/` — отдельный тест RabbitMQ
|
||||
|
||||
**Задача:** прочитай `*.tf` файлы в этих директориях и определи:
|
||||
1. Какие стенды реально используются (судя по датам файлов и содержимому)?
|
||||
2. Какой провайдер используется — Legacy (`registry.terraform.io/nubes/nubes`) или Universal (`terra.k8c.ru/nubes/nubes`)?
|
||||
3. Есть ли расхождения между стендами (разные версии провайдера, разные подходы)?
|
||||
4. `TEST_STAND/POSTGRES/` — не дублирует ли он `PROD_STAND/POSTGRES/`?
|
||||
|
||||
---
|
||||
|
||||
## Вопрос 7: auth.k8s.ngcloud.ru — как работает аутентификация?
|
||||
|
||||
Для доступа к кластеру мы используем токен от `auth.k8s.ngcloud.ru` (Keycloak realm `shturval`).
|
||||
|
||||
**Задача:** найди в репозитории любые упоминания `auth.k8s.ngcloud.ru`, `keycloak`, `shturval`. Есть ли:
|
||||
1. Конфигурация OIDC для кластера?
|
||||
2. Скрипты обновления токена?
|
||||
3. Инструкции по получению kubeconfig?
|
||||
4. Связанные HAR-файлы (`HAR/keycloak.nubes.ru.har`)?
|
||||
|
||||
---
|
||||
|
||||
## Вопрос 8: Неиспользуемые PVC и orphan-ресурсы в кластере
|
||||
|
||||
Мы нашли в кластере `iot-naeel`:
|
||||
- `default/storage-check-local` (100Mi) — тестовый PVC без пода
|
||||
- `default/storage-check-vcd` (10Gi) — тестовый PVC без пода
|
||||
- `509145c3-.../postgresqlk8s` — statefulset 0/0 уже 28 дней, но сервисы и PVC висят
|
||||
- `dc5db45d-.../postgresqlk8s` — statefulset 0/0 уже 28 дней, но сервисы и PVC висят
|
||||
|
||||
**Задача:** проверь в репозитории:
|
||||
- Есть ли Terraform-конфиги, создававшие эти PVC (`storage-check-*`)?
|
||||
- Есть ли в `docs/` или `HISTORY/` упоминания о проблемах с PostgreSQL в этих тенантах?
|
||||
- Может ли `terraform-operator` быть причиной появления `storage-check-*` PVC (тестовые ресурсы оператора)?
|
||||
|
||||
---
|
||||
|
||||
## Формат ответа
|
||||
|
||||
На каждый вопрос: **находка → файлы (конкретные строки) → вывод → рекомендация**. Если вопрос нельзя решить только чтением файлов репозитория — так и напиши, что нужно посмотреть за пределами репо (на кластере, в API).
|
||||
Reference in New Issue
Block a user