Проблема: у части сервисов пароль пользователя генерирует платформа и кладёт его в
секрет Vault РОДИТЕЛЬСКОГО инстанса. vault_secrets родителя — Computed и обновляется
только при его Read, поэтому внутри одного apply после create_user пароль недоступен
(Invalid index). Из-за этого в pg/outputs.tf приходилось читать vault_secrets кластера,
а стенду требовались два apply.
Решение: подресурс-пользователь отдаёт пароль СВОИМ выходом сразу после create_user.
- types.go: GenSubresource.VaultUserPassword (признак из данных спека);
- loader.go: признак = подресурс user + у сервиса есть vault-выходы + create_user
принимает username и НЕ принимает password; имя сервиса нигде не проверяется;
- templates/subresource.go: поле модели + Computed/Sensitive атрибут password,
чтение Vault родителя в Create (GetInstanceStateDetails + GetInstanceVaultSecrets)
и перенос уже полученного пароля в Update (чтобы Computed-атрибут не стал unknown);
- resources_core/subresource_user_password.go: ExtractUserPassword — разбор
{"<username>":{"password":"..."}} с безопасным возвратом пустой строки;
- writers: регрессионный тест «фича включена/выключена».
Проверено генерацией и сборкой test-стенда: выход получили 4 подресурса
(postgres_user, kafka_user, clickhouse_user, mongodb_user); mariadb_user НЕ затронут
(там пароль входной); k8s_*_user и vc_org_user не затронуты (пользователь
адресуется не через username). go test ./... — ok, go build — чисто.
Код-ревью (раунд 5), п.1/6: pre-check брал схему из GET /instanceOperations/default/{opId},
а payload строился по живой схеме ?fields=cfsParams — два источника. default может
расходиться с живой => риск ложного пропуска modify.
Решение: pre-check вообще не запрашивает схему.
- modifierDesiredEqualsLive(ctx, uid, desired): сравнение по live-кодам
(live[lower(code)]), единственный источник — state.params.
- Значения: похожи на JSON ({/[) — смысловое сравнение; иначе скалярное с
нормализацией (null/"" -> ""; true/false без учёта регистра) — закрывает и
регистр bool.
- Fail-safe сохранён: пусто/нет кода/ошибка live => modify выполняется.
- Удалены: fetchOperationSchemaByID, modifierValuesEqual, lookupLiveParam больше
не участвует в pre-check (остаётся для досылки).
- Тесты переписаны (RawValuesEqual_Scalars/JSON, DesiredEqualsLive: 4 кейса).
Документация: TOOLS/ARCHITECTURE.md -> новый раздел «Modifier Idempotency»
(5 правил контракта); HISTORY — журнал раунда 5.
Проверено: go build ./... OK; go test ./internal/... -short PASS.
Проблема: CreateGenericInstanceUniversalV6 при ЛЮБОЙ ошибке после создания инстанса
возвращал "", а шаблон Create при ошибке не писал ID в state => облачный инстанс
осиротевал (Terraform о нём не знает, повторный apply упирается в страж дубликатов).
- core/instance_create.go: ошибки после получения instanceUid возвращают uid вместе
с ошибкой (POST /instanceOperations, пустой opUid, разбор cfsParams, resolve,
отправка параметров, validate, run, waitForOperationFinish, ensureInstanceCreated).
До создания uid — по-прежнему "".
- templates/instance.go: при err != nil и id != "" -> data.ID + resp.State.Set (partial
state), затем AddError.
- client_test.go: TestCreateGenericInstance_KeepsUIDWhenOperationCreateFails,
TestCreateGenericInstance_EmptyUIDWhenInstanceCreateFails.
- ARCHITECTURE.md: пункт про partial state.
Проверено: 02 (dev) + dev-materialize -> 40 файлов resources_gen содержат фикс;
go build ./... OK; go test ./internal/... -short PASS.
- core/refsvc.go: комментарий ссылался на несуществующий блок 'Restore user-provided
casing' в instance.go. Факт: ref_svc исключены из read-back (InputField только при
RefSvcId==0), UUID внутри JSON нормализуются при отправке (BuildJSON ->
LowercaseUUIDsInText).
- docs/60_strategy/terraform_case_sensitivity_fix.md: §4 помечен как историческая
справка (подхода originalVappUid в коде нет); актуальные §10-§11.
Проверено: go build ./... OK; go test ./internal/... -short -> PASS.
Проблема: modifierDesiredEqualsCurrent сравнивает desired с paramValue из cfsParams
(дефолт ФОРМЫ операции), а не с состоянием инстанса. Пропуск modify на такой основе
может быть ложным (HAR/edge_.har: needEnableAVI paramValue=false при live=true).
- core/modifier_compare.go: добавлен modifierDesiredEqualsLive (источник —
instanceLiveParams/state.params; неопределённость => не пропускаем) и хелперы
modifierValuesEqual / modifierCodeMap; modifierDesiredEqualsCurrent переведён на них.
- core/operation_run_bycode.go: idempotent-путь использует live-pre-check; при ошибке
чтения live modify НЕ пропускается.
- resources_core/nsxt_snat_resource.go: setSnat -> RunInstanceOperationUniversalByIdempotent
(лишний modify на повторном apply больше не отправляется).
- modifier_compare_test.go: TestModifierDesiredEqualsLive (совпало/отличается/live недоступен).
Проверено: go build ./... OK; go test ./internal/core/... -short -> PASS.
isRetryable (core/http.go): добавлен StatusUnauthorized (401) к {429,502,503,504}.
Gateway может временно отклонять валидный JWT — без этого одиночный 401 ронял
read/plan/поллинг.
- client_test.go: юнит-тест TestIsRetryable (401/429/502/503/504 = true; 400/403/404/500 = false).
- ARCHITECTURE.md: раздел API Resilience приведён к фактическому поведению
(401 реализован; POST не ретраится намеренно).
Проверено: go build ./... OK; go test ./internal/core/... -short OK.
- resources_core.BuildJSON оборачивает результат в jsonutil.LowercaseUUIDsInText:
платформа сравнивает регистр UUID при create, а ресурсы отдают id в UPPERCASE
(nsxtUid/vdcUid) -> без нормализации create Штурвала падал 'Edge не развёрнут
в указанном vDC' (обнаружено на провайдере 2.0.23 из-под Windows).
- Одна точка покрывает все map-fixed-параметры (create/modify/redeploy),
регенерация не требуется.
- Документация: HISTORY/2026-09-30, docs/60_strategy/terraform_case_sensitivity_fix.md §11,
NOTES/30_analysis/ARCHITECTURE_NEW.md §6.5, docs/help/architecture-and-methods.md §7.
Не выпущено: версия не поднималась, релиз/регенерация не выполнялись.
- баннеры «ЛОЖНЫЙ ПУТЬ — ОТМЕНЕНО» на 4 файла HISTORY/OPUS/2026-09-22_modifier_* и docs/60_strategy/modifier_resources_ideology_and_specification.md
- vIPConfigure: replace-семантика, НЕ накопительная (по тесту docs/ORG_IP_MODIFIER_TEST_2026-09-22.md)
- обновлены ссылки на перенесённые материалы (docs/... -> NOTES/..., HOW_TO/...)
- In provider/internal/core/client.go:
- If GET /instanceOperations/{opUid}?fields=cfsParams fails with 500 or JSON unmarshal error,
check whether provided parameters contain unresolved names via hasUnresolvedParams.
- If all parameters are already resolved (UUIDs/numbers/booleans/JSON), proceed to send
parameters via POST /instanceOperationCfsParams without hard-failing.
- If unresolved names remain, fail immediately with original error.
- Bumped DEV provider version to 2.0.2 in TOOLS/config/dev/profile.env, VERSIONS.md, and DEV_STAND/FullPipe/versions.tf.
- Built, signed, and published provider 2.0.2 to DEV registry bucket nubes-terraform-registry.
- stages always visible (remove log_level gating)
- fix tty resource leak (move out of poll loop)
- show current stage [..] in addition to completed [OK]/[FAIL]
- sync services_list.txt for all 3 stands with live UI
- add YAML vs API comparison script
- bump TEST version 5.0.1 -> 5.0.2