From a0113586605d0cfae48a73c2092426e2eb60d789 Mon Sep 17 00:00:00 2001 From: Repinoid Date: Tue, 22 Sep 2026 22:04:09 +0300 Subject: [PATCH] =?UTF-8?q?fix(core):=20=D0=B4=D0=BE=D1=81=D1=8B=D0=BB?= =?UTF-8?q?=D0=B0=D1=82=D1=8C=20=D0=BD=D0=B5=D0=B7=D0=B0=D0=B4=D0=B0=D0=BD?= =?UTF-8?q?=D0=BD=D1=8B=D0=B5=20modify-params=20=D0=B8=D0=B7=20live=20stat?= =?UTF-8?q?e.params=20(=D0=B0=20=D0=BD=D0=B5=20paramValue=20=D1=84=D0=BE?= =?UTF-8?q?=D1=80=D0=BC=D1=8B)=20=E2=80=94=20=D1=83=D1=81=D1=82=D1=80?= =?UTF-8?q?=D0=B0=D0=BD=D1=8F=D0=B5=D1=82=20=D1=81=D0=B1=D1=80=D0=BE=D1=81?= =?UTF-8?q?=20needEnableAVI/ALB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TOOLS/config/dev/profile.env | 2 +- provider/internal/core/operation_cfs.go | 41 +++++++++++++++++++ provider/internal/core/operation_run.go | 15 ++++--- .../internal/core/operation_run_bycode.go | 35 +++++++++------- 4 files changed, 72 insertions(+), 21 deletions(-) diff --git a/TOOLS/config/dev/profile.env b/TOOLS/config/dev/profile.env index 1e0eb75..6b1d377 100644 --- a/TOOLS/config/dev/profile.env +++ b/TOOLS/config/dev/profile.env @@ -4,7 +4,7 @@ TOKEN_FILE="secrets/dev.token" # Release versions # Version -VERSION="2.0.13" +VERSION="2.0.14" NAMESPACE="nubes-dev" PROVIDER_NAME="nubes" diff --git a/provider/internal/core/operation_cfs.go b/provider/internal/core/operation_cfs.go index eb8840b..53b422b 100644 --- a/provider/internal/core/operation_cfs.go +++ b/provider/internal/core/operation_cfs.go @@ -4,8 +4,49 @@ import ( "context" "encoding/json" "fmt" + "strings" ) +// instanceLiveParams возвращает ТЕКУЩИЕ params инстанса (GET /instances/{uid} → state.params), +// keyed by lower(code). Это единственный достоверный источник live-значений. +// +// ВАЖНО: paramValue из ответа GET /instanceOperations/{opUid}?fields=cfsParams — это +// дефолт ФОРМЫ операции, а НЕ состояние инстанса. Пример из HAR/edge_.har: +// для needEnableAVI в cfsParams paramValue="false", тогда как live-значение инстанса +// needEnableAVI=true. Досылка paramValue как live стирала ALB (reset-to-default). +func (c *UniversalClient) instanceLiveParams(ctx context.Context, instanceUid string) map[string]string { + live := map[string]string{} + params, err := c.GetInstanceStateParams(ctx, instanceUid) + if err != nil { + return live + } + for k, v := range params { + key := strings.ToLower(strings.TrimSpace(k)) + if key == "" { + continue + } + live[key] = v + } + return live +} + +// lookupLiveParam ищет live-значение параметра инстанса по возможным ключам cfsParam. +func lookupLiveParam(live map[string]string, p universalCfsParam) (string, bool) { + if len(live) == 0 { + return "", false + } + for _, cand := range []string{p.Code, p.SvcOperationCfsParam, p.Name, p.Label} { + key := strings.ToLower(strings.TrimSpace(cand)) + if key == "" { + continue + } + if v, ok := live[key]; ok && strings.TrimSpace(v) != "" { + return v, true + } + } + return "", false +} + // fetchOperationCfsParams возвращает cfsParams операции. При неудаче живого // GET /instanceOperations/{opUid}?fields=cfsParams (backend 500 в // getResourceRealmConfig на проблемных инстансах) берёт схему из diff --git a/provider/internal/core/operation_run.go b/provider/internal/core/operation_run.go index 9255d47..ad350cf 100644 --- a/provider/internal/core/operation_run.go +++ b/provider/internal/core/operation_run.go @@ -135,16 +135,21 @@ func (c *UniversalClient) RunInstanceOperationUniversalWithDefaults(ctx context. sent[paramId] = true } + live := c.instanceLiveParams(ctx, instanceUid) for _, param := range cfsParams { if sent[param.SvcOperationCfsParamId] { continue } - val := "" - if param.ParamValue != nil { - val = *param.ParamValue - } else if param.DefaultValue != nil { - val = *param.DefaultValue + // Приоритет: live state.params инстанса → paramValue операции → defaultValue. + // paramValue из cfsParams — дефолт формы, не состояние инстанса (см. operation_cfs.go). + val, hasLive := lookupLiveParam(live, param) + if !hasLive { + if param.ParamValue != nil { + val = *param.ParamValue + } else if param.DefaultValue != nil { + val = *param.DefaultValue + } } val = normalizeUniversalValueV6(val, param) diff --git a/provider/internal/core/operation_run_bycode.go b/provider/internal/core/operation_run_bycode.go index 9b7cd3e..3fa04d2 100644 --- a/provider/internal/core/operation_run_bycode.go +++ b/provider/internal/core/operation_run_bycode.go @@ -105,26 +105,31 @@ func (c *UniversalClient) runInstanceOperationByCode(ctx context.Context, instan sent[paramId] = true } + live := c.instanceLiveParams(ctx, instanceUid) for _, param := range cfsParams { if sent[param.SvcOperationCfsParamId] { continue } - // Дозаполняем ВСЕ незаданные параметры их текущим live-значением - // (ParamValue из GET ?fields=cfsParams), а не только required. - // Бэкенд для modify трактует пропущенный/null как reset-to-default, - // из-за чего частичный payload затирал create-поля (см. prompt_for_opus_modifier_null_bug.md). - // Поведение выровнено под RunInstanceOperationUniversalWithDefaults. - // НО: если ни live-значения, ни дефолта НЕТ — пропускаем (не шлём синтетический + // Дозаполняем ВСЕ незаданные параметры, чтобы бэкенд modify не трактовал + // пропущенный/null как reset-to-default (иначе частичный payload затирает + // create-поля, см. prompt_for_opus_modifier_null_bug.md). + // + // ПРИОРИТЕТ ИСТОЧНИКА: live state.params инстанса → paramValue операции → defaultValue. + // КРИТИЧНО: paramValue из ?fields=cfsParams — дефолт ФОРМЫ операции, не состояние + // инстанса (HAR/edge_.har: needEnableAVI paramValue="false" при live=true → стирало ALB). + // Если ни live, ни paramValue, ни defaultValue НЕТ — пропускаем (не шлём синтетический // "0"/"false"/"[]", который может нарушить constraint "integer > 0"). - if (param.ParamValue == nil || strings.TrimSpace(*param.ParamValue) == "") && - (param.DefaultValue == nil || strings.TrimSpace(*param.DefaultValue) == "") { - continue - } - val := "" - if param.ParamValue != nil { - val = *param.ParamValue - } else if param.DefaultValue != nil { - val = *param.DefaultValue + val, hasLive := lookupLiveParam(live, param) + if !hasLive { + if (param.ParamValue == nil || strings.TrimSpace(*param.ParamValue) == "") && + (param.DefaultValue == nil || strings.TrimSpace(*param.DefaultValue) == "") { + continue + } + if param.ParamValue != nil { + val = *param.ParamValue + } else if param.DefaultValue != nil { + val = *param.DefaultValue + } } val = normalizeUniversalValueV6(val, param)