# Инструкция по добавлению нового сервиса в Terraform-провайдер Nubes ## 1. Где прописывать Единственная точка входа — `devops/config/services_list.txt` (или профильный `profiles/{stand}/services_list.txt`). Формат строки: ``` {service_id} {service_name} # Описание (опционально) ``` Пример: ``` 90 postgres # Управляемая база данных PostgreSQL ``` - **service_id** — число, ID сервиса из API Nubes (`/services/{id}`) - **service_name** — snake_case, латиница. Будет использоваться как имя ресурса `nubes_{name}` Исключение сервиса — закомментировать строку `#`. --- ## 2. Что происходит после добавления в список Пайплайн (3 шага): ``` services_list.txt → 01_generate_yamls.sh → запрос к API → resources_yaml/{id}_{name}.yaml → 02_generate_resources_and_docs_v2.sh → internal/resources_gen/{name}_resource.go + docs → 03_build_and_upload_provider.sh → сборка + S3 ``` **Никаких ручных правок YAML или сгенерированного Go-кода.** Всё из API. --- ## 3. Структура YAML (что генерируется) ```yaml name: postgres # snake_case, из services_list.txt service_id: 90 # ID сервиса service_display_name: PostgreSQL # человекочитаемое имя service_short_name: postgres # краткое имя из API service_man: "описание..." # MAN-руководство (HTML) lifecycle: suspend_on_destroy_default: true # есть ли операция suspend adopt_existing_on_create_default: false outputs: params: # выходные параметры (всегда одинаковые) - code: state_params; type: map - code: state_out; type: map - code: vault_secrets; type: map; sensitive: true - code: vault_url; type: string ... operations: - name: create # имя операции из API (snake_case) id: 19 # svcOperationId kind: instance # instance | subresource | action action: create # create | modify | delete | suspend | ... man: "руководство" # описание операции params: - id: 788 # svcOperationCfsParamId code: clusterConfiguration data_type: map-fixed required: true sort: 20 is_modifiable: true ``` --- ## 4. Типы параметров (data_type) | Тип | Пример | Когда использовать | |---|---|---| | `string` | `resourceRealm`, `domain`, `dbName` | Имена, домены, UUID, строки | | `integer > 0` | `resourceCPU`, `resourceMemory` | Числовые значения больше нуля | | `integer >= 0` | `autoScaleTechWindow` | Числовые, допускающие 0 | | `boolean` | `enablePgPoolerMaster`, `allowNoSsl` | Флаги вкл/выкл | | `uuid` | `s3Uid`, `vdcUid` | Ссылки на другие сервисы (ref) | | `map-fixed` | `clusterConfiguration` | **Сложные K8s-сервисы**: JSON-объект с фиксированным набором полей | | `array-map-fixed` | `postgresConf` | Массив JSON-объектов (доп. конфигурации) | | `json` | `jsonEnv`, `jsonParameters` | Произвольный JSON | | `map` | `state_params` | Только в outputs | | `yaml` | — | YAML-строка (редко) | --- ## 5. Виды операций (kind) ### instance — управление жизненным циклом сервиса | action | Terraform | Пример | |---|---|---| | `create` | `resource "nubes_X" "Y" {}` | Создание сервиса | | `delete` | `terraform destroy` | Удаление | | `modify` | изменение параметров → `apply` | Модификация | | `suspend` | `suspend_on_destroy = true` | Остановка (без удаления) | | `resume` | `adopt_existing_on_create = true` | Запуск остановленного | **Правило**: если у сервиса есть `suspend` И `resume` → `suspend_on_destroy_default = true`. ### subresource — вложенные объекты Стандартные subresource'ы: - **user** → `nubes_{service}_user` (create/delete) - Параметры: `username` (string, required), `role` (string, required, value_list) - **database** → `nubes_{service}_database` (create/delete) - Параметры: `dbName` (string, required, regex), `dbOwner` (string, required) - **topic** → Kafka - **backup** → S3, PostgreSQL - **vdc** → vcOrg **Правило**: subresource всегда ссылается на родительский ресурс через `{service}_id`. ### action — разовые операции | action | Когда | |---|---| | `reconcile` | Синхронизация состояния (у 20 сервисов) | | `redeploy` | Переразвёртывание (приложения: flask, nodejs, lucee, ...) | | `restart` | Перезапуск (postgres, mariadb, ...) | | `recovery` | Восстановление из бэкапа | **Правило**: action-ресурсы используют trigger-поле (`run_id`/`nonce`) для идемпотентности. --- ## 6. Валидация параметров (автоматически из API) | Механизм | Параметров | Пример | |---|---|---| | `regex` | 29 | `dbName: ^[A-Za-z0-9]+$` | | `value_list` | 38 | `role: [app_user, ddl_user]` | | `minlength`/`maxlength` | 87/74 | `username: min 2, max 62` | | `minvalue`/`maxvalue` | — | `resourceCPU > 0` | | `default` | 120 | `deleteS3Bucket: true` | | `is_modifiable` | 106 | Можно менять после create | | `is_sensitive` | 2 | `vault_secrets` | --- ## 7. Что запрещено - ❌ **Править сгенерированные YAML вручную** — источник истины только API - ❌ **Править `internal/resources_gen/*.go` вручную** — перезапишется при следующей генерации - ❌ **Использовать кириллицу в `service_name`** — только латиница, snake_case - ❌ **Менять `embed.go`** — генерируется автоматически - ❌ **Пропускать сервис через комментарий без причины** — лучше явно указать причину в комментарии: `# 24 DEPRECATED ...` --- ## 8. Быстрый старт: добавляем новый сервис ```bash # 1. Добавить строку в services_list.txt echo "200 my_new_service # Моя новая услуга" >> devops/config/services_list.txt # 2. Сгенерировать YAML (test-стенд) devops/01_generate_yamls.sh --profile devops/profiles/test # 3. Сгенерировать Go-код + доки devops/02_generate_resources_and_docs_v2.sh --profile devops/profiles/test # 4. Проверить что появился файл ls devops/profiles/test/generated/resources_yaml/200_my_new_service.yaml ls devops/profiles/test/generated/go/200_my_new_service_resource.go # 5. Собрать и задеплоить провайдер devops/03_build_and_upload_provider.sh --profile devops/profiles/test # 6. Написать тестовый манифест в TEST_STAND/my_new_service/main.tf # 7. terraform init && terraform plan && terraform apply ``` --- ## 9. Как писать тестовый манифест ### Простой сервис (flat-параметры) ```hcl resource "nubes_s3bucket" "test" { resource_name = "Мой бакет" s3_user_uid = var.s3_uid bucket_name = "test-bucket" } ``` ### Сложный сервис (map-fixed) ```hcl resource "nubes_postgres" "test" { resource_name = "pg-test" cluster_configuration = jsonencode({ platform = "k8s-3.ext.nubes.ru" instances = 1 memory = 512 cpu = 500 disk = "1" }) startup_configuration = jsonencode({ appVersion = "17" }) access_configuration = jsonencode({ needExternalAddressMaster = false }) # ... остальные блоки } ``` **JSON-ключи внутри map-fixed** — camelCase-версии старых плоских названий параметров. Точные имена полей смотри в сгенерированном YAML (поле `code` у параметров в `operations.params`) или в `generated/docs/{service}_params_create.md`.