From 33672e05bfa6cd960d0087ca24c427a6b2a275f9 Mon Sep 17 00:00:00 2001 From: Repinoid Date: Sat, 19 Sep 2026 08:32:28 +0300 Subject: [PATCH] docs: add modifier resources ideology and architecture specification --- ...er_resources_ideology_and_specification.md | 237 ++++++++++++++++++ 1 file changed, 237 insertions(+) create mode 100644 docs/60_strategy/modifier_resources_ideology_and_specification.md diff --git a/docs/60_strategy/modifier_resources_ideology_and_specification.md b/docs/60_strategy/modifier_resources_ideology_and_specification.md new file mode 100644 index 0000000..15c0b82 --- /dev/null +++ b/docs/60_strategy/modifier_resources_ideology_and_specification.md @@ -0,0 +1,237 @@ +# Архитектурная концепция: Modifier-ресурсы (Идеология, правила и интеграция в Terraform Provider) + +## 1. Введение и архитектурный контекст + +### 1.1. Проблема: Чередующиеся зависимости (Interleaved Lifecycle) +В классической декларативной модели Terraform каждый ресурс управляется монолитно: один блок `resource` соответствует полному жизненному циклу одной сущности (Create -> Read -> Update -> Delete). + +Однако при комплексном развертывании инфраструктуры у облачного провайдера (например, цепочка для сервиса **Штурвал** `k8s_sthutrval_cluster`) возникает жесткая **чередующаяся зависимость**: +1. `vcOrg/create` — создание тенанта (Организации). +2. `vcVdc/create` — создание виртуального датацентра внутри Организации. +3. `vcNsxt/create` — создание базового сетевого шлюза (Edge Gateway) с включением AVI ALB и 4 Service Engine. +4. `vcOrg/modify` — выделение пула из 3 внешних IP-адресов в Организации (требует, чтобы NSX-T уже существовал). +5. `vcNsxt/modify` — включение правила SNAT на шлюзе с привязкой `ipSpace`, созданного на шаге 4 (требует наличия свободных IP). +6. `k8sSthutrvalCluster/create` — развертывание кластера Штурвал (требует настроенного SNAT, AVI и свободных IP). + +Попытка «зашить» шаги 4 и 5 внутрь основных ресурсов `vc_org` и `vc_nsxt` приводит к тупику в графе зависимостей Terraform (DAG) или к ошибкам API из-за несвоевременного вызова параметров. + +### 1.2. Решение: Класс Modifier-ресурсов +Для разрешения таких зависимостей в архитектуру провайдера вводится специальный класс сущностей — **Modifier-ресурсы (Модификаторы)**. + +* **Instance-ресурс (базовый сервис)** — отвечает за владение и жизненный цикл инстанса в облаке (`POST /create`, `GET /state`, `DELETE /delete`). +* **Modifier-ресурс (модификатор)** — отвечает за выполнение отложенной операции конфигурирования/связывания над уже созданным инстансом (`POST /modify`), являясь самостоятельным блоком в графе Terraform. + +--- + +## 2. Идеология Terraform: Почему это каноничный подход + +Разделение базовой сущности и отложенных настроек/связей на отдельные ресурсы — это официальный архитектурный паттерн Terraform (**Resource Association / Separate Resource Pattern**), используемый во всех провайдерах первого эшелона: +* **AWS**: `aws_security_group` (базовый контейнер) + `aws_security_group_rule` (отдельные правила привязки). +* **AWS**: `aws_vpc` + `aws_route_table_association` / `aws_vpn_gateway_attachment`. +* **GCP**: `google_project` + `google_project_iam_binding`. + +### Преимущества подхода: +1. **Естественный граф зависимостей (DAG)**: Terraform выстраивает порядок шагов исключительно между блоками `resource`. Вынос модификаций в отдельные ресурсы позволяет вклинивать промежуточные сервисы между созданием родителя и его донастройкой. +2. **Симметричный и безопасный `destroy`**: При удалении стека Terraform автоматически разворачивает порядок: + * Сначала удаляется `k8s_sthutrval_cluster`. + * Затем Modifier шлюза отключает SNAT. + * Затем Modifier организации освобождает выделенные IP. + * И только потом удаляются базовые шлюз, VDC и организация. +3. **Предсказуемый `plan` и локализация сбоев**: Любая ошибка настройки локализуется в блоке модификатора, не повреждая стейт базового инстанса. + +--- + +## 3. Правила определения входных данных Modifier-ресурса + +Входные данные Modifier-ресурса определяются строго детерминированно на основе официальной YAML-спецификации сервиса из API (`operations` -> `name: modify`). + +### Правило 1: Якорь привязки (`instance_id` / `_id`) +Каждый модификатор обязан содержать ровно один обязательный атрибут привязки: +* Имя: `instance_id` (или семантическое имя, например `org_id`, `nsxt_id`). +* Тип: `string` (UUID). +* В манифесте `.tf` значение передаётся как ссылка на атрибут родительского ресурса: + ```hcl + org_id = nubes_vc_org.main.id + ``` + Это гарантирует, что Terraform выполнит модификатор **строго после** создания родителя. + +### Правило 2: Строгая функциональная группа параметров +Операция `modify` в API может содержать множество разнородных параметров. Модификатор инкапсулирует **только одну целевую функциональную задачу**: +* **Для модификатора IP организации (`vc_org_ip_modifier`)**: + * Входные параметры берутся из секции `modify` YAML `vc_org`: массив `vIPConfigure` (`name`, `count`). +* **Для модификатора SNAT шлюза (`vc_nsxt_snat_modifier`)**: + * Входные параметры берутся из секции `modify` YAML `vc_nsxt`: `ipSpaceName`, `needEnableAVI`, `virtualServicesCount`, `routedNetConfiguration`. + +Все параметры операции `modify`, не относящиеся к данной задаче, в схему конкретного модификатора **не включаются**. + +### Правило 3: Наследование типов и валидаций из YAML +Схема атрибутов модификатора строится по существующей универсальной таблице типов провайдера: +* Обязательность (`required`), значения по умолчанию (`default`), регулярные выражения (`regex`) и диапазоны значений наследуются напрямую из спецификации параметров YAML. + +### Правило 4: Экспорт вычисляемых атрибутов (Computed Outputs) +Если модификатор формирует сущность, необходимую последующим шагам, он экспортирует её как `Computed`: +* `vc_org_ip_modifier` экспортирует `ip_space_name`. +* Модификатор шлюза может сослаться на него напрямую: + ```hcl + ip_space_name = nubes_vc_org_ip_modifier.ips.ip_space_name + ``` + +--- + +## 4. Жизненный цикл Modifier-ресурса в провайдере (CRUD) + +| Метод Terraform | Вызов API облака | Поведение | +|---|---|---| +| **Create** | `POST /api/v1/svc/{service_id}/{instance_id}/modify` | Отправляет payload с целевыми параметрами модификации. Запускает polling задачи до статуса успешного завершения. Сохраняет ID и параметры в State. | +| **Read** | `GET /api/v1/svc/{service_id}/{instance_id}` | Читает текущее состояние родительского инстанса. Извлекает значения целевых параметров (например, текущие IP или статус SNAT) и сверяет с State. | +| **Update** | `POST /api/v1/svc/{service_id}/{instance_id}/modify` | Вызывается при изменении атрибутов модификатора в `.tf` файле. Отправляет обновлённый payload и ожидает завершения задачи. | +| **Delete** | `POST /api/v1/svc/{service_id}/{instance_id}/modify` | **Откат настройки**: отправляет запрос на деактивацию конкретного функционала (отключение SNAT, обнуление/освобождение пула IP), не удаляя сам родительский инстанс. | + +--- + +## 5. Схема интеграции в конвейер провайдера + +Провайдер сохраняет архитектурную чистоту и принцип неизменяемости кода конкретных сервисов (**Immutability Policy**): + +``` + [ API Облака ] + │ + ▼ + TOOLS/scripts/01_generate_yamls.sh + │ + ▼ + [ generated//resources_yaml/ ] + (Спецификации стандартных сервисов) + │ + ┌───────────────────┴───────────────────┐ + ▼ ▼ + [ Универсальный Генератор ] [ Модуль Модификаторов ] + (Генерирует стандартные (Описывает схему и CRUD + *_resource.go сервисов) для Modifier-ресурсов) + │ │ + └───────────────────┬───────────────────┘ + ▼ + [ Точка сборки: provider.go ] + (Регистрация всех ресурсов в + едином списке Resources(ctx)) + │ + ▼ + TOOLS/scripts/03_build_... + │ + ▼ + [ Единый бинарный провайдер Nubes ] +``` + +### Шаги интеграции: +1. **Генерация стандартных ресурсов**: Универсальный генератор штатно обрабатывает YAML-спецификации сервисов, создавая основные ресурсы инстансов. +2. **Добавление кода модификаторов**: + * Файлы модификаторов реализуют интерфейс `resource.Resource` (Terraform Plugin Framework) и размещаются в кодовой базе провайдера. + * Они используют общее ядро клиента (`provider/core/`) для отправки запросов и трекинга асинхронных операций. +3. **Регистрация в провайдере**: + * В функции `Resources(ctx)` провайдера фабричные методы модификаторов (например, `NewVcOrgIpModifierResource`, `NewVcNnxtSnatModifierResource`) добавляются в общий срез доступных ресурсов наряду со стандартными ресурсами сервисов. +4. **Сборка**: + * Провайдер компилируется в один исполняемый файл. Для пользователя Terraform новые ресурсы доступны нативно: `nubes_vc_org_ip_modifier`, `nubes_vc_nsxt_snat_modifier`. + +--- + +## 6. Пример сквозного использования в HCL + +Итоговый пользовательский сценарий развертывания выглядит чисто, декларативно и прозрачно: + +```hcl +# 1. Создание Организации +resource "nubes_vc_org" "org" { + organization_type = "iaas" + resource_realm = "sandbox.nubes.ru" +} + +# 2. Создание VDC +resource "nubes_vc_vdc" "vdc" { + organization_uid = nubes_vc_org.org.id + network_provider = "default" + provider_vdc = "fast-2.8" + cpu_allocated = 80 + mem_allocated = 200 + + storage_config = [ + { + name = "fast" + size = 2000 + } + ] +} + +# 3. Создание базового Edge NSX-T (включение AVI и 4 SE) +resource "nubes_vc_nsxt" "edge" { + vdc_type = "vdc" + vdc_uid = nubes_vc_vdc.vdc.id + need_enable_avi = true + virtual_services_count = 4 + + routed_net_configuration = { + ip_addr_pool = "10.10.102.0/24" + main_dns = "8.8.8.8" + second_dns = "8.8.4.4" + } +} + +# 4. Модификатор Org: выделение 3 IP (выполняется после Edge) +resource "nubes_vc_org_ip_modifier" "org_ips" { + org_id = nubes_vc_org.org.id + + vip_configure = [ + { + name = "shturval-ip-space" + count = 3 + } + ] + + # Явная зависимость гарантирует готовность Edge + depends_on = [nubes_vc_nsxt.edge] +} + +# 5. Модификатор Edge: включение SNAT с ipSpace из шага 4 +resource "nubes_vc_nsxt_snat_modifier" "edge_snat" { + nsxt_id = nubes_vc_nsxt.edge.id + ip_space_name = nubes_vc_org_ip_modifier.org_ips.vip_configure[0].name + + need_enable_avi = true + virtual_services_count = 4 + + routed_net_configuration = { + ip_addr_pool = "10.10.102.0/24" + main_dns = "8.8.8.8" + second_dns = "8.8.4.4" + } +} + +# 6. Развертывание кластера Штурвал +resource "nubes_k8s_sthutrval_cluster" "cluster" { + startup_configuration = { + vdc_uid = nubes_vc_vdc.vdc.id + nsxt_uid = nubes_vc_nsxt.edge.id + cluster_name = "k8s-prod-cluster" + } + + control_plane_configuration = { + count = 1 + sizing_policy = "standard-cp" + sizing_disk = 50 + } + + worker_configuration = [ + { + count = 2 + sizing_policy = "standard-worker" + sizing_disk = 50 + label_deck = true + } + ] + + # Требует полной готовности сетевой связки и свободных IP + depends_on = [ + nubes_vc_nsxt_snat_modifier.edge_snat, + nubes_vc_org_ip_modifier.org_ips + ] +} +```