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