fix(core): graceful fallback for cfsParams 500 error in createInstanceWithContext

- 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.
This commit is contained in:
Repinoid
2026-09-21 09:59:15 +03:00
parent 7ff98f8edc
commit 308f92bc37
4 changed files with 76 additions and 34 deletions
+1 -1
View File
@@ -4,7 +4,7 @@ terraform {
required_providers { required_providers {
nubes = { nubes = {
source = "tf-registry.containerk8s.services.ngcloud.ru/nubes-dev/nubes" source = "tf-registry.containerk8s.services.ngcloud.ru/nubes-dev/nubes"
version = "2.0.1" version = "2.0.2"
} }
} }
} }
+1 -1
View File
@@ -4,7 +4,7 @@ TOKEN_FILE="secrets/dev.token"
# Release versions # Release versions
# Version # Version
VERSION="2.0.0" VERSION="2.0.2"
NAMESPACE="nubes-dev" NAMESPACE="nubes-dev"
PROVIDER_NAME="nubes" PROVIDER_NAME="nubes"
+1 -1
View File
@@ -5,7 +5,7 @@
| Стенд | Namespace | Версия | Дата заливки | | Стенд | Namespace | Версия | Дата заливки |
|---|---|---|---| |---|---|---|---|
| PROD | `nubes` | `1.0.0` | 2026-09-03 | (новая нумерация) | | 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 | (новая нумерация) | | TEST | `nubes-test` | `3.0.0` | 2026-09-03 | (новая нумерация) |
## Как проверить ## Как проверить
+73 -31
View File
@@ -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) 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 var opDetails universalOpResponse
if err := json.Unmarshal(opDetailsResp, &opDetails); err != nil { hasOpDetails := false
return "", fmt.Errorf("не удалось разобрать детали операции: %w", err)
}
params, err = c.resolveRefSvcParamValues(ctx, opDetails.InstanceOperation.CfsParams, params)
if err != nil { 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) sent := make(map[int]bool)
@@ -297,30 +302,32 @@ func (c *UniversalClient) CreateGenericInstanceUniversalV6(ctx context.Context,
sent[paramId] = true sent[paramId] = true
} }
for _, param := range opDetails.InstanceOperation.CfsParams { if hasOpDetails {
if sent[param.SvcOperationCfsParamId] { for _, param := range opDetails.InstanceOperation.CfsParams {
continue if sent[param.SvcOperationCfsParamId] {
} continue
if !param.IsRequired { }
continue if !param.IsRequired {
} continue
}
val := "" val := ""
if param.ParamValue != nil { if param.ParamValue != nil {
val = *param.ParamValue val = *param.ParamValue
} else if param.DefaultValue != nil { } else if param.DefaultValue != nil {
val = *param.DefaultValue val = *param.DefaultValue
} }
val = normalizeUniversalValueV6(val, param) val = normalizeUniversalValueV6(val, param)
pPayload := genericParamReq{ pPayload := genericParamReq{
InstanceOperationUid: opUid, InstanceOperationUid: opUid,
SvcOperationCfsParamId: param.SvcOperationCfsParamId, SvcOperationCfsParamId: param.SvcOperationCfsParamId,
ParamValue: val, ParamValue: val,
} }
_, _, err := c.doRequest(ctx, "POST", "/instanceOperationCfsParams", pPayload) _, _, err := c.doRequest(ctx, "POST", "/instanceOperationCfsParams", pPayload)
if err != nil { if err != nil {
return "", fmt.Errorf("не удалось отправить параметр по умолчанию %d: %w", param.SvcOperationCfsParamId, err) return "", fmt.Errorf("не удалось отправить параметр по умолчанию %d: %w", param.SvcOperationCfsParamId, err)
}
} }
} }
@@ -1352,6 +1359,41 @@ func isUUIDLike(value string) bool {
return true 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 { func isHexDigit(r rune) bool {
return (r >= '0' && r <= '9') || (r >= 'a' && r <= 'f') || (r >= 'A' && r <= 'F') return (r >= '0' && r <= '9') || (r >= 'a' && r <= 'f') || (r >= 'A' && r <= 'F')
} }