diff --git a/HISTORY/2026-09-21_fullpipe_vdc_nsxt_and_refsvc_fixes.md b/HISTORY/2026-09-21_fullpipe_vdc_nsxt_and_refsvc_fixes.md new file mode 100644 index 0000000..4a6252a --- /dev/null +++ b/HISTORY/2026-09-21_fullpipe_vdc_nsxt_and_refsvc_fixes.md @@ -0,0 +1,219 @@ +# 2026-09-21 — FullPipe (vDC + Edge): серия фиксов генератора и провайдера + +## Контекст + +Поднимался полный стенд `DEV_STAND/FullPipe` (целевой пайплайн: Организация → vDC → Edge), +заливался провайдер в реестр (`nubes-dev/nubes`). По ходу вылезла цепочка багов — +в генераторе ресурсов, в сгенерированном коде и в docs-генераторе. + +Версия на выходе: **2.0.5** (DEV, namespace `nubes-dev`). + +--- + +## Баг 1. Непересобираемый генератор (stale binary) — устранён ранее в этот же день + +**Симптом:** `kind: modifier` в YAML не поддерживался; генерация YAML падала. + +**Причина:** `02_generate_resources_and_docs_v2.sh` пересобирал `resource-generator` +только по `mtime`. Лежавший в `TOOLS/resource-generator/bin/resource-generator` +устаревший бинарь затенял исходники. + +**Фикс:** +- генераторы (`resource-generator`, `docs-generator`) пересобираются **всегда** из исходников; +- устаревший бинарник удалён; `TOOLS/resource-generator/bin/` добавлен в `.gitignore`; +- обновлены `README.md`, `TOOLS/README.md`. +- Коммит: `7ecd2aa Fix generator rebuild and release pipeline`. + +--- + +## Баг 2. `declared and not used: resolvedKafkaUid` — сборка падала + +**Симптомы (сборка из сгенерированного кода):** +``` +internal/resources_gen/119_akhq_resource.go:246:2: declared and not used: resolvedKafkaUid +internal/resources_gen/111_dnsrecord_resource.go:248:2: declared and not used: resolvedZoneUid +internal/resources_gen/21_vc_vdc_resource.go:244:2: declared and not used: resolvedOrganizationUid +... (и ещё по всем ресурсам с refSvc в create) +``` + +**Причина:** в шаблоне `TOOLS/resource-generator/internal/templates/instance.go`: +- блок объявления резолва шёл по `{{range .SchemaParams}}` — т.е. объявлял `resolvedX` + для **всех** refSvc-полей; +- а мапа `params` в `Create` НЕ содержала refSvc-условия и писала сырое `data.X`. + +Итог: `resolvedX` объявлен, но нигде не использован → ошибка компиляции. + +**Фикс (шаблон `instance.go`, `subresource.go`):** +- циклы резолва переведены на `{{range .CreateParams}}` / `{{range .ModifyParams}}`; +- в мапу `params` в `Create` добавлено refSvc-условие: + ``` + {{.ID}}: resolved{{ToCamel .Code}}, // в API уходит UUID + {{else}} data.X // сырое значение + ``` +- Коммит: `2286d34`. + +--- + +## Баг 3. `terraform destroy` падал: «РЕСУРС С ТАКИМ ИМЕНЕМ УЖЕ СУЩЕСТВУЕТ (RUNNING)» + +**Симптом:** +``` +terraform destroy +nubes_vc_vdc.vdc: Refreshing state... [id=...] +╷ Error: РЕСУРС С ТАКИМ ИМЕНЕМ УЖЕ СУЩЕСТВУЕТ (RUNNING) +``` + +**Причина:** `ModifyPlan` сгенерированного ресурса на **destroy-плане** запускал +create-time проверку существования/adopt (`PlanExistingResourceDiagnostics...` → +`FindInstanceByDisplayName`). Гварды `config == nil` и +`State.Raw.IsNull() && Plan.Raw.IsNull()` destroy не отсекали (config ненулевой — +блок ресурса ещё в `.tf`; а в destroy-плане state есть, plan = null). + +Debug-подтверждение: `/tmp/nubes_find_debug.log` → +`PlanExistingResourceDiagnostics entered: serviceId=21 name="fullpipe-vdc" adopt=false`. + +**Обходной путь (временный):** `adopt_existing_on_create=true` — но это «телега впереди +лошади»: destroy не должен зависеть от adopt. + +**Фикс (шаблон `instance.go`, `ModifyPlan`):** добавить destroy-guard +``` +if req.Plan.Raw.IsNull() { return } +``` +Теперь destroy-план не запускает create-time проверку и доходит до `Delete`, +который по `suspend_on_destroy=true` отправляет `suspend`. + +Логика suspend уже была в `Delete`: `deleteMode := "state_only"` → `"suspend"`. + +- Коммит: `2286d34`. + +--- + +## Баг 4. `Provider produced inconsistent result after apply`: `.organization_uid` было `"kontora"`, стало UUID + +**Симптом:** +``` +.provider produced an unexpected new value: .organization_uid: +was cty.StringVal("kontora"), but now cty.StringVal("ec4d3a6a-...") +``` + +**Причина:** refSvc-поле резолвилось и **записывалось обратно в state**, из-за чего +state (UUID) не совпадал с plan (user input). + +**Фикс (универсальный, все сервисы):** +- резолв идёт только в локальную переменную `resolvedX`; в state остаётся ровно то, + что ввёл пользователь (имя ИЛИ UUID); +- refresh исключает refSvc-поля (`{{if eq .RefSvcId 0}}`) — не перезаписывает ввод; +- `ResolveRefSvcParamValue` принимает имя (→ UUID) и UUID (→ lowercase); + обратный маппинг `ResolveRefSvcParamDisplayName` для refresh. +- Коммит: `2286d34`. + +--- + +## Баг 5. `Provider returned invalid result object after apply`: `vdc_group_uid` остался unknown + +**Симптом (создание Edge):** +``` +Error: Provider returned invalid result object after apply +After the apply operation, the provider still indicated an unknown value for +nubes_vc_nsxt.edge.vdc_group_uid. +``` + +**Причина:** в схеме refSvc-поля были `Optional: true, Computed: true` **без дефолта** +(строка шаблона: `{{- else if or .IsJson (gt .RefSvcId 0) }}Computed: true,{{- end }}`). +Если пользователь поле не задавал (например, `vdc_group_uid` при `vdc_type="vdc"`), +Terraform планировал его как **unknown** и требовал от провайдера известное значение. +Провайдер его не вычисляет (по дизайну хранит ввод юзера) → остаётся unknown → ошибка. + +`Computed: true` — рудимент **старого** дизайна (когда провайдер писал резолвленный UUID +в state). После перехода на «храним ввод юзера» он стал вредным. + +**Фикс (оба шаблона: `instance.go`, `subresource.go`):** +``` +- {{- else if or .IsJson (gt .RefSvcId 0) }}Computed: true,{{- end }} ++ {{- else if .IsJson }}Computed: true,{{- end }} +``` +Теперь незаданный refSvc = `null` (известное значение). `IsJson` оставлен Computed +намеренно (нужно для нормализации JSON из API). + +Проверено: в сгенерированном `22_vc_nsxt_resource.go` → +`"vdc_group_uid": schema.StringAttribute{Optional: true, ...}` (без `Computed`). + +- Коммит: `bffe3d9`. + +--- + +## Баг 6. docs-generator: вложенный `map-fixed` рендерится как блок (НЕ исправлено → TODO) + +Пример в сгенерированной доке (`generated/dev/docs/vc_nsxt_example.md`) рисует +`routed_net_configuration` **блоком**, но схема — `SingleNestedAttribute`, значит нужен +аргумент `= { ... }`. Копирование примера → `terraform validate` падает: +`Unsupported block type`. + +Виноват `TOOLS/docs-generator/internal/writers/writers.go` → `formatParamOrBlock` +(~стр. 715). Подробности — `docs/TODO/docs_generator_nested_attr_syntax.md`. +Коммит: `92e04da`. + +--- + +## Баг 7. FullPipe: дефолт `vdc_storage_config = "fast"` + +**Симптом:** дефолт в `variables.tf` — `[{"name":"fast","size":200}]`. +Имя политики берётся из ресурсного пула (`getKeyListFromStruct(...providerVdcs[...].storage)`), +и `fast` в окружении не существует. + +**История (по git):** `fast` появился в первом коммите стенда `7ff98f8` — причём их было +**два**: `vdc_provider_vdc = "fast-2.8"` и `vdc_storage_config = "fast"`. Коммит +`7d44697` («Fix FullPipe VDC example placeholders») поправил только `provider_vdc` +(`"fast-2.8"` → `null`), а `storage_config` не тронул. Так что «опять fast» — это +незакрытый второй хвост, а не откат. + +**Фикс:** дефолт → `[{"name":"SATA","size":"200"}]` (совпадает с рабочим `terraform.tfvars`). +Коммит: `d608fba`. + +--- + +## Добавлено в стенд FullPipe + +- `DEV_STAND/FullPipe/edge.tf` — ресурс `nubes_vc_nsxt.edge` (create), + `vdc_uid = nubes_vc_vdc.vdc.id` (Edge создаётся после vDC), + `routed_net_configuration = { ... }` (аргумент, не блок — см. Баг 6). +- переменные `nsxt_*` в `variables.tf`, outputs `nsxt_*` в `outputs.tf`, + пример в `terraform.tfvars.example`. +- `versions.tf` → провайдер `2.0.4` (затем `2.0.5`). +- Коммит: `d608fba`. + +--- + +## Изменённые файлы (генератор) + +| Файл | Что | +|------|-----| +| `TOOLS/resource-generator/internal/templates/instance.go` | destroy-guard в `ModifyPlan`; резолв refSvc по `.CreateParams`; refSvc-условие в мапе `params`; refSvc без `Computed` | +| `TOOLS/resource-generator/internal/templates/subresource.go` | резолв по `.CreateParams`/`.ModifyParams`; refSvc без `Computed` | +| `TOOLS/scripts/02_generate_resources_and_docs_v2.sh` | детерминированная пересборка генераторов | +| `.gitignore`, `README.md`, `TOOLS/README.md` | игнор бинарника, доки | + +## Версии + +| Стенд | Namespace | Версия | +|---|---|---| +| DEV | `nubes-dev` | `2.0.5` | + +## Коммиты сессии (master) + +``` +bffe3d9 fix(generator): refSvc-поля без Computed (unset = null, а не unknown) +92e04da docs(TODO): баг docs-generator - вложенный map-fixed как блок вместо = {} +d608fba stand(FullPipe): vc_nsxt (edge.tf), storage_config fast->SATA, provider 2.0.4 +1401003 release(dev): 2.0.4 +2286d34 fix(generator): destroy-guard в ModifyPlan + универсальный refSvc (имя или UUID) +7ecd2aa Fix generator rebuild and release pipeline +``` + +## Открытые вопросы + +- [ ] docs-generator: `map-fixed` → `= { ... }`, `array-map-fixed` → JSON/jsonencode + (см. `docs/TODO/docs_generator_nested_attr_syntax.md`). +- [ ] Проверить `IsJson`-поля без дефолта: тот же класс unknown-after-apply? (не воспроизводилось). +- [ ] `fast` в тест-фикстуре `provider/internal/core/client_test.go:287` и спек-доке + `docs/60_strategy/...:158` — не трогали.