Files
tf_provider/NOTES/30_analysis/FORENSIC_ANALYSIS_REPORT_2026-09-23.md
T
Repinoid f7fffb9ed7 refactor: разложить рабочие материалы по NOTES/ и HOW_TO/, корневой README — карта проекта
- NOTES/: 10_plans, 20_prompts, 30_analysis, 40_chat_summaries, 60_reference + README в каждой папке
- HOW_TO/: все общие инструкции (сборка/заливка, DevOps-ранбук, добавление сервиса, миграция, генерация доков) + индекс «что нужно -> какой файл»
- новый README.md: карта проекта, пайплайн, стенды, реестр, запреты/грабли
- HOWTO-UPLOAD.md: исправлена легаси-схема версий (prod=1.*, dev=2.*, test=3.*)
- DEVOPS_BUILD_PIPELINE.md: пути скриптов -> TOOLS/scripts, universal_rebuild/main.go -> provider/main.go
- howitwasdone.md / MIGRATION_PLAN_FOR_AGENT.md: пометки о соответствии старых путей
- внутри перенесённых файлов обновлены ссылки на новые пути
2026-09-24 07:51:38 +03:00

20 KiB
Raw Blame History

Forensic Analysis: API Model to Ordinary YAML

Анализ от Gemini 3.8 flash.

Исследование проведено строго в границах требований FORENSIC_ANALYSIS_BRIEF_2026-09-23.md.


1. Подтверждённые факты

  • Цепочка генерации YAML: Инструмент yaml-generator (TOOLS/yaml-generator/main.go) опрашивает live HTTP REST Gateway API Nubes по сети и сериализует результат в YAML-файлы спецификаций (generated/{stand}/resources_yaml/{service_id}_{name}.yaml), используя структуры контракта TOOLS/lib/types.go.
  • Спецификации сервисов в репозитории: Канонические сгенерированные спеки сервисов физически размещены в generated/dev/resources_yaml/. В частности, 19_vc_org.yaml и 22_vc_nsxt.yaml.
  • В vc_org.yaml:
    • Операция modify имеет числовой ID 207, kind: instance, action: modify.
    • Параметр vIPConfigure присутствует как параметр операции modify с числовым ID 662, типом data_type: array-map-fixed, required: true.
    • Поле count присутствует внутри sub_params параметра vIPConfigure с числовым ID 40, data_type: integer > 0, required: true, is_modifiable: false.
    • Поле name присутствует внутри sub_params параметра vIPConfigure с числовым ID 39, data_type: string, required: true, is_modifiable: false.
  • В vc_nsxt.yaml:
    • Операция modify имеет числовой ID 111, kind: instance, action: modify.
    • Параметр ipSpaceName присутствует в операции modify с числовым ID 372, data_type: string, required: false, sort: 50.
    • С ним рядом в операции modify присутствуют:
      • needEnableAVI (ID 368, data_type: boolean, required: false, value_list: ["false", "true"]);
      • virtualServicesCount (ID 369, data_type: integer > 0, required: false, minvalue: 1, maxvalue: 4);
      • qosProfile (ID 856, data_type: string, required: false);
      • routedNetConfiguration (ID 1112, data_type: map-fixed, required: true, с вложенными подполями ipAddrPool, mainDns, secondDns).
  • Генератор ресурсов (Ordinary Resource Generator):
    • Расположен в TOOLS/resource-generator/main.go.
    • При op.Kind == "instance" (что установлено для modify в generated/dev/resources_yaml/19_vc_org.yaml и generated/dev/resources_yaml/22_vc_nsxt.yaml) генератор объединяет параметры create и modify в схему одного общего ресурса nubes_vc_org / nubes_vc_nsxt.
    • Параметры, отсутствующие в операции create, но присутствующие в операции modify (как vIPConfigure в vc_org), не включаются в жизненный цикл create, а при отсутствии отдельной разметки kind: modifier в YAML генератор не создаёт под них отдельного ресурса модификатора.

2. Исходная API-модель

  • Источник и формат: Модель получается HTTP-клиентом (TOOLS/yaml-generator/internal/client/client.go) по протоколу HTTP GET в формате JSON.
  • Эндпоинты API:
    • Метаданные сервиса и список операций: GET /services/{svcId} (возвращает types.ServiceResponse).
    • Метаданные конкретной операции: GET /instanceOperations/default/{svcOperationId} (возвращает types.ServiceOperationResponse).
  • Идентификаторы операций и параметров:
    • Операция идентифицируется стабильным числовым svcOperationId (int) и строковым именем operation (например, "modify", "create").
    • Параметры идентифицируются стабильным числовым svcOperationCfsParamId (int) и строковым кодом svcOperationCfsParam (например, "vIPConfigure", "ipSpaceName").
  • Вложенные объекты и массивы (dataDescriptor):
    • В ответе эндпоинта /instanceOperations/default/{id} сложная структура передаётся в поле dataDescriptor: map[string]CfsSubParam.
    • Каждое подполе имеет свой числовой svcOperationCfsSubparamId, строковый ключ (код подполя), dataType, isRequired, isModifiableDefinition, defaultValue, valueList (строка через запятую или массив).

