# Universal Provider (ядро + генератор ресурсов) — подробный отчёт Дата: 2026-01-31 ## Цель Нужна архитектура «универсального провайдера», где: - есть **ядро** (общий клиент и универсальный флоу операций); - есть **папка ресурсов** (Go-файлы ресурсов); - есть **папка YAML-описаний сервисов** (чтобы DevOps мог добавить сервис одним файлом); - есть **генератор**, который: - читает YAML, - генерирует ресурсы, - генерирует реестр ресурсов, - дальше выполняется build (ядро + ресурсы из папки). В итоге: если файла ресурса нет — в провайдере его не будет. Если YAML изменён — перегенерация. --- ## Итоговая архитектура (v2.0.0, «голое» ядро + ресурсы) Новая сборка размещена в: - [nubes_provider_gen](nubes_provider_gen) Структура: - [nubes_provider_gen/internal/core](nubes_provider_gen/internal/core) — **ядро** (клиент, универсальный flow, запуск операций) - [nubes_provider_gen/internal/provider](nubes_provider_gen/internal/provider) — **провайдер** (schema + клиент) - [nubes_provider_gen/internal/resources_gen](nubes_provider_gen/internal/resources_gen) — **сгенерированные ресурсы** (Go) - [nubes_provider_gen/resources_yaml](nubes_provider_gen/resources_yaml) — **YAML-описания сервисов** - [nubes_provider_gen/tools/gen](nubes_provider_gen/tools/gen) — **генератор** - [nubes_provider_gen/test_persistent](nubes_provider_gen/test_persistent) — тестовые манифесты ### 1) Ядро (core) Файл: [nubes_provider_gen/internal/core/client.go](nubes_provider_gen/internal/core/client.go) Содержит: - `UniversalClient` с HTTP-клиентом и API endpoint/token. - Универсальный flow **CreateGenericInstanceUniversalV6**: 1. POST /instances 2. POST /instanceOperations (create) 3. GET /instanceOperations/{opUid}?fields=cfsParams 4. POST /instanceOperationCfsParams (явные параметры) 5. POST /instanceOperationCfsParams (дефолтные/пустые для пропущенных) 6. GET /instanceOperations/{opUid}/validate-cfs 7. POST /instanceOperations/{opUid}/run - Универсальные операции: - `RunInstanceOperationUniversal` (delete/suspend/resume и т.д.) - `RunInstanceOperationUniversalWithDefaults` (modify с автоподстановкой параметров) - Утилиты: - `FindInstanceByDisplayName` - `GetInstanceState` **Почему нужно WithDefaults для modify** На modify требуются обязательные параметры (часто даже те, что не меняются). Без них backend падает. ### 2) Провайдер (provider) Файл: [nubes_provider_gen/internal/provider/provider.go](nubes_provider_gen/internal/provider/provider.go) - Тип провайдера: `nubes` - Адрес: `terrareg.kube5s.ru/nubes/nubes` - Ресурсы приходят из **реестра**: - `resources_gen.AllResources()` — возвращает список функций создания ресурсов. ### 3) Генератор (tools/gen) Файл: [nubes_provider_gen/tools/gen/main.go](nubes_provider_gen/tools/gen/main.go) Функции: - Читает все YAML-файлы в `resources_yaml/` - Генерирует ресурсный Go-файл в `internal/resources_gen/` для каждого YAML - Генерирует `registry.go` с `AllResources()` То есть: - **Добавили YAML → сгенерировали Go → build** - Нет YAML → нет Go → нет ресурса ### 4) YAML-описания Файл: [nubes_provider_gen/resources_yaml/dummy.yaml](nubes_provider_gen/resources_yaml/dummy.yaml) Минимальная схема (пример dummy): - `name`, `service_id`, `display_name_default` - `create.params` — ID параметров create - `modify.params` — ID параметров modify - `lifecycle` — defaults для `adopt_existing_on_create` и `suspend_on_destroy` --- ## Что было сделано (конкретные шаги) ### Шаг 1. «Голое» ядро + генератор Создан отдельный проект: - [nubes_provider_gen](nubes_provider_gen) Сделан core + provider + tools/gen + resources_yaml. ### Шаг 2. YAML для dummy Создан YAML dummy: [nubes_provider_gen/resources_yaml/dummy.yaml](nubes_provider_gen/resources_yaml/dummy.yaml) ### Шаг 3. Генерация ресурсов Команда: - `go run ./tools/gen` Сгенерированы: - [nubes_provider_gen/internal/resources_gen/dummy_resource.go](nubes_provider_gen/internal/resources_gen/dummy_resource.go) - [nubes_provider_gen/internal/resources_gen/registry.go](nubes_provider_gen/internal/resources_gen/registry.go) ### Шаг 4. Build Команда: - `go build -o terraform-provider-nubes` ### Шаг 5. Тесты dummy Тестовый конфиг: - [nubes_provider_gen/test_persistent/main.tf](nubes_provider_gen/test_persistent/main.tf) - [nubes_provider_gen/test_persistent/dev_override.tfrc](nubes_provider_gen/test_persistent/dev_override.tfrc) Проверены сценарии: 1. `apply` (create/adopt) — OK 2. `modify` (duration 750 → 820) — OK 3. `destroy` (`suspend_on_destroy = true`) — OK 4. `apply` после suspend (resume/adopt c явным `adopt_existing_on_create`) — OK 5. удаление ресурса из манифеста + `apply` — OK 6. возвращение ресурса в манифест + `apply` — OK --- ## Ошибки и как решались ### 1) `action modify not available` Причина: в Update использовался `id` из `Plan` (неизвестен). Решение: брать `id` из `State`. ### 2) `API error 400: Parameter specified does not belong to this operation` Причина: неверные ID параметров modify. Решение: для modify использовать `287/288` вместо `198/199`. ### 3) `API error 500: checkParam ... instanceOperationCfsParamUid` (повторно) Причина: операция modify требует подстановки всех параметров; без них backend падает. Решение: - Добавлен `RunInstanceOperationUniversalWithDefaults` — читает `cfsParams` и заполняет недостающие - Позже для dummy оказалось нужно отправлять **все** параметры (не только required) ### 4) `TLS handshake timeout` Причина: VPN/сеть. Решение: повторить команду. ### 5) `status 401` Причина: токен истёк/невалидный. Решение: заменить токен и сохранить по правилу `/home/naeel/terra/HH-MM-SS.token`. ### 6) Критерий завершения операции (важно) Любая операция (create/modify/delete/suspend/resume) считается **завершённой**, когда у неё заполнено **время окончания**. Это является признаком завершения **и успеха, и ошибки**. В UI и API ориентируемся на наличие `dtFinish`. --- ## Текущее состояние - Провайдер «голый» + ресурсы из YAML работает. - `dummy` полностью тестируется из YAML → Go → build. - В `resources_gen` присутствует 1 ресурс: `nubes_dummy`. --- ## План (следующий шаг) 1) Расширить YAML-схему: - поддержку дополнительных параметров (например, failInProgress, whereFail и т.п.) - поддержку optional параметров с явными defaults 2) Добавить генерацию документации из YAML 3) Добавить проверку схемы YAML (валидация) перед генерацией 4) Скрипт "build pipeline": - gen → registry → go build 5) Опционально: тестовый фреймворк для «batch» тестов --- ## Важные файлы - Ядро: [nubes_provider_gen/internal/core/client.go](nubes_provider_gen/internal/core/client.go) - Генератор: [nubes_provider_gen/tools/gen/main.go](nubes_provider_gen/tools/gen/main.go) - Реестр: [nubes_provider_gen/internal/resources_gen/registry.go](nubes_provider_gen/internal/resources_gen/registry.go) - YAML dummy: [nubes_provider_gen/resources_yaml/dummy.yaml](nubes_provider_gen/resources_yaml/dummy.yaml) - Сгенерированный ресурс: [nubes_provider_gen/internal/resources_gen/dummy_resource.go](nubes_provider_gen/internal/resources_gen/dummy_resource.go) - Тесты: [nubes_provider_gen/test_persistent/main.tf](nubes_provider_gen/test_persistent/main.tf) --- ## MAYDO (отложено до запроса заказчика) - Добавить в YAML `outputs` (из `state/out`) и `state_params` (из `state/params`). - Добавить `param_meta`: `valueList`, `func`, `refSvcId`, `dataDescriptor`. - Добавить `secrets` (из `vault`) с пометкой чувствительных данных. - Добавить `operations` (man/описания, доступные операции) для генерации доков. - Добавить поддержку `subparams` (nested/list/json) и типизацию сложных структур. - Добавить обработку версий операций (если API это отдаёт).