Files
tf_examples/AGENTS.md
T

375 lines
28 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# AGENTS.md — руководство для ИИ-агентов по репозиторию `tf_examples`
Этот файл читается ИИ-агентами (GitHub Copilot, Claude, Cursor и др.), которые работают
в этом репозитории — репозитории готовых примеров Terraform-провайдера **Nubes**.
**Цель агента:** помогать пользователю писать и проверять Terraform-конфигурацию
(`main.tf`, `variables.tf`, `terraform.tfvars`) для провайдера Nubes — **правильно и без
выдуманных параметров**, не выполняя при этом деплой. `terraform plan` и `terraform apply`
запускает **только пользователь**.
> Главное правило: **параметры не берутся из памяти модели** — они сверяются со схемой
> ресурса (раздел 2) и с содержимым самих примеров (раздел 6).
---
## 0. Кому адресован этот файл
Файл написан для **стороннего ИИ-агента, который не знает о предметной области ничего**.
Если ты такой агент — прочитай файл целиком, прежде чем советовать. Ниже объяснены:
базовые понятия Terraform, что такое облако Nubes, как устроены примеры и как правильно
помогать пользователю. Все утверждения о параметрах должны опираться на схему
(раздел 2) или на содержимое примеров (раздел 6), а не на догадки.
## 0.1. Базовые понятия Terraform (с нуля)
- **Terraform** — инструмент «инфраструктура как код» (Infrastructure as Code). Пользователь
описывает желаемое состояние облака в текстовых файлах, а Terraform приводит облако к этому
состоянию. Ничего не делается руками в интерфейсе — всё описано в коде.
- **HCL** — язык этих файлов (расширение `.tf`). Пример блока ресурса:
```hcl
resource "nubes_postgres" "main_pg" { … } # тип ресурса — nubes_postgres, локальное имя — main_pg
```
- **Провайдер** — плагин, через который Terraform общается с конкретным облаком. Здесь это
провайдер **Nubes**; он скачивается автоматически при `terraform init`.
- **Ресурс** — объект в облаке (кластер БД, виртуальная машина, бакет и т.п.).
- **Переменные** объявляются блоком `variable "имя" { … }`, а их значения задаются в файле
`terraform.tfvars` — это то, что заполняет пользователь под свою организацию.
- **Файл состояния** (`terraform.tfstate`) хранит соответствие «код ↔ реальные объекты».
Его **не редактируют вручную**.
- **Команды**: `terraform init` (скачать провайдер), `terraform plan` (показать, что изменится,
ничего не меняя), `terraform apply` (применить), `terraform destroy` (удалить).
- Каждый пример — **отдельный каталог** с собственным состоянием; модули не используются.
## 0.2. Что такое Nubes
**Nubes** — облачная платформа, предоставляющая услуги «как сервис»: базы данных
(PostgreSQL, MongoDB, Redis, MariaDB, ClickHouse), очереди (Kafka, RabbitMQ), Kubernetes
(«Штурвал»), виртуализацию VMware (vDC, Edge, виртуальные машины), объектное хранилище S3,
готовые приложения (Node.js, Flask, Lucee и др.) и сеть (DNS, внешние IP, SNAT).
- Управляется через **Личный кабинет (ЛК)** и **API**. Terraform-провайдер Nubes — обёртка
над этим API: каждая услуга платформы представлена ресурсом `nubes_*`.
- Ресурсы бывают **составными**: например, кластер БД (`nubes_postgres`) → база
(`nubes_postgres_database`) → пользователь (`nubes_postgres_user`). Часть ресурсов —
**модификаторы** уже существующих услуг (внешние IP, SNAT).
- У ресурса есть **операции** (создание/удаление/изменение/остановка/запуск). Провайдер сам
вызывает нужную операцию по командам Terraform.
- Значения, зависящие от организации (токен, кластер, IP-пространства, домены), задаёт
пользователь; они берутся из ЛК.
## 0.3. Зачем эти примеры и как их используют
Пользователь клонирует репозиторий, копирует `terraform.tfvars.example` → `terraform.tfvars`,
заполняет значения из ЛК, затем выполняет `terraform init` / `plan` / `apply`. Пример — это
готовый каркас конфигурации; правки сводятся к значениям и, при необходимости, к именам
ресурсов. Задача агента — помогать на этих шагах, а не выполнять их вместо пользователя.
---
## 1. Что это за репозиторий
Здесь лежат законченные сценарии развёртывания инфраструктуры в облаке Nubes через
Terraform. Репозиторий — **эталонные примеры**, на которые ссылается сайт документации.
| Папка | Что это | Состояние | Документация |
|---|---|---|---|
| **`fullpipe_chain/`** | vDC → Edge → внешние IP → SNAT → vApp → ВМ → Штурвал (полная цепочка) | **рекомендовано использовать** — повторяет рабочую конфигурацию стенда | `/nubes-dev/curated/pipeline/vdc_edge_ip_snat/` |
| **`Maria_CRUD/`** | MariaDB + три приложения (Lucee/Flask/Node.js) + HTTP-проверка | проверен на TEST-стенде | `/nubes-test/curated/mariadb/three_apps/` |
| `CRUD/` | PostgreSQL + три приложения (Lucee/Flask/Node.js) | черновик (устаревшая схема, старый пин провайдера) — **с оговорками** | `/nubes-test/curated/crud/three_apps/` |
| `SHTURVAL_MGMT/` | management-кластер Kubernetes через сервис «Штурвал» (148) | черновик | — |
| `iot-rmq-demo/` | «Умный дом»: RabbitMQ + Redis + MongoDB + 3 Node.js-приложения | черновик | — |
| `modify_resources/` | внешние IP на организации + SNAT на шлюзе (для уже существующих услуг) | черновик | — |
**Что рекомендовать пользователю:** `fullpipe_chain` (полный цикл) или `Maria_CRUD`
(проверенный CRUD-пример). Остальные папки — черновики от прошлых работ: могут содержать
неактуальные версии провайдера и работать некорректно. Не выдавай черновик за рабочий образец.
---
## 2. Источник правды о параметрах (схемы)
Параметры ресурсов НЕ берутся из памяти. Их берут из схемы:
| Источник | Что внутри |
|---|---|
| Страница ресурса на сайте доков | `https://tf-docs.nodejsk8s.dev.nubes.ru/<stand>/<service>/` — таблица атрибутов = схема |
| Машиночитаемая схема (JSON) | `https://tf-docs.nodejsk8s.dev.nubes.ru/<stand>/schema/<service>.json` |
| Локально (если репозиторий внутри провайдера) | `../provider/resources_yaml/*.yaml` и `../stack/<stand>/<service>/raw.json` |
Структура `raw.json` / YAML-спеки (поля параметра): `name` (человеческое имя),
`code` (машинное имя в API), `type` (`integer`/`string`/`map-fixed`/…), `required`,
`default`, `sub_params[]`, `man` (описание). Тип ресурса в HCL — это `resource_type`
(вида `nubes_*`); поля ресурса — `create.params[]`.
Стенды: `nubes-test` (тест), `nubes-dev` (dev), `nubes` (prod).
---
## 3. Инвентарь ресурсов провайдера (`nubes_*`)
Если пользователь называет ресурс, которого нет в этом списке — его не существует
(например, `nubes_instance`, `nubes_floating_ip` — таких нет).
| Ресурс | `resource_type` |
|---|---|
| PostgreSQL | `nubes_postgres`, `nubes_postgres_user`, `nubes_postgres_database` |
| MongoDB | `nubes_mongodb`, `nubes_mongodb_user`, `nubes_mongodb_backup` |
| Redis | `nubes_redis` |
| MariaDB | `nubes_mariadb`, `nubes_mariadb_user`, `nubes_mariadb_database` |
| ClickHouse | `nubes_clickhouse` |
| Kafka | `nubes_kafka`, `nubes_kafka_topic`, `nubes_kafka_user` |
| RabbitMQ | `nubes_rabbitmq` |
| S3 | `nubes_s3`, `nubes_s3bucket`, `nubes_s3_sub_user` |
| Сеть | `nubes_zones_v2`, `nubes_dnsrecord`, `nubes_vcexternalip` |
| VMware / Cloud Director | `nubes_vc_org`, `nubes_vc_vdc`, `nubes_vc_vdc_group`, `nubes_vc_nsxt`, `nubes_vc_nsxt_snat`, `nubes_vc_org_ip_allocation`, `nubes_vapp`, `nubes_vc_vm_v3` |
| Kubernetes (Штурвал) | `nubes_k8s_sthutrval_cluster`, `nubes_vc_mgmt_sthutrval_cluster`, `nubes_k8s_velero`, `nubes_k8s_openbao` |
| Приложения | `nubes_nodejs`, `nubes_flask`, `nubes_lucee`, `nubes_nextcloud`, `nubes_gitea`, `nubes_harbor`, `nubes_superset`, `nubes_pgadmin`, `nubes_akhq`, `nubes_http` |
| Прочее | `nubes_llm_ai`, `nubes_valo_tenant`, `nubes_template`, `nubes_dummy` |
Полный актуальный список — в `provider/resources_yaml/` (у провайдера) или на сайте доков.
---
## 4. Структура примера и назначение файлов
```
tf_examples/<example>/
README.md # что делает пример, порядок запуска, что заполнить
main.tf # провайдер + объявление переменных
variables.tf # описания и типы переменных (где есть)
<ресурсы>.tf # ресурсы (например postgres.tf, vm.tf, resources.tf)
locals.tf # настраиваемые значения (имена, домены, git-ревизии)
terraform.tfvars.example # ШАБЛОН параметров; копируется в terraform.tfvars
terraform.tfvars # реальные значения — в .gitignore, НЕ коммитится
```
Ключевая мысль: пользователь копирует `terraform.tfvars.example` в `terraform.tfvars` и
заполняет. Всё, что «постоянное» (имена ресурсов, размеры), лежит в `.tf`/`locals.tf`;
все значения, зависящие от организации пользователя (токен, кластер, IP-пространства) —
в `terraform.tfvars`.
---
## 5. Конфигурация провайдера (одинакова во всех примерах)
```hcl
terraform {
required_providers {
nubes = {
source = "tf-registry.containerk8s.services.ngcloud.ru/<stand>/nubes"
version = "<версия>"
}
}
}
provider "nubes" {
api_token = var.api_token # JWT из ЛК (ЛК → Профиль → Токены → «Технический»)
api_endpoint = "https://lk-api-gateway-<stand>.ngcloud.ru/api/v1/svc"
}
```
- `api_token` — **всегда** из `terraform.tfvars`/`secrets.auto.tfvars`, никогда не в git.
- `realm` — ресурсная платформа (кластер Kubernetes), должна существовать в ЛК.
- `api_endpoint` — у тестового стенда `...-test.ngcloud.ru`, у прода — без суффикса.
---
## 6. Разбор примеров (что создаётся, что задаёт пользователь, грабли)
### 6.1. `fullpipe_chain` — полная цепочка (рекомендуемый)
Создаёт: **vDC → Edge (AVI) → внешние IP на организации → SNAT → vApp → ВМ → кластер Штурвал**.
Организацию пользователь создаёт заранее в ЛК (Terraform её не создаёт и не удаляет).
Обязательные значения `terraform.tfvars`:
| Переменная | Где брать |
|---|---|
| `api_token` | ЛК → Профиль → Токены → «Технический» |
| `organization` | точное название услуги «Организация в Cloud Director» |
| `vdc_network_provider` | ЛК → создание vDC → «Сетевой провайдер» |
| `vdc_provider_vdc` | ЛК → создание vDC → «Provider VDC» (тот же ресурсный пул) |
| `vdc_storage_config` | JSON-массив: `[{"name":"SATA","size":"200"}]` (дисковая политика + ГБ) |
| `ip_space_name` | имя ipSpace из карточки организации |
| `vm_user_public_key` | содержимое `~/.ssh/id_ed25519.pub` |
Порядок: `terraform init` → `validate` → `plan` (7 ресурсов) → `apply`.
Проверка: `terraform output` (UUID/имена услуг), ВМ — `terraform output vm_state_flat`
(`externalConnect` для SSH), Штурвал — `shturval_state_params` (`kubernetesApiAddress`, `ingressAddress`).
Кластер Штурвала создаётся **десятки минут** (`operation_timeout = "60m"`).
`destroy` по умолчанию = **«заморозка»**: почти ничего не удаляется (см. раздел 10).
Полное удаление — осознанное: снять `keep_on_destroy`/`suspend_on_destroy`, порядок —
кластер → ВМ → vApp → `count = 0` → SNAT → Edge → vDC.
### 6.2. `Maria_CRUD` — CRUD на MariaDB (проверенный)
Создаёт: кластер **MariaDB 9.4.0**, базу `maria`, пользователя `cruduser` (ALL PRIVILEGES),
три приложения (**Lucee / Flask / Node.js**) и контейнер **HTTP-проверки** (`nubes_http`).
Все приложения пишут в одну таблицу `crud_items`.
Обязательные `terraform.tfvars`: `api_token`, `realm`, `mariadb_user_password` (пароль БД).
Проверка работоспособности: `curl .../readyz` у HTTP-проверки → `{"status":"ready","database":"connected"}`.
**Грабли (важно объяснять пользователю):**
- логин БД — **без `_`** (`cruduser`, не `crud_user`): `_` ломает RFC 1123 в имени k8s-объекта;
- креды берутся из подресурса `nubes_mariadb_user`, а не из `vault_secrets` кластера
(внутри одного `apply` `vault_secrets` ещё пуст → `Invalid index`);
- `retain` бэкапа — **в часах**, минимум `24`;
- версия MariaDB — только `8.4.0` или `9.4.0` (можно только повышать);
- `db_name` пользователя — JSON-массив: `jsonencode([{ dbname = "maria" }])`;
- `registry_path` образа — **полный** путь (`<реестр>/<namespace>/<образ>:<тег>`);
- `adopt_existing_on_create` — повторный `apply` усыновляет существующий инстанс (не падает).
### 6.3. `CRUD` — PostgreSQL + три приложения
Создаёт: PostgreSQL (`nubes_postgres`), пользователя и БД, приложения Lucee/Flask/Node.js.
Особенность: PostgreSQL не успевает отдать пароль до старта приложений — нужен
**двойной `terraform apply`** (или два этапа через `*_git_revision`). Домены приложений
задаются в `locals.tf` (должны быть уникальны). Папка — черновик (старая плоская схема,
старый пин провайдера): использовать осторожно, при вопросах сверяться с `Maria_CRUD`.
### 6.4. `SHTURVAL_MGMT` — management-кластер Kubernetes
Создаёт `nubes_vc_mgmt_sthutrval_cluster` (сервис **148**) — management-кластер «Штурвал».
`terraform.tfvars`: `api_token`, `realm` (для dev-стенда `sandbox.nubes.ru`), `sizing_policy`
(`TKG 4CPU 8RAM` / `8CPU 16RAM` / `16CPU 32RAM`). Настройки кластера — в `locals.tf`
(`cp_count`, `app_version`, `worker_config`, IP-списки доступа).
### 6.5. `iot-rmq-demo` — «Умный дом» (6 сервисов)
Пайплайн: **Producer** (Node.js) → **RabbitMQ** (`iot-events`, durable) → **Consumer** (Node.js)
→ параллельно **Redis** (кэш: читает Dashboard) и **MongoDB** (архив с TTL 7 дней) →
**Dashboard** (графики Chart.js). Все связи внутренние (K8s Service), наружу — только HTTP.
`terraform.tfvars`: `api_token`, `realm`, `api_endpoint` (закомментирован). В `locals.tf`
меняются: git-пути приложений, **уникальные** домены и имена инстансов.
Примечание: MongoDB без пароля (демо); `suspend_on_destroy = true` — `destroy` усыпляет.
### 6.6. `modify_resources` — IP и SNAT на существующих услугах
Два ресурса для **уже созданных** в ЛК услуг: `nubes_vc_org_ip_allocation` (внешние IP
на организации) и `nubes_vc_nsxt_snat` (SNAT на шлюзе).
`terraform.tfvars`: `api_token`, `organization`, `nsxt_uid` (UUID шлюза Edge из карточки
услуги), `ip_space_name`, `ip_count`.
---
## 7. Секреты — жёсткое правило
1. `api_token`, пароли, ключи, приватные ключи **никогда** не попадают в `terraform.tfvars`
в репозитории и тем более в git.
2. Реальные значения — в локальном **`secrets.auto.tfvars`** (Terraform загружает файлы
`*.auto.tfvars` автоматически). Этот файл — в `.gitignore`.
3. Значения-заглушки (`<token>`, `eyJhbGciOi...`, `AAAA...`) — это примеры; их заменяет
пользователь на свои.
4. Если файл передаётся внешнему сервису/ЛЛМ — только **без секретов**.
---
## 8. Workflow агента (проверка/написание файла)
Когда просят «проверить/написать `terraform.tfvars` (или `.tf`) для X»:
1. **Определи ресурс** и найди его схему (раздел 2): `resource_type` + `params[]`.
2. **Сверь каждое поле** со схемой: имя (`code`/`name`), тип, `required`. Пропуск
обязательного поля — ошибка; поле не из схемы — ошибка/предупреждение.
3. **Значения не выдумывай.** Не подставляй «правдоподобные» имена кластеров, IP,
UUID, домены — их даёт пользователь или ЛК.
4. **Проверь слои**: провайдер-конфиг (`api_token`/`realm`/`api_endpoint`), параметры ресурса,
зависимости (например, база должна существовать до пользователя — `depends_on`).
5. **Верни результат**: исправленный файл целиком + список изменений + предупреждения.
6. **`plan`/`apply` — пользователь.** Агент не деплоит и не удаляет.
Дополнительно: проверить файл можно и через сервис — `POST /<stand>/_api/file`
(`{"content":"...","instruction":"..."}` → `{content, changes[], warnings[]}`), см. раздел 11.
---
## 9. Типичные ошибки (реальные, встречаются в примерах)
- **Секрет в `terraform.tfvars`** (`api_token`, пароли) вместо `secrets.auto.tfvars`.
- **`realm` не существует в ЛК** → «Ресурсной платформы … нету в Личном кабинете»
(при этом `/resourceRealms/available` может её показывать — доверяй ЛК, не API-списку).
- **Логин БД с `_`** (`crud_user`) → платформа падает на имени k8s-объекта. Только буквы/цифры.
- **Короткий `registry_path`** (без хоста реестра) → образ «не находится».
- **`s3_uid`, которого нет на стенде** → `invalid input syntax for type uuid`.
- **Домен занят** (не уникален) → ошибка деплоя приложения.
- **Пропущен `required`-параметр** (`cluster_configuration` и т.п.) → `plan`/`apply` падает.
- **Опечатка в имени поля** (`clusterConfig` вместо `clusterConfiguration`) → поле игнорируется.
- **Эмодзи (4-байтовые) в MariaDB-примере** → `Incorrect string value` (частичная utf8mb3-таблица).
- **Асинхронные сбои платформы** («Kubernetes cluster unreachable», IAM-сбои) — не всегда
вина конфигурации: повтор команды обычно проходит.
---
## 10. Жизненный цикл ресурсов (что объяснять пользователю)
Провайдер управляет не только созданием, но и «судьбой» ресурса при удалении:
- **`suspend_on_destroy = true`** — `destroy` не удаляет, а **останавливает** (данные сохраняются).
Повторный `apply` усыновляет и размораживает.
- **`keep_on_destroy = true`** — ресурс при `destroy` **не трогается вовсе**
(например, Edge, SNAT, квота IP, пользователь/база внутри кластера).
- **`adopt_existing_on_create = true`** — если инстанс с таким именем уже есть, провайдер
его **усыновит**, а не упадёт с «уже существует».
- **Организация в Cloud Director** создаётся вручную в ЛК — Terraform её не создаёт и не удаляет.
Практический вывод: `terraform destroy` в этих примерах по умолчанию **не удаляет данные** —
это «заморозка». Полное удаление требует явно снять флаги и соблюсти порядок.
---
## 11. Карта документации и сервисов
- Ресурс на сайте доков: `https://tf-docs.nodejsk8s.dev.nubes.ru/<stand>/<service>/`
(таблица атрибутов = схема); например `/nubes-test/postgres/`.
- Машиночитаемая схема: `…/<stand>/schema/<service>.json`.
- Готовые примеры (curated): `…/<stand>/curated/…` — приоритетны (проверены на деплоях).
- Стенды: `nubes-test`, `nubes-dev`, `nubes` (prod).
- Проверка файла через ЛЛМ: `POST /<stand>/_api/file` (`{content, instruction}`) → `{content, changes, warnings}`.
- Справка по документации: `POST /<stand>/_api/assist` (ответ по шаблону Роль/Задача/Контекст/Требования/Ограничения).
---
## 12. Чего агент делать НЕ должен
- **Деплоить**: `terraform apply`/`destroy` — только пользователь.
- **Удалять/менять** реальные ресурсы и состояние Terraform.
- **Читать секреты** из файлов пользователя и подставлять их в вывод.
- **Выдумывать** параметры, значения, домены, UUID без источника (схема/ЛК/пользователь).
- **Навязывать свой workflow** — помощь и проверка, а не «делай так, как я сказал».
---
## 13. Глоссарий
| Термин | Значение |
|---|---|
| `realm` / ресурсная платформа | кластер Kubernetes, в котором разворачивается ресурс |
| `resource_name` | имя услуги в ЛК; уникально внутри своего сервиса; используется для усыновления |
| `sizing_policy` | профиль ресурсов (`TKG 4CPU 8RAM` и т.п.) |
| `cluster_configuration` | размеры кластера: `cpu` (мили-ядра, 1000 = 1 vCPU), `memory` (МБ), `replicas`, `disk` (ГБ), `shards` |
| `ipSpace` | пул внешних IP организации |
| `vDC` | виртуальный датацентр (Cloud Director) |
| `Edge` | сетевой шлюз периметра (NSX-T), балансировщик AVI |
| `SNAT` | трансляция исходящего трафика через шлюз |
| `vApp` | виртуальный каталог; контейнер для ВМ |
| `suspend` | остановка ресурса с сохранением данных |
| `adopt` | «усыновление» уже существующего ресурса по имени |
| **ЛК** | Личный кабинет Nubes — веб-интерфейс управления услугами |
| **услуга / инстанс** | конкретный объект в облаке, созданный по ресурсу |
| **операция** | действие платформы над услугой: `create`/`delete`/`modify`/`suspend`/`resume` и др. |
| `resource_name` | имя услуги в ЛК; по нему провайдер находит и усыновляет объект |
| `startup_configuration` | параметры размещения (например `resource_realm` — кластер Kubernetes) |
| `access_configuration` | сетевой доступ: IP-списки, внешний IP |
| `backup_configuration` | резервное копирование: `schedule` (cron), `retain` (срок хранения) |
| `json_env` | переменные окружения приложения (JSON) |
| `git_revision` | версия кода приложения; смена значения → redeploy |
| `domain` | доменное имя приложения (полный суффикс платформа добавляет сама) |
| **HCL** | язык конфигурации Terraform (файлы `.tf`) |
| `terraform.tfvars` | файл значений переменных; в этом репозитории — в `.gitignore` |
| `terraform.tfstate` | файл состояния Terraform; хранит соответствие «код ↔ облако» |