3. Как ordinary generator читает модель

  • Входной поток: yaml-generator вызывает cli.GetService(svc.ID) и перебирает список info.Operations.
  • Чтение операций и параметров: Для каждой операции вызывается cli.GetServiceOperation(op.SvcOperationID) (TOOLS/yaml-generator/internal/client/client.go).
  • Преобразования и нормализация:
    • Имена сервисов и операций нормализуются в snake_case функцией normalize.Identifier (TOOLS/yaml-generator/internal/normalize/normalize.go).
    • Классификация операции: функция classifyOperation делит операции на instance (для create, modify, delete, suspend, resume), subresource (если есть символ подчеркивания) или action.
    • Разворачивание dataDescriptor: генератор обходит map[string]CfsSubParam, преобразует подполя в срез types.ParamSpec и детерминированно сортирует по subParams[i].ID.
    • Сортировка верхнеуровневых параметров по params[i].ID.
    • Сортировка операций по имени и ID.
  • Что отбрасывается / не сохраняется в YAML:
    • Конкретные HTTP method и URL-пути эндпоинтов API платформы (они зашиты в код клиента, в спек YAML не пишутся).
    • Вспомогательные поля CfsParam, не имеющие тега yaml: в TOOLS/lib/types.go, если они не сериализуются или приходят пустыми: IsModifiable, IsSensitive (указаны с omitempty). Поле dataDescriptor как мапа отбрасывается — сохраняется преобразованный срез sub_params.

4. Как формируется API YAML

  • Сериализация структуры types.ServiceSpec в YAML выполняется через библиотеку gopkg.in/yaml.v3 (TOOLS/yaml-generator/main.go).
  • В результирующий файл пишутся:
    • Метаданные сервиса (name, service_id, service_display_name, service_short_name, service_man).
    • Стандартная секция lifecycle и outputs.
    • Секция operations со списком операций и всеми их параметрами (id, code, data_type, required, default, value_list, descr, man, sort, sub_params).

5. Цепочка данных для vc_org.vIPConfigure.count

  1. API Model:
    • Эндпоинт /instanceOperations/default/207 возвращает параметр с svcOperationCfsParamId: 662, svcOperationCfsParam: "vIPConfigure", dataType: "array-map-fixed".
    • Внутри него поле dataDescriptor содержит ключ "count":
      • svcOperationCfsSubparamId: 40,
      • dataType: "integer > 0",
      • isRequired: true,
      • defaultValue: "",
      • descr: "Пример: \1`"`.
  2. Generator Input:
  3. Internal Generator Representation:
    • В TOOLS/yaml-generator/internal/client/client.go мапа dataDescriptor разворачивается в types.ParamSpec.SubParams. Поле count становится элементом среза SubParams с ID: 40, Code: "count", DataType: "integer > 0".
  4. Generator Transformation:
    • Сортируется по ID (sort.Slice(subParams, ...)).
  5. API YAML:
    • Записывается в generated/dev/resources_yaml/19_vc_org.yaml:
      - id: 662
        code: vIPConfigure
        data_type: array-map-fixed
        required: true
        sort: 10
        sub_params:
          - id: 39
            code: name
            data_type: string
            required: true
            default: ""
            is_modifiable: false
          - id: 40
            code: count
            data_type: integer > 0
            required: true
            default: ""
            descr: 'Пример: `1`'
            is_modifiable: false
      
    • Потерь в YAML нет: vIPConfigure и count полностью и без искажений сохранены в каноническом YAML спецификации.

6. Цепочка данных для vc_nsxt.ipSpaceName и связанных параметров

  1. API Model:
    • Эндпоинт /instanceOperations/default/111 возвращает операцию modify сервиса 22.
    • Параметр ipSpaceName возвращается с:
      • svcOperationCfsParamId: 372,
      • svcOperationCfsParam: "ipSpaceName",
      • dataType: "string",
      • isRequired: false,
      • descr: "Имя ip Space для внешнего IP",
      • man: "Необходимо указывать, если включён параметр \Выделить VIP для SNAT`"`,
      • sort: 50.
    • В HAR-дампах (HAR/edge_.har) на живом инстансе в рантайме возвращается valueList: ["no-needed", ...].
  2. Generator Input:
  3. Internal Generator Representation:
    • Преобразуется в types.ParamSpec со значениями ID: 372, Code: "ipSpaceName", DataType: "string".
  4. Generator Transformation:
    • Нормализуются defaults и value_list через normalizeDefault и normalizeValueList.
  5. API YAML:
    • Записывается в generated/dev/resources_yaml/22_vc_nsxt.yaml:
      - id: 372
        code: ipSpaceName
        data_type: string
        required: false
        descr: Имя ip Space для внешнего IP
        man: Необходимо указывать, если включён параметр `Выделить VIP для SNAT`
        sort: 50
      
    • Рядом в той же операции сохранены: needEnableAVI (ID 368), virtualServicesCount (ID 369), qosProfile (ID 856), routedNetConfiguration (ID 1112 с sub_params).
    • Особенность по value_list: в статическом 22_vc_nsxt.yaml поле value_list для ipSpaceName отсутствует (null в ответе static-дефолтов эндпоинта /instanceOperations/default/111), хотя в рантайме на конкретном инстансе valueList динамически содержит ["no-needed", ...].

7. Что сохраняется

  • Полная идентичность сущностей: service_id, svcOperationId (как id операции), svcOperationCfsParamId (как id параметра), svcOperationCfsSubparamId (как id в sub_params).
  • Строковые коды: operation, коды параметров (code).
  • Исходные типы платформы: dataType (string, boolean, integer > 0, array-map-fixed, map-fixed).
  • Вся структура вложенности (dataDescriptor → sub_params).
  • Флаги required, валидационные regex, min/max, описания (descr, man), порядок (sort).

8. Что преобразуется или нормализуется

  • Имена операций и сервисов приводятся к ASCII snake_case через normalize.Identifier.
  • dataDescriptor из мапы ключей преобразуется в упорядоченный срез sub_params с сортировкой по числовому id.
  • Значения valueList и default приводятся к строковым представлениям (убираются пробелы, пустые значения приводятся к nil).

9. Что теряется

  • HTTP-метод и путь обращения к API (в YAML отсутствуют; генератор считает их внешним знанием рантайма).
  • Динамические значения списков выбора (valueList): эндпоинт дефолтов /instanceOperations/default/{id} возвращает пустой valueList для полей, зависящих от конкретного тенанта/инстанса (например, доступные ipSpaceName для конкретного VDC/Org).

10. Где происходят потери

  • Потери HTTP-метаданных (метод, URL) происходят на этапе маршалинга структуры types.ServiceSpec в YAML (TOOLS/yaml-generator/main.go), так как они изначально отсутствуют в контракте TOOLS/lib/types.go.
  • Отсутствие runtime valueList обусловлено вызовом шаблонного эндпоинта /instanceOperations/default/{id} вместо запроса контекста живого инстанса.

11. Какие данные доступны в API YAML

  • Полный перечень всех сервисов, операций (create, modify, delete, suspend, resume, сабресурсов) и их параметров.
  • Точные типы платформы (data_type) и иерархия подполей (sub_params).
  • Идентификаторы id (CFS param IDs) и символические коды (code).
  • Метаданные валидации (обязательность, регулярные выражения, ограничения диапазонов).

12. Какие данные доступны только в API-модели

  • Динамические списки допустимых значений (valueList), вычисляемые бэкендом для конкретного состояния конкретного инстанса (например, список реально существующих ipSpaces организации при вызове modify на Edge).
  • Внутренние служебные поля платформы CFS, отфильтрованные моделью генератора (contractId, contragentId, nestedRefData, config, statePath, expression).

13. Неизвестные места

  • Неизвестно, возвращает ли платформа Nubes какую-либо схему валидации для эндпоинтов отката/деаллокации (например, принимает ли vc_org.modify пустой массив vIPConfigure: [] для полного снятия или требует только уменьшения count: 0), так как в дефолтной модели операции 207 описан только общий формат vIPConfigure.

14. Что необходимо проверить фактическим API

  • Поведение vc_org (операция 207) при передаче vIPConfigure: [] против vIPConfigure: [{"name": "...", "count": 0}] при попытке полной деаллокации IP-пространства.
  • Поведение vc_nsxt (операция 111) при передаче ipSpaceName: "no-needed" на различных окружениях (Dev/Test/Prod).

Мнение Opus: финальное ревью отчёта

Отчёт признан годным и принят как вход для дальнейшего архитектурного этапа.

Достаточно для дальнейшей работы

  • Цепочка API model → yaml-generator → API YAML прослежена по коду, а не по догадкам: classifyOperation, params.Merge, разворачивание dataDescriptor в sub_params.
  • Обе обязательные трассировки (vc_org.vIPConfigure.count, vc_nsxt.ipSpaceName) доведены до YAML с подтверждением «потерь нет».
  • Зафиксирован ключевой факт: динамический valueList есть только в рантайме живого инстанса, а в API YAML его нет. Это прямое ограничение для будущего modifier-слоя.
  • Неизвестное поведение payload деаллокации (vIPConfigure: [] против count: 0) помечено как unknown, а не закрыто предположением.

Следствия перед архитектурным этапом

  • Под ярлыком «Ordinary Resource Generator» в отчёте упоминаются два разных инструмента: yaml-generator пишет спеки, а resource-generator создаёт Go-ресурсы. Для проектирования ModifierSpec это две разные точки вмешательства.
  • vIPConfigure и ipSpaceName присутствуют только в modify и отсутствуют в create. В текущей схеме они сливаются в общий ресурс и не имеют отдельного жизненного цикла. Это корень задачи модификаторов.
  • Runtime-valueList придётся получать не из дефолтного эндпоинта, а из контекста инстанса. Это вопрос рантайма провайдера, а не генератора.

Итоговая оценка

Ошибок, которые ломали бы выводы отчёта, не выявлено. Отчёт можно использовать как подтверждённую основу для дальнейшего проектирования.