diff --git a/HISTORY/OPUS/2026-09-22_modifier_null_reset_bug.md b/HISTORY/OPUS/2026-09-22_modifier_null_reset_bug.md new file mode 100644 index 0000000..64591f0 --- /dev/null +++ b/HISTORY/OPUS/2026-09-22_modifier_null_reset_bug.md @@ -0,0 +1,55 @@ +# Opus-разбор: modify-модификатор сбрасывает create-поля в дефолт — 2026-09-22 + +Источник: ответ Opus на `prompt_for_opus_modifier_null_bug.md`. + +## Симптом + +`nubes_vc_nsxt_network` (modifier vc_nsxt.network, modify 111) после create Edge с +`needEnableAVI=true`, `virtualServicesCount=3` сбрасывал `needEnableAVI` на платформе +обратно в `false`. + +## Корень бага (подтверждено cfsParams операций) + +Два пути отправки modify ведут себя по-разному: + +- **Generic modify** (`UpdateResource` → `RunInstanceOperationUniversalWithDefaults`, + `client.go:493`) — в цикле дозаполнения шлёт **все** незаданные cfsParams их текущим + `ParamValue` (или `DefaultValue`) — безусловно. +- **Модификатор** (`RunOperationByCodeWithTimeout` → `RunInstanceOperationUniversalByCode`, + `client.go:1518`) — в аналогичном цикле стоял guard `if !param.IsRequired { continue }`, + который пропускал опциональные параметры. + +`needEnableAVI` — опциональный параметр modify 111 и не входит в `SchemaParams` модификатора +`vc_nsxt.network` (там только SNAT/routedNetConfiguration). Итог: +1. модификатор его не шлёт (не его поле); +2. back-fill его пропускает (`IsRequired == false`); +3. бэкенд видит отсутствующий параметр → трактует как reset-to-default → `false`. + +`CompactParams` тут ни при чём для `needEnableAVI` — параметр вообще не был в payload модификатора. + +## Ответы Opus + +1. **Полный или частичный payload?** Канон — полный: все параметры операции, незаданные + дозаполняются текущим live-значением (`ParamValue`). Бэкенд для modify трактует + пропущенный/null как reset-to-default, поэтому частичный payload обязан затирать create-поля. +2. **Где чинить?** В `RunInstanceOperationUniversalByCode` — убрать `IsRequired`-guard в цикле + дозаполнения (стало: слать ВСЕ незаданные params их live-значением, как в `WithDefaults`). + - НЕ в `CompactParams` (он не видит полный набор cfsParams, только поля модификатора). + - НЕ в шаблоне генератора (шаблон тоже не знает полного набора). +3. **Риск для vc_org.ip_space:** основной live-путь безопасен (досылка идёт **текущим** значением, + не хардкод-дефолтом). На fallback-пути `/instanceOperations/default/{opId}` `ParamValue` пуст — + есть только `DefaultValue`; но тот же риск уже несёт `WithDefaults`, новой регрессии нет. + +## Внесённый фикс + +`provider/internal/core/client.go` — `RunInstanceOperationUniversalByCode`, цикл дозаполнения: +убраны `if !param.IsRequired { continue }` и `if !param.IsRequired && val == "" { continue }`. +Теперь все незаданные параметры modify досылаются их live-значением (или default). + +Коммит: `c420ea0`. + +## Примечание + +Костыль в `DEV_STAND/FullPipe/edge_network.tf` (явная передача ALB/VS/qos в модификаторе) +после фикса ядра становится избыточным, но не вреден. После пересборки провайдера можно +убрать эти три поля из `edge_network.tf` — досылка теперь происходит автоматически.