Files
tf_provider/docs/DEBUG_REPORT_VC_VDC_500.md
T
Repinoid 7ff98f8edc 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.
2026-09-21 09:46:43 +03:00

5.6 KiB
Raw Blame History

Отчет о проблеме: Ошибка 500 при получении cfsParams для vc_vdc и план исправления

1. Проблема

При создании виртуального дата-центра nubes_vc_vdc (сервис 21 vc_vdc, операция создания 9) Terraform завершается с ошибкой клиента:

не удалось получить детали операции: ошибка 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):

    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:

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.