Files
tf_provider/TEST_STAND/CRUD/README.md
T
Repinoid 39849bf459 docs(crud): раздел «Что пользователь задаёт сам» продублирован на странице сайта
Стендовый README и страница curated/crud/three_apps.md описывают одно и то же,
поэтому раздел перенесён в страницу сайта сразу после «Структура манифестов»:
- обязательные значения (api_token, realm, s3_name) и создание terraform.tfvars;
- имена с правилами уникальности (кластер и приложения — в пределах стенда,
  юзер/база — в пределах кластера, домены — в облаке) и объяснение через
  adopt_existing_on_create (crud.go:171, refsvc_find.go:47);
- что можно не задавать (дефолты pg/main.tf).
Из шагов 1 и 3 убраны дублирующие таблицы — вместо них ссылка на раздел.
Плюс выровнены отступы в блоке cp terraform.tfvars.example (в двух файлах было
по-разному).
Проверено: diff разделов — совпадает построчно, отличие только в разделителе «---»
(есть в README, не используется на странице); страница 228 строк, 18 строк с
блоками кода (чётно).
2026-10-02 08:08:35 +03:00

191 lines
10 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.
# CRUD Test Stand
CRUD = Create / Read / Update / Delete — создать, прочитать, изменить, удалить.
Три приложения (Lucee, Flask, Node.js) работают с одной общей таблицей `crud_items` в PostgreSQL:
в любом из них можно добавлять записи, просматривать их, редактировать и удалять.
Итог после запуска: добавьте запись в одном приложении — остальные два её увидят.
---
## Как это устроено
Два каталога — два отдельных файла состояния Terraform:
```
CRUD/
├── pg/ база данных: кластер PostgreSQL + пользователь + база
└── apps/ приложения: Lucee + Flask + Node.js
```
- `terraform apply` и `terraform destroy` в `apps/` меняют **только приложения** (создают, изменяют, удаляют) — база в `pg/` не затрагивается;
- `terraform apply` и `terraform destroy` в `pg/` меняют только базу — приложения в `apps/` не затрагиваются;
- приложения можно пересоздавать сколько угодно, база при этом не меняется;
- связь между каталогами — файл `apps/creds.json` (см. шаг 2): он делается из `terraform output` в `pg/`, поэтому после смены хоста или пароля его нужно обновить и повторить `apply` в `apps/`.
---
## Что пользователь задаёт сам
### Обязательные значения (без них `apply` не пройдёт)
| Переменная | Где | Что это |
|---|---|---|
| `api_token` | `pg/terraform.tfvars` и `apps/terraform.tfvars` | токен Nubes: ЛК → Профиль → Токены → создать «Технический» |
| `realm` | там же, **одно и то же значение** | ресурсная платформа (кластер Kubernetes), например `k8s-4-sandbox-nubes-ru`; в `apps/` обязан совпасть с `pg/`, иначе приложения не увидят кластер с базой |
| `s3_name` | `pg/terraform.tfvars` | имя (или UUID) экземпляра S3 для бэкапов: ЛК → S3 |
Файлы создаются из примеров и нужны в обеих папках:
```bash
cd pg && cp terraform.tfvars.example terraform.tfvars
cd ../apps && cp terraform.tfvars.example terraform.tfvars
```
### Имена: придумать самому, и они обязаны быть уникальными
| Имя | Где задаётся | Правило |
|---|---|---|
| `pg_resource_name` (кластер) | `pg/terraform.tfvars` | уникально **в пределах стенда** |
| `pg_username`, `pg_db_name` | `pg/terraform.tfvars` | уникальны в пределах кластера; служебные имена (`admin`, `postgres`, `standby`) платформа не примет |
| `lucee_resource_name`, `flask_resource_name`, `nodejs_resource_name` | `apps/locals.tf` | уникальны **в пределах стенда** |
| `lucee_domain`, `flask_domain`, `nodejs_domain` | `apps/locals.tf` | уникальны **в облаке** — один домен нельзя повесить на два инстанса |
Почему это важно: все ресурсы создаются с `adopt_existing_on_create = true`, а провайдер ищет
инстанс **по имени внутри своего сервиса** (`provider/internal/resources_core/crud.go:171`,
`provider/internal/core/refsvc_find.go:47`). Если такое имя уже занято, он не станет создавать
новый ресурс, а **усыновит** существующий — то есть при занятом имени можно подцепить чужой или
старый инстанс. Если усыновление отключено, `apply` упадёт с «инстанс с resource_name … уже
существует».
Живой пример: прежние имена `tflucee`, `tfflask`, `tfnodejs` заняты старыми инстансами,
поэтому в стенде взяты `lucee-crud`, `flask-crud`, `nodejs-crud` (`apps/locals.tf:45,57,68`).
### Можно не задавать (есть значения по умолчанию)
`pg/main.tf`: `pg_cpu=500`, `pg_memory=512`, `pg_replicas=1`, `pg_disk=10`, `pg_version="17"`,
`pg_retain=14`, `pg_schedule="0 0 * * *"`, `pg_timeout="11m"`, `pg_username="user4crudpg"`,
`pg_role="ddl_user"`, `pg_db_name="db4crudpg"`. Размеры приложений (`*_cpu`, `*_memory`,
`*_replicas`) — в `apps/locals.tf`.
---
## Запуск — три шага
### 1. База данных (`pg/`)
```bash
cd pg
cp terraform.tfvars.example terraform.tfvars
# заполнить api_token, realm, s3_name (раздел «Что пользователь задаёт сам»)
terraform init
terraform apply
```
Создадутся кластер (несколько минут), пользователь БД и база.
Обязательные значения — `api_token`, `realm`, `s3_name`. Полный список и правила по именам —
в разделе «Что пользователь задаёт сам» выше.
### 2. Передать креды БД в `apps/`
```bash
# всё ещё в папке pg/
terraform output -json > ../apps/creds.json
```
Одна команда выгружает хост, порт, имя пользователя, имя БД и пароль в `apps/creds.json`.
Приложения читают этот файл (см. `apps/locals.tf`).
> ⚠️ В `creds.json` пароль лежит **открытым текстом**: файл в `apps/.gitignore`, не коммитить и не пересылать.
Что именно выгружается и как это читают приложения — в разделе «Справка» в конце файла.
### 3. Приложения (`apps/`)
```bash
cd ../apps
cp terraform.tfvars.example terraform.tfvars
# заполнить api_token и realm — те же, что в pg/
terraform init
terraform apply
```
Создадутся три приложения, подключённые к общей БД.
Обязательные значения — `api_token` и `realm` (те же, что в `pg/`). Имена доменов нужно
придумать самому, они должны быть уникальны в облаке — см. раздел «Что пользователь задаёт сам».
---
## Повседневные операции
| Задача | Команда |
|---|---|
| Передеплоить приложения | `cd apps && terraform apply` |
| Удалить приложения | `cd apps && terraform destroy` — БД не трогается |
| Изменить БД | `cd pg && terraform apply` |
| Удалить БД | `cd pg && terraform destroy` — кластер уйдёт в `Suspend`, а не удалится |
> При `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` | пользователь `crud_user_0` + база `pg_db` |
| `pg/outputs.tf` | хост, порт, юзер, база, пароль — для выгрузки кредов |
| `apps/main.tf` | провайдер, переменные приложений |
| `apps/locals.tf` | чтение `creds.json`, домены, версии, размеры |
| `apps/lucee.tf`, `apps/flask.tf`, `apps/nodejs.tf` | три приложения |
| `apps/creds.json` | генерируется шагом 2, **не в git** |
---
## Справка: выходные параметры `pg/`
В `pg/outputs.tf` объявлено шесть выходов:
| Выход | Значение | Откуда |
|---|---|---|
| `pg_host` | внутренний хост master | `state_out_flat["internalMaster"]` кластера |
| `pg_port` | `5432` | константа в `outputs.tf` |
| `pg_username` | имя пользователя БД | выход подресурса `nubes_postgres_user` |
| `pg_db_name` | имя базы | выход `nubes_postgres_database` |
| `pg_password` | пароль, `sensitive = true` | выход подресурса `nubes_postgres_user`: платформа генерирует пароль сама при создании пользователя, провайдер читает его из Vault и отдаёт выходом подресурса |
| `pg_ssl_mode` | `require` | константа в `outputs.tf` |
`terraform output -json` кладёт в файл не голые значения, а объекты вида `{ "sensitive": ..., "type": ..., "value": ... }`:
```json
{
"pg_host": { "sensitive": false, "type": "string", "value": "<хост master>" },
"pg_password": { "sensitive": true, "type": "string", "value": "<пароль>" }
}
```
Поэтому в `apps/locals.tf` значение берётся через `.value`:
```hcl
creds = jsondecode(file("${path.module}/creds.json"))
pg_host = local.creds.pg_host.value
pg_pass = local.creds.pg_password.value
```
Дальше `apps/*.tf` передают эти значения приложениям как переменные окружения:
| Выход `pg/` | Flask | Node.js | Lucee |
|---|---|---|---|
| `pg_host` | `PGHOST` | `PGHOST` | `PGHOST`, `testds_connectionString` |
| `pg_port` | `PGPORT` | `PGPORT` | `PGPORT`, `testds_connectionString` |
| `pg_username` | `PGUSER` | `PGUSER` | `PGUSER`, `testds_username` |
| `pg_password` | `PGPASSWORD` | `PGPASSWORD` | `PGPASSWORD`, `testds_password` |
| `pg_db_name` | `PGDATABASE` | `PGDATABASE` | `testds_connectionString`, `DATABASE_URL` |
| `pg_ssl_mode` | `PGSSLMODE` | `PGSSLMODE` | `PGSSLMODE` |