# Оркестрация взаимозависимых ресурсов и пошаговых модификаций (на примере Штурвал) ## 1. Контекст и проблематика ### Исходная последовательность развертывания Для развертывания инстанса сервиса **Штурвал** требуется подготовить сетевую и виртуальную инфраструктуру, состоящую из трёх взаимозависимых компонентов: 1. `vcOrg/create` — создание виртуальной организации (vCD Org). 2. `vcVdc/create` — создание виртуального дата-центра (vDC) внутри организации. 3. `vcNsxt/create` — создание сетевого шлюза NSX-T (включение AVI, выделение 4 Service Engine). 4. `vcOrg/modify` — модификация организации (добавление 3 внешних IP-адресов). 5. `vcNsxt/modify` — повторная модификация NSX-T (включение SNAT, привязка выделенного `ipSpace` из `vcOrg`). 6. `Штурвал/create` — создание кластера сервиса «Штурвал». ### В чём архитектурная сложность для Terraform В стандартной декларативной модели Terraform каждый ресурс управляется монолитно: один блок `resource` соответствует полному жизненному циклу одной сущности (Create -> Read -> Update -> Delete). В описанном сценарии возникает **чередующаяся (interleaved) зависимость**: * `vcOrg` должен существовать до `vcVdc` и `vcNsxt`. * Но добавление IP-адресов в `vcOrg` (шаг 4) и настройка SNAT в `vcNsxt` (шаг 5) должны выполняться **после** создания базового `vcNsxt` (шаг 3). * Штурвал (шаг 6) требует, чтобы и IP-адреса, и SNAT уже были применены. Если пытаться упаковать шаги 1 и 4 в один ресурс `cloud_vc_org`, а шаги 3 и 5 — в один `cloud_vc_nsxt`, возникает тупик в графе зависимостей Terraform (Directed Acyclic Graph, DAG), либо API вернет ошибку из-за несвоевременного вызова параметров. --- ## 2. Архитектурное решение: Паттерн отдельных ресурсов модификации (Subresource / Action Pattern) Канонический подход в экосистеме Terraform (аналогично `aws_security_group` + `aws_security_group_rule`, `aws_vpc` + `aws_route`) — **декомпозиция отложенных действий и привязок в отдельные управляемые ресурсы провайдера**. ### Структура ресурсов 1. **Базовые ресурсы жизненного цикла (Core Instances):** * `cloud_vc_org` — создает и держит базу организации. * `cloud_vc_vdc` — создает VDC внутри Org. * `cloud_vc_nsxt` — создает NSX-T шлюз (AVI, 4 SE). 2. **Ресурсы отложенной конфигурации / модификаций (Action / Subresources):** * `cloud_vc_org_ip_allocation` (или `cloud_vc_org_modify_ip`) — управляет пулом выделенных IP-адресов организации. * `cloud_vc_nsxt_snat` (или `cloud_vc_nsxt_modify_snat`) — управляет правилом SNAT и связкой с `ip_space`. 3. **Целевой сервис:** * `cloud_shturval` — разворачивает кластер Штурвал. ### Пример манифеста HCL ```hcl # 1. Создание организации resource "cloud_vc_org" "org" { name = "demo-org" } # 2. Создание VDC resource "cloud_vc_vdc" "vdc" { name = "demo-vdc" org_id = cloud_vc_org.org.id } # 3. Создание NSX-T (включение AVI и 4 Service Engine) resource "cloud_vc_nsxt" "nsxt" { name = "demo-nsxt" vdc_id = cloud_vc_vdc.vdc.id enable_avi = true service_engines = 4 } # 4. Модификация vcOrg: добавление 3 IP после готовности NSX-T resource "cloud_vc_org_ip_allocation" "org_ips" { org_id = cloud_vc_org.org.id ip_count = 3 # Явная зависимость гарантирует выполнение после создания NSX-T depends_on = [cloud_vc_nsxt.nsxt] } # 5. Модификация vcNsxt: включение SNAT с ipSpace из vcOrg resource "cloud_vc_nsxt_snat" "snat" { nsxt_id = cloud_vc_nsxt.nsxt.id ip_space = cloud_vc_org_ip_allocation.org_ips.ip_space_id enabled = true } # 6. Создание сервиса Штурвал resource "cloud_shturval" "cluster" { name = "demo-shturval" vdc_id = cloud_vc_vdc.vdc.id # Зависит от полной готовности сетевой связки depends_on = [ cloud_vc_nsxt_snat.snat, cloud_vc_org_ip_allocation.org_ips ] } ``` Terraform самостоятельно строит идеальный граф исполнения: ```mermaid graph TD A[cloud_vc_org] --> B[cloud_vc_vdc] B --> C[cloud_vc_nsxt] C --> D[cloud_vc_org_ip_allocation] D --> E[cloud_vc_nsxt_snat] E --> F[cloud_shturval] ``` --- ## 3. Интеграция в провайдер ### Реализация через генератор провайдера Согласно политике репозитория (Immutability Policy), код конкретных ресурсов не правится вручную, а генерируется: 1. В схему генератора добавляются описания новых сущностей: * Тип `action` или `subresource` для вызова эндпоинтов модификации. * Контракты входных/выходных атрибутов (`org_id`, `ip_count`, `ip_space_id`, `nsxt_id`, `enabled`). 2. Кодогенератор генерирует стандартные CRUD-структуры Terraform Plugin Framework / SDK. ### Жизненный цикл ресурсов модификации * **Create**: - Вызывает соответствующий API-метод (`POST /api/v1/vcOrg/{id}/modify` или `/api/v1/vcNsxt/{id}/modify`). - Дожидается применения задачи (task tracking / polling). - Сохраняет идентификатор операции или полученный `ip_space_id` в Terraform State. * **Read**: - Запрашивает текущее состояние родительского ресурса через GET API. - Проверяет, выделены ли IP / активен ли SNAT. * **Update**: - Если меняется количество IP или настройки SNAT — отправляет повторный запрос на модификацию. * **Delete (terraform destroy)**: - При уничтожении инфраструктуры порядок разворачивается в обратную сторону. - Сначала удаляется `cloud_shturval`. - Затем `cloud_vc_nsxt_snat` отключает SNAT. - Затем `cloud_vc_org_ip_allocation` освобождает выделенные IP. - И только затем удаляются базовые `vcNsxt`, `vcVdc` и `vcOrg`. --- ## 4. Альтернативные подходы 1. **Smart Provider (комбинированный Create)**: - Если API позволяет вызывать шаги последовательно внутри одного HTTP-сеанса бэкенда, провайдер мог бы скрыть это внутри `Create` ресурса `cloud_shturval`. - *Минус*: теряется гибкость и прозрачность статусов; сбой на промежуточном этапе оставляет "зависшие" ресурсы в облаке без записи в tfstate. 2. **Модули Terraform (Module Wrapper)**: - Описанная выше структура ресурсов упаковывается в официальный Terraform-модуль `terraform-nubes-shturval`, скрывая сложность связей от конечного пользователя и предоставляя простой интерфейс ввода параметров.