В раздел 6 (Подресурсы) добавлен подраздел про секреты, рождаемые операцией подресурса: - проблема: vault_secrets родителя — Computed, обновляется только в Read, поэтому после create_user пароль недоступен в том же apply (Invalid index); - принцип: пароль должен быть выходом самого подресурса, не читаться из родителя; - как привязано к данным, а не хардкодом: признак в YAML-спеке (по аналогии с suspend_on_destroy_default), условный блок в общем шаблоне; - факт из спеков: postgres = пароль генерит платформа (нужен выход), mariadb = пароль задаёт пользователь на входе (выход не нужен).
13 KiB
Terraform Provider Development Strategy & AI Integration
1. Vision: "The Helpful Kitchen Assistant"
The provider should not just be a silent executor of commands. It should act as an intelligent assistant for users of all skill levels (the "Cook" persona). It must provide context, warnings, and advice before any potentially destructive or wasteful actions are taken.
2. Advanced Pre-Apply Logic
To prevent "bad applies" and incomplete infrastructure states, the provider will implement:
A. Context-Aware Validation (Scanning the Cloud)
- Data Sources as Sensors: Use Data Sources not just for referencing existing resources, but as "sensors" to understand the current cloud state during
terraform plan. - Pre-emptive Warnings: Compare the proposed
planagainst current cloud limits, existing resource names, and regional availability. - Example: Trigger a warning if a user is creating a 6th bucket while 5 existing ones are empty.
B. Cascading & Logical Validation (Deep Validation)
- Attribute Inter-dependency: Check compatibility between linked resources (e.g., App vs DB versions, S3 permissions vs CDN requirements).
- Early Termination: If Resource A in a chain is logically "broken" or suboptimal, the provider should error out or warn on the entire chain before
applybegins.
3. Human-Centric Output & AI Insights
- Standardized Info:
terraform planshould output human-readable summaries of what's already in the cloud, not just what's being changed. - AI-Driven Advice:
- The provider outputs a structured JSON state.
- An AI Agent (Sub-chef) analyzes this JSON.
- Result: "Hey, I noticed you're creating a DB in Region A but your VMs are in Region B. This will cause latency. Want to fix it?"
4. Implementation via Taffy API Specifics
- Lifecycle Utilization: Leverage the 3-step Taffy lifecycle (
Operation -> Parameters -> Run). - Validation Step: Use the
/validate-cfsAPI endpoint during the TerraformPlanorCreatephase (before the finalRun) to ensure the cloud accepts the parameters without actually committing the change.
5. Reliability & Uncertainty Tracking
- All documentation and internal agent logic should use Confidence Scores (e.g.,
[CONFIDENCE: 85%]). - Explicitly mark
[UNCERTAINTY]tags for deprecated API features or areas where documentation contradicts observed behavior (from HAR/Traffic analysis).
This document serves as the architectural north star for the project.
6. Подресурсы (subresource) и поведение Terraform
Зачем нужен ForceNew
Для подресурсов вроде create_user, delete_user, create_database часто нет операции modify в API. Это значит, что при изменении ключевых полей (например, username или dbName) мы не можем обновить объект на месте — только удалить и создать заново.
ForceNew — это стандартный механизм Terraform: изменение таких полей приводит к замене ресурса (Delete + Create). Это предотвращает ложные обновления, когда Terraform считает, что всё изменилось, а в API реально ничего не выполнено.
Правило для генерации подресурсов
- Если у подресурса нет операции
modify, то все его параметры считаются ForceNew. - Если
modifyесть, то ForceNew получают параметры, которые участвуют в идентификации/удалении (ключи, по которым ресурс можно уникально определить).
Итоговое поведение
- Изменение ключевых параметров подресурса => ресурс заменяется.
- Это соответствует возможностям API и сохраняет корректность состояния Terraform.
Секреты, рождаемые операцией подресурса (принцип и практика)
Проблема (наблюдаемая, 2026-10-01): у части сервисов секрет (пароль пользователя
БД) генерируется платформой внутри операции create_user, а доступен он не в
выходе подресурса, а в vault_secrets родительского инстанса. При этом
vault_secrets родителя — Computed-атрибут, который обновляется только в Read
родителя (перечитывание облака на refresh/повторном apply), а не в конце
Create подресурса. Следствие: внутри одного apply после create_user пароль
нельзя прочитать — vault_secrets["users"] ещё пуст, и обращение к нему даёт
Invalid index.
Принцип (правило для генератора): секрет, который порождает операция
подресурса, должен становиться выходом самого подресурса сразу по завершении
его Create, а не читаться потребителем из родителя. Потребитель берёт пароль
из nubes_<svc>_<sub>.<sub>.password, и зависимость по графу Терраформа
автоматически разносит создание по времени.
Как это «прописано для postgres», а не хардкод в шаблоне. В общий шаблон
подресурса (TOOLS/resource-generator/internal/templates/subresource.go) НЕ
зашивается имя сервиса. Признак «у этого подресурса-пользователя пароль
генерируется платформой» должен приходить из данных спека (YAML), как уже
сделано для suspend_on_destroy_default / adopt_existing_on_create_default
(раздел 7). В шаблоне появится условный блок, срабатывающий только при наличии
этого признака.
Различия сервисов (факт из спеков):
| Сервис | Пароль пользователя | Что нужно |
|---|---|---|
| PostgreSQL (90) | генерирует платформа → vault_secrets["users"] |
выход пароля у postgres_user из Vault по завершении create_user |
| MariaDB (115) | задаёт САМ пользователь (входной параметр password в create_user) |
ничего — пароль уже в манифесте, nubes_mariadb_user.x.password |
Вывод: фича «выход пароля» нужна только подресурсам, у которых create_user
не принимает пароль на входе (то есть где он auto-generated). Хардкодить
перечень сервисов в шаблоне запрещено — признак декларируется в YAML-спеке сервиса.
7. Каноничная lifecycle-логика для сервисов с suspend/resume
Этот раздел обязателен для всех агентов, генераторов и разработчиков.
Если в других документах встречаются старые правила (resume_if_exists, delete_mode), приоритет всегда у этого раздела.
Область действия:
- Только
instance-ресурсы, у которых API поддерживаетsuspend/resume. - Для сервисов без
suspendдействует обычная логика create/modify/delete.
Флаги:
adopt_existing_on_create(optional, default:false) — разрешает усыновление ужеrunningресурса при create/apply.suspend_on_destroy(optional, default:true) — при destroy/удалении из манифеста выполнятьsuspendвместо удаления из state.
Правила по умолчанию:
- Никакого неявного adopt/import: без
adopt_existing_on_create=trueнайденныйrunningресурс считается конфликтом. - На destroy выполняется
suspendпо умолчанию (suspend_on_destroy=true). - Режим state-only допускается только при явном
suspend_on_destroy=false.
8. Обязательные правила apply / plan / destroy / modify
Apply/Create (instance с suspend/resume)
- Всегда проверить облако по
resource_nameперед create. - Если ресурс не найден в облаке или найден только в статусе
deleted— всегдаcreate. - Если ресурс в статусе
suspend:- при
adopt_existing_on_create=trueи совпадении основных параметров —resume+adopt, и явное сообщение в plan/apply; - при
adopt_existing_on_create=false— hard error (явно требовать включить флаг для resume/adopt); - при несовпадении — hard error с перечислением несовпавших ключей.
- при
- Если ресурс в статусе
running:- при
adopt_existing_on_create=true— adopt (import-поведение) с явным сообщением в plan; - при
adopt_existing_on_create=false— hard error "resource already exists".
- при
- Если ресурс в статусе
not created— hard error "проверьте ресурс в личном кабинете" (без auto-adopt/create). - Если ресурс в статусе
creating/pending/failed— hard error.
Формат диагностик (обязательно)
- Диагностики должны быть многострочными и читаемыми.
- В тексте обязательно указывать:
- итоговое решение (
adopt/resume/error), - какой флаг повлиял (
adopt_existing_on_create/suspend_on_destroy), - детализированные поля (
resource_name,service_id,instance_uid,status,status_raw,operation_pending,operation_in_progress) отдельными строками.
- итоговое решение (
- Для конфликта
resource already existsприadopt_existing_on_create=falseдиагностика обязана явно предлагать оба пути:- изменить
resource_name, если нужен новый ресурс; - включить
adopt_existing_on_create=trueи повторитьapplyдля импорта/усыновления существующего ресурса.
- изменить
Destroy / удаление из манифеста (instance с suspend/resume)
- Если
suspend_on_destroy=true— выполнитьsuspend. - Если
suspend_on_destroy=false— удалить только из Terraform state, без delete/suspend вызовов в API (осознанный override).
Modify
- При diff по immutable/create-only параметрам — hard error (replace запрещён).
- При diff только по mutable параметрам — выполнить
modify.
Replace
- Для сервисов с
suspendзапрещён любой implicit replace (delete+create). - Попытка replace должна завершаться ошибкой на этапе plan.
9. Контрольный список для реализации
Plan messages (обязательно)
- Явно указывать обнаруженный cloud status:
deleted/suspend/running/not created/creating. - Явно указывать, какой флаг повлиял на решение:
adopt_existing_on_createилиsuspend_on_destroy. - Для веток
resumeиadoptпоказывать причину выбора действия.
Safety guards
- Для
suspend+ mismatch не допускать auto-resume. - Для
runningбезadopt_existing_on_create=trueне допускать auto-adopt. - После
resume/adoptвыполнять read-back и обновлять state только по фактическому ответу API.
Терминология
resume_if_existsиdelete_modeсчитаются legacy-терминами и не используются в новой логике.