From 7ff98f8edc11a4218ca95d3a616039a0df9ce726 Mon Sep 17 00:00:00 2001 From: Repinoid Date: Mon, 21 Sep 2026 09:46:43 +0300 Subject: [PATCH] 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. --- DEV_STAND/FullPipe/outputs.tf | 14 +++++ DEV_STAND/FullPipe/provider.tf | 4 ++ DEV_STAND/FullPipe/terraform.tfvars.example | 13 +++++ DEV_STAND/FullPipe/variables.tf | 59 +++++++++++++++++++ DEV_STAND/FullPipe/vdc.tf | 18 ++++++ DEV_STAND/FullPipe/versions.tf | 10 ++++ docs/DEBUG_REPORT_VC_VDC_500.md | 63 +++++++++++++++++++++ 7 files changed, 181 insertions(+) create mode 100644 DEV_STAND/FullPipe/outputs.tf create mode 100644 DEV_STAND/FullPipe/provider.tf create mode 100644 DEV_STAND/FullPipe/terraform.tfvars.example create mode 100644 DEV_STAND/FullPipe/variables.tf create mode 100644 DEV_STAND/FullPipe/vdc.tf create mode 100644 DEV_STAND/FullPipe/versions.tf create mode 100644 docs/DEBUG_REPORT_VC_VDC_500.md diff --git a/DEV_STAND/FullPipe/outputs.tf b/DEV_STAND/FullPipe/outputs.tf new file mode 100644 index 0000000..9d0a30b --- /dev/null +++ b/DEV_STAND/FullPipe/outputs.tf @@ -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 +} diff --git a/DEV_STAND/FullPipe/provider.tf b/DEV_STAND/FullPipe/provider.tf new file mode 100644 index 0000000..9d91910 --- /dev/null +++ b/DEV_STAND/FullPipe/provider.tf @@ -0,0 +1,4 @@ +provider "nubes" { + api_token = var.api_token + api_endpoint = var.api_endpoint +} diff --git a/DEV_STAND/FullPipe/terraform.tfvars.example b/DEV_STAND/FullPipe/terraform.tfvars.example new file mode 100644 index 0000000..ccd199f --- /dev/null +++ b/DEV_STAND/FullPipe/terraform.tfvars.example @@ -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}]" diff --git a/DEV_STAND/FullPipe/variables.tf b/DEV_STAND/FullPipe/variables.tf new file mode 100644 index 0000000..885b55a --- /dev/null +++ b/DEV_STAND/FullPipe/variables.tf @@ -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)" +} diff --git a/DEV_STAND/FullPipe/vdc.tf b/DEV_STAND/FullPipe/vdc.tf new file mode 100644 index 0000000..9afec66 --- /dev/null +++ b/DEV_STAND/FullPipe/vdc.tf @@ -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 +} diff --git a/DEV_STAND/FullPipe/versions.tf b/DEV_STAND/FullPipe/versions.tf new file mode 100644 index 0000000..f28cb12 --- /dev/null +++ b/DEV_STAND/FullPipe/versions.tf @@ -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" + } + } +} diff --git a/docs/DEBUG_REPORT_VC_VDC_500.md b/docs/DEBUG_REPORT_VC_VDC_500.md new file mode 100644 index 0000000..737d995 --- /dev/null +++ b/docs/DEBUG_REPORT_VC_VDC_500.md @@ -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`.