fix(core): idempotency pre-check сравнивает с live, а не с paramValue формы

Проблема: 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.
This commit is contained in:
Repinoid
2026-09-30 20:35:09 +03:00
parent 383f8ea321
commit ea75cac1a8
4 changed files with 139 additions and 36 deletions
@@ -250,9 +250,10 @@ func (r *NsxtSnatResource) setSnat(ctx context.Context, nsxtUID types.String, ip
unlock := r.client.LockInstance(uid)
defer unlock()
// ByCode, а не ByIdempotent: idempotency-сравнение идёт с paramValue ФОРМЫ операции,
// а не с live-состоянием инстанса — можно ложно пропустить modify.
return r.client.RunInstanceOperationUniversalByCode(ctx, uid, "modify", map[string]string{
// Idempotent: core.RunInstanceOperationUniversalByIdempotent выполняет pre-check
// по LIVE-значениям инстанса (state.params) и пропускает modify, если
// ipSpaceName уже совпадает — повторный apply не дёргает платформу зря.
return r.client.RunInstanceOperationUniversalByIdempotent(ctx, uid, "modify", map[string]string{
"ipSpaceName": value,
})
}