feat(stand): add FullPipe DEV stand for vc_vdc and document 500 error fallback plan
- Added DEV_STAND/FullPipe with modular configuration for vc_vdc resource creation:
- versions.tf: Terraform >= 1.5.0, provider nubes 2.0.1
- provider.tf: nubes provider config with DEV gateway endpoint
- variables.tf: variables for vdc (cpu=8, mem=32, guaranteed=0, fast storage=200GB)
- vdc.tf: nubes_vc_vdc resource definition supporting org name or UUID
- outputs.tf: vdc_id, vdc_name, vdc_state_params
- terraform.tfvars.example: example values without secrets
- Added docs/DEBUG_REPORT_VC_VDC_500.md documenting API 500 issue in Lucee backend:
- Error: getResourceRealmConfig fails casting Struct to string on GET /instanceOperations/{opUid}?fields=cfsParams
- Planned changes in provider/internal/core/client.go:
Implement graceful fallback in createInstanceWithContext to skip hard-failing
when GET ?fields=cfsParams returns 500, since vc_vdc ref parameters (organization_uid)
are already resolved to UUID and remaining parameters are literals/numbers.
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
output "vdc_id" {
|
||||
description = "UID созданного VDC"
|
||||
value = nubes_vc_vdc.vdc.id
|
||||
}
|
||||
|
||||
output "vdc_name" {
|
||||
description = "Имя VDC"
|
||||
value = nubes_vc_vdc.vdc.resource_name
|
||||
}
|
||||
|
||||
output "vdc_state_params" {
|
||||
description = "Параметры состояния VDC из API"
|
||||
value = nubes_vc_vdc.vdc.state_params
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
provider "nubes" {
|
||||
api_token = var.api_token
|
||||
api_endpoint = var.api_endpoint
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
api_token = "ВАШ_ТОКЕН_ИЗ_ЛК"
|
||||
|
||||
# Имя или UUID организации:
|
||||
organization = "kontora"
|
||||
|
||||
vdc_resource_name = "fullpipe-vdc"
|
||||
vdc_network_provider = "default"
|
||||
vdc_provider_vdc = "fast-2.8"
|
||||
vdc_cpu_allocated = 8
|
||||
vdc_cpu_guaranteed = 0
|
||||
vdc_mem_allocated = 32
|
||||
|
||||
vdc_storage_config = "[{\"name\":\"fast\",\"size\":200}]"
|
||||
@@ -0,0 +1,59 @@
|
||||
variable "api_token" {
|
||||
type = string
|
||||
sensitive = true
|
||||
description = "API-токен Nubes"
|
||||
}
|
||||
|
||||
variable "api_endpoint" {
|
||||
type = string
|
||||
default = "https://lk-api-gateway-dev.ngcloud.ru/api/v1/svc"
|
||||
description = "API Gateway URL"
|
||||
}
|
||||
|
||||
# Имя (display_name, напр. "kontora") ИЛИ UUID организации из ЛК
|
||||
variable "organization" {
|
||||
type = string
|
||||
description = "Имя или UUID организации (vc_org)"
|
||||
}
|
||||
|
||||
variable "vdc_resource_name" {
|
||||
type = string
|
||||
default = "fullpipe-vdc"
|
||||
description = "Имя VDC"
|
||||
}
|
||||
|
||||
variable "vdc_network_provider" {
|
||||
type = string
|
||||
default = "default"
|
||||
description = "Сетевой провайдер (неизменяемый)"
|
||||
}
|
||||
|
||||
variable "vdc_provider_vdc" {
|
||||
type = string
|
||||
default = "fast-2.8"
|
||||
description = "Профиль Provider VDC (неизменяемый)"
|
||||
}
|
||||
|
||||
variable "vdc_cpu_allocated" {
|
||||
type = number
|
||||
default = 8
|
||||
description = "vCPU (шт.)"
|
||||
}
|
||||
|
||||
variable "vdc_cpu_guaranteed" {
|
||||
type = number
|
||||
default = 0
|
||||
description = "Резервирование vCPU (%, допустимо: 0, 50, 80)"
|
||||
}
|
||||
|
||||
variable "vdc_mem_allocated" {
|
||||
type = number
|
||||
default = 32
|
||||
description = "RAM (GB)"
|
||||
}
|
||||
|
||||
variable "vdc_storage_config" {
|
||||
type = string
|
||||
default = "[{\"name\":\"fast\",\"size\":200}]"
|
||||
description = "Дисковое хранилище (JSON-массив, size в GB)"
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
resource "nubes_vc_vdc" "vdc" {
|
||||
resource_name = var.vdc_resource_name
|
||||
|
||||
# Организация: имя из ЛК ("kontora") или точный UUID
|
||||
organization_uid = var.organization
|
||||
|
||||
network_provider = var.vdc_network_provider
|
||||
provider_vdc = var.vdc_provider_vdc
|
||||
|
||||
cpu_allocated = var.vdc_cpu_allocated
|
||||
cpu_guaranteed = var.vdc_cpu_guaranteed
|
||||
mem_allocated = var.vdc_mem_allocated
|
||||
|
||||
# JSON-массив дисковых политик (size в GB)
|
||||
storage_config = var.vdc_storage_config
|
||||
|
||||
suspend_on_destroy = true
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
terraform {
|
||||
required_version = ">= 1.5.0"
|
||||
|
||||
required_providers {
|
||||
nubes = {
|
||||
source = "tf-registry.containerk8s.services.ngcloud.ru/nubes-dev/nubes"
|
||||
version = "2.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
# Отчет о проблеме: Ошибка 500 при получении cfsParams для vc_vdc и план исправления
|
||||
|
||||
## 1. Проблема
|
||||
При создании виртуального дата-центра `nubes_vc_vdc` (сервис 21 `vc_vdc`, операция создания 9) Terraform завершается с ошибкой клиента:
|
||||
```text
|
||||
не удалось получить детали операции: ошибка API 500: Invalid call of the function [getResourceRealmConfig], first Argument [resourceRealm] is of invalid type, Cannot cast Object type [Struct] to a value of type [string]: the function is located at [/app/api/v1/resources/instance_operation_cfs_param.cfc]
|
||||
```
|
||||
|
||||
## 2. Анализ причины
|
||||
1. **Место падения в провайдере**: `provider/internal/core/client.go` (строка 272 в `createInstanceWithContext`):
|
||||
```go
|
||||
opDetailsResp, _, err := c.doRequest(ctx, "GET", fmt.Sprintf("/instanceOperations/%s?fields=cfsParams", opUid), nil)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("не удалось получить детали операции: %w", err)
|
||||
}
|
||||
```
|
||||
2. **Поведение бэкенда Nubes (Lucee/ColdFusion)**:
|
||||
- В файле `/app/api/v1/resources/instance_operation_cfs_param.cfc` при обработке запроса `?fields=cfsParams` для операции 9 вызывается функция `getResourceRealmConfig(resourceRealm)`.
|
||||
- Для сервиса `vc_vdc` поле `resourceRealm` в БД DEV-окружения хранится как комплексный объект (`Struct`), а не скалярная строка (`string`).
|
||||
- При попытке приведения типа `Struct -> string` бэкенд падает с HTTP 500.
|
||||
|
||||
3. **Сравнение с веб-интерфейсом (HAR/vdc.har)**:
|
||||
- В официальном веб-интерфейсе ЛК запрос `GET /instanceOperations/{opUid}?fields=cfsParams` **вообще не выполняется**.
|
||||
- Браузер выполняет строго следующий флоу:
|
||||
1. `POST /instanceOperations` -> получает `instanceOperationUid`
|
||||
2. `POST /instanceOperationCfsParams` -> отправляет каждое значение CFS-параметра
|
||||
3. `GET /instanceOperations/{opUid}/validate-cfs` -> валидация бэкендом
|
||||
4. `POST /instanceOperations/{opUid}/run` -> запуск операции в оркестраторе
|
||||
5. Polling `GET /instanceOperations/{opUid}` -> ожидание статуса завершения
|
||||
|
||||
4. **Зачем провайдер делает шаг 2**:
|
||||
- Шаг 2 в провайдере использовался исключительно для вызова `resolveRefSvcParamValues(ctx, opDetails.InstanceOperation.CfsParams, params)` — чтобы узнать `refSvcId` параметров и попробовать отрезолвить имена в UUID.
|
||||
- Для ресурса `nubes_vc_vdc` параметр `organization_uid` (CFS param 30, refSvc 19) **уже гарантированно отрезолвлен в UUID** до создания операции (на этапе `ModifyPlan` и в начале `Create`).
|
||||
- Остальные параметры `vc_vdc` (провайдер сети, профиль Provider VDC, CPU, RAM, резервирование, storage_config) являются скалярами/числами/JSON-строками и не содержат `refSvcId`.
|
||||
- Соответственно, данные запроса `?fields=cfsParams` для `vc_vdc` фактически не требуются.
|
||||
|
||||
## 3. План изменений в провайдере (что будем менять)
|
||||
|
||||
### Целевой файл: `provider/internal/core/client.go`
|
||||
В функции `createInstanceWithContext` (и при необходимости в `runInstanceOperationUniversalByCodeWithTimeout` / `updateResourceWithTimeout`) заменяется жёсткое падение на условный graceful fallback:
|
||||
|
||||
```go
|
||||
opDetailsResp, _, err := c.doRequest(ctx, "GET", fmt.Sprintf("/instanceOperations/%s?fields=cfsParams", opUid), nil)
|
||||
if err != nil {
|
||||
// Проверяем, есть ли среди переданных строковых параметров не-UUID значения,
|
||||
// требующие резолвинга через refSvcId.
|
||||
// Если все строковые параметры уже UUID или числа/литералы — логируем предупреждение и продолжаем.
|
||||
c.logWarn(ctx, "не удалось получить cfsParams для операции %s (%v), продолжаем отправку параметров", opUid, err)
|
||||
} else {
|
||||
var opDetails universalOpResponse
|
||||
if err := json.Unmarshal(opDetailsResp, &opDetails); err == nil {
|
||||
params, err = c.resolveRefSvcParamValues(ctx, opDetails.InstanceOperation.CfsParams, params)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Критерии безопасности:
|
||||
1. Fallback условный: если бэкенд упал, но параметры уже валидны / являются UUID — выполнение продолжается.
|
||||
2. Логируется явный warning с идентификатором операции `opUid` и телом ошибки.
|
||||
3. Валидация значений параметров не теряется — её по-прежнему выполняет бэкенд на этапе `GET /validate-cfs`.
|
||||
Reference in New Issue
Block a user