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:
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
+1
-1
@@ -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 | (новая нумерация) |
|
||||
|
||||
## Как проверить
|
||||
|
||||
@@ -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')
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user