Files
tf_provider/TEST_STAND/CRUD
Repinoid 4834e992f3 test_stand/crud: разделение на pg/ (общая БД) и apps/ (потребители)
Было: одна папка, один state — destroy убивал и БД, и приложения; пароль БД
приходилось вытаскивать костылём try() в одном apply, приложения поднимались
с пустым PGPASSWORD.

Стало:
- pg/  — кластер PostgreSQL + пользователь + база + outputs (хост/порт/юзер/база/пароль);
  destroy здесь переводит кластер в Suspend, а пользователь и база не удаляются
  (keep_on_destroy = true + adopt_existing_on_create = true);
- apps/ — три приложения-потребителя; креды БД читаются из creds.json (выгрузка
  outputs папки pg/, т.к. state раздельные, а data-source у провайдера нет);
  try() убран полностью — пароль к моменту этого apply уже существует;
- apps/.gitignore — creds.json, terraform.tfvars, state/lock;
- README.md переписан: структура, «зачем разделено», пошаговые 3 шага запуска,
  повседневные операции (destroy приложений не трогает БД), про destroy базы.

Проверено: terraform init+validate в обеих папках — Success;
plan в pg/ — 3 to add (кластер, пользователь, база) + 6 outputs.
2026-10-01 16:35:14 +03:00
..

CRUD Test Stand

Три приложения (Lucee / Flask / Node.js) + общая PostgreSQL. Каждое делает CRUD в одной таблице crud_items.

Структура — ДВЕ папки, ДВА независимых состояния

CRUD/
├── pg/      ← база данных: кластер PostgreSQL + пользователь + база
└── apps/    ← приложения-потребители: Lucee + Flask + Node.js

🎯 Почему так, а не «всё в одной папке»

Зачем Что это даёт
destroy в apps/ убивает ТОЛЬКО приложения база данных с данными не трогается — она в другом state (pg/)
БД — общий ресурс к одному кластеру можно подключить сколько угодно папок-потребителей: apps/, потом ещё my-service/, reporting/ и т.д.
Разные жизненные циклы приложения пересоздаются сколько угодно раз, БД живёт всё это время

Если держать PG и приложения в одном state, terraform destroy снесёт и кластер с данными. Именно поэтому база вынесена отдельно.

🎯 Почему нельзя создать всё одним apply

Пароль пользователя БД генерирует платформа и отдаёт его в выходе кластера vault_secrets["users"] — только после операции create_user. При создании всё одним apply приложения запрашивали пароль, когда его ещё не существовало, и поднимались с пустым PGPASSWORD (раньше это маскировалось функцией try()).

Теперь порядок явный: сначала БД отдельным прогоном, потом приложения — пароль к этому моменту существует и лежит в файле apps/creds.json. Костыль try() убран.


📋 Порядок запуска (по шагам)

Шаг 0. Скачать репозиторий

git clone https://gitea.services.ngcloud.ru/terraform/tf_examples.git
cd tf_examples/CRUD

Шаг 1. База данных — папка pg/

cd pg
cp terraform.tfvars.example terraform.tfvars
#  → отредактировать terraform.tfvars: api_token, realm, s3_name
terraform init
terraform apply

Создастся кластер PostgreSQL (несколько минут), затем пользователь БД и база. В конце apply выводятся outputs, включая пароль (помечен sensitive).

Переменная Где брать
api_token ЛК → Профиль → Токены → создать «Технический»
realm ЛК → Кластеры, например k8s-4-sandbox-nubes-ru
s3_name ЛК → S3 → имя экземпляра (для бэкапов PG)

Шаг 2. Передать креды БД в папку приложений 👈 ОДНА команда

# всё ещё в папке pg/
terraform output -json > ../apps/creds.json

Что делает: выгружает хост БД, порт, имя пользователя, имя базы и пароль в файл apps/creds.json. Папка apps/ читает этот файл (см. apps/locals.tf).

Почему файлом, а не «само»: pg/ и apps/ — разные состояния Terraform (разные terraform.tfstate), между ними значения автоматически не передаются: у провайдера нет data-source, а общего backend в стенде нет. Поэтому креды передаются явной выгрузкой outputs — зато видно, что и откуда берётся.

⚠️ В creds.json пароль в открытом виде. Файл добавлен в apps/.gitignore. Не коммитить и не пересылать. Если пароль ротировали (пересоздали пользователя в pg/) — повторите шаг 2, файл надо обновить.

Шаг 3. Приложения — папка apps/

cd ../apps
cp terraform.tfvars.example terraform.tfvars
#  → отредактировать: api_token (тот же), realm (ТОТ ЖЕ, где создана БД)
#  → проверить домены в locals.tf: они должны быть УНИКАЛЬНЫМИ
terraform init
terraform apply

Создадутся три приложения, каждое подключится к общей БД.

Что менять Файл Зачем
api_token, realm terraform.tfvars доступ к API; realm обязан совпадать с БД
lucee_domain / flask_domain / nodejs_domain locals.tf имена доменов — уникальны в облаке
*_git_path locals.tf репозиторий с кодом приложения

🔄 Повседневные операции

Что нужно Команда
Передеплоить приложения cd apps && terraform apply (БД не затрагивается)
Снести приложения cd apps && terraform destroy — БД и данные остаются
Изменить БД cd pg && terraform apply
Снести БД cd pg && terraform destroy — кластер уйдёт в Suspend, а не удалится; юзер и база останутся внутри (keep_on_destroy = true)
Добавить ещё одного потребителя той же БД скопировать apps/ → своя папка → свой creds.json (шаг 2) → свои домены

Про destroy базы

  • Кластер PostgreSQL при destroy не удаляется, а переводится в Suspend (поведение сервиса по умолчанию).
  • Пользователь и база тоже не удаляются: у них keep_on_destroy = true. Иначе из живого кластера исчезли бы база и пользователь. Провайдер печатает предупреждение «Подресурс оставлен как есть, а не удалён».
  • Удалить по-настоящему: снять keep_on_destroy в pg/postgres_user_db.tf и apply.

Состав

Файл Ресурс / назначение
pg/main.tf провайдер + переменные кластера, пользователя, базы
pg/postgres.tf nubes_postgres.main_pg — кластер
pg/postgres_user_db.tf nubes_postgres_user + nubes_postgres_database
pg/outputs.tf выводы для потребителей (хост, порт, юзер, база, пароль)
apps/main.tf провайдер + переменные приложений
apps/locals.tf креды из creds.json + домены, версии, размеры
apps/lucee.tf nubes_lucee.applucee
apps/flask.tf nubes_flask.appflask
apps/nodejs.tf nubes_nodejs.appnodejs
apps/creds.json не в git — выгрузка кредов из pg/ (шаг 2)