diff --git a/DEV_STAND/FullPipe/versions.tf b/DEV_STAND/FullPipe/versions.tf index f28cb12..1698b11 100644 --- a/DEV_STAND/FullPipe/versions.tf +++ b/DEV_STAND/FullPipe/versions.tf @@ -4,7 +4,7 @@ terraform { required_providers { nubes = { source = "tf-registry.containerk8s.services.ngcloud.ru/nubes-dev/nubes" - version = "2.0.1" + version = "2.0.2" } } } diff --git a/TOOLS/config/dev/profile.env b/TOOLS/config/dev/profile.env index 4e41d29..e496756 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.0" +VERSION="2.0.2" NAMESPACE="nubes-dev" PROVIDER_NAME="nubes" diff --git a/VERSIONS.md b/VERSIONS.md index 148808d..708ed62 100644 --- a/VERSIONS.md +++ b/VERSIONS.md @@ -5,7 +5,7 @@ | Стенд | Namespace | Версия | Дата заливки | |---|---|---|---| | PROD | `nubes` | `1.0.0` | 2026-09-03 | (новая нумерация) | -| DEV | `nubes-dev` | `2.0.1` | 2026-09-20 | (добавлены parent modify/modifier ресурсы) | +| DEV | `nubes-dev` | `2.0.2` | 2026-09-21 | (graceful fallback для cfsParams при 500) | | TEST | `nubes-test` | `3.0.0` | 2026-09-03 | (новая нумерация) | ## Как проверить diff --git a/provider/internal/core/client.go b/provider/internal/core/client.go index 6e5c9b2..94c1986 100644 --- a/provider/internal/core/client.go +++ b/provider/internal/core/client.go @@ -270,17 +270,22 @@ func (c *UniversalClient) CreateGenericInstanceUniversalV6(ctx context.Context, } opDetailsResp, _, err := c.doRequest(ctx, "GET", fmt.Sprintf("/instanceOperations/%s?fields=cfsParams", opUid), nil) - if err != nil { - return "", fmt.Errorf("не удалось получить детали операции: %w", err) - } var opDetails universalOpResponse - if err := json.Unmarshal(opDetailsResp, &opDetails); err != nil { - return "", fmt.Errorf("не удалось разобрать детали операции: %w", err) - } - - params, err = c.resolveRefSvcParamValues(ctx, opDetails.InstanceOperation.CfsParams, params) + hasOpDetails := false if err != nil { - return "", err + if hasUnresolvedParams(params) { + return "", fmt.Errorf("не удалось получить детали операции: %w", err) + } + } else if jsonErr := json.Unmarshal(opDetailsResp, &opDetails); jsonErr != nil { + if hasUnresolvedParams(params) { + return "", fmt.Errorf("не удалось разобрать детали операции: %w", jsonErr) + } + } else { + hasOpDetails = true + params, err = c.resolveRefSvcParamValues(ctx, opDetails.InstanceOperation.CfsParams, params) + if err != nil { + return "", err + } } sent := make(map[int]bool) @@ -297,30 +302,32 @@ func (c *UniversalClient) CreateGenericInstanceUniversalV6(ctx context.Context, sent[paramId] = true } - for _, param := range opDetails.InstanceOperation.CfsParams { - if sent[param.SvcOperationCfsParamId] { - continue - } - if !param.IsRequired { - continue - } + if hasOpDetails { + for _, param := range opDetails.InstanceOperation.CfsParams { + if sent[param.SvcOperationCfsParamId] { + continue + } + if !param.IsRequired { + continue + } - val := "" - if param.ParamValue != nil { - val = *param.ParamValue - } else if param.DefaultValue != nil { - val = *param.DefaultValue - } - val = normalizeUniversalValueV6(val, param) + val := "" + if param.ParamValue != nil { + val = *param.ParamValue + } else if param.DefaultValue != nil { + val = *param.DefaultValue + } + val = normalizeUniversalValueV6(val, param) - pPayload := genericParamReq{ - InstanceOperationUid: opUid, - SvcOperationCfsParamId: param.SvcOperationCfsParamId, - ParamValue: val, - } - _, _, err := c.doRequest(ctx, "POST", "/instanceOperationCfsParams", pPayload) - if err != nil { - return "", fmt.Errorf("не удалось отправить параметр по умолчанию %d: %w", param.SvcOperationCfsParamId, err) + pPayload := genericParamReq{ + InstanceOperationUid: opUid, + SvcOperationCfsParamId: param.SvcOperationCfsParamId, + ParamValue: val, + } + _, _, err := c.doRequest(ctx, "POST", "/instanceOperationCfsParams", pPayload) + if err != nil { + return "", fmt.Errorf("не удалось отправить параметр по умолчанию %d: %w", param.SvcOperationCfsParamId, err) + } } } @@ -1352,6 +1359,41 @@ func isUUIDLike(value string) bool { return true } +// hasUnresolvedParams возвращает true, если хотя бы одно значение параметров похоже на +// неразрешённое имя вместо UUID. Значения map-fixed/json/array, числа и булевы пропускаются. +func hasUnresolvedParams(params map[int]string) bool { + for _, val := range params { + trimmed := strings.TrimSpace(val) + if trimmed == "" || trimmed == "{}" || trimmed == "[]" { + continue + } + if strings.HasPrefix(trimmed, "{") || strings.HasPrefix(trimmed, "[") { + // json/map-fixed/array не являются именами одиночных инстансов + continue + } + if trimmed == "true" || trimmed == "false" { + continue + } + isNum := true + for _, r := range trimmed { + if r < '0' || r > '9' { + isNum = false + break + } + } + if isNum { + continue + } + if isUUIDLike(trimmed) { + continue + } + // Обнаружена не-числовая строка, не JSON и не UUID (потенциальное имя). + // Если GET ?fields=cfsParams упал, такое имя невозможно разрешить в UUID. + return true + } + return false +} + func isHexDigit(r rune) bool { return (r >= '0' && r <= '9') || (r >= 'a' && r <= 'f') || (r >= 'A' && r <= 'F') }