Files
tf_provider/docs/curated/crud/three_apps.md
T
Repinoid 00bfe7a3ad docs(curated): страница CRUD-стенда + ссылки на git-репозитории приложений
Новая страница docs/curated/crud/three_apps.md (раздел «Проверенные примеры» в mkdocs.yml):
- что создаётся (6 ресурсов: PG + user + db + Lucee + Flask + Node.js);
- ссылки на git: gitea.services.ngcloud.ru/terraform/{tfluceecrud,tfflaskcrud,tfnodejscrud}
  (проверено HTTP 200), в манифестах — lucee_git_path/flask_git_path/nodejs_git_path;
- запуск: два apply и почему (пароль появляется только после create_user);
- доступ к БД: vault_secrets["users"] -> { "<username>": { "password" } }, и ловушка
  state_out.users (метаданные без пароля);
- особенности: adopt_existing_on_create, уникальные домены, camelCase в postgres_conf, realm;
- диагностика: ошибку смотреть в stages операции, а не в errorLog.

Проверено локальной сборкой mkdocs: страница собирается, битых ссылок нет,
все три ссылки на репозитории присутствуют в HTML.
2026-10-01 14:28:50 +03:00

117 lines
6.3 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-стенд: PostgreSQL + Lucee + Flask + Node.js
Проверенный пример: одна общая база PostgreSQL, три приложения на разных стеках и один сквозной
сценарий CRUD (создать/прочитать/обновить/удалить) в одной таблице `crud_items`.
Манифесты: `TEST_STAND/CRUD/` (test-стенд). Аналог — `DEV_STAND/CRUD/`.
Состояние на 2026-10-01, TEST-стенд: все 6 ресурсов созданы, сервисы `running`.
## Что создаётся
| Ресурс | Имя | Что это |
|---|---|---|
| `nubes_postgres` | `pg4crud2` | кластер PostgreSQL 17 |
| `nubes_postgres_user` | `user4crudpg` | пользователь БД, роль `ddl_user` |
| `nubes_postgres_database` | `db4crudpg` | база, владелец — этот пользователь |
| `nubes_lucee` | `crud-lucee` | приложение на CFML |
| `nubes_flask` | `crud-flask` | приложение на Python |
| `nubes_nodejs` | `crud-nodejs` | приложение на Node.js |
Все три приложения ходят в одну базу и одну таблицу — так видно, что CRUD работает одинаково
с любого стека.
## Код приложений (git)
| Приложение | Репозиторий |
|---|---|
| Lucee (CFML) | `https://gitea.services.ngcloud.ru/terraform/tfluceecrud` |
| Flask (Python) | `https://gitea.services.ngcloud.ru/terraform/tfflaskcrud` |
| Node.js (Express) | `https://gitea.services.ngcloud.ru/terraform/tfnodejscrud` |
Ссылки на репозитории задаются в манифестах: `lucee_git_path`, `flask_git_path`,
`nodejs_git_path` в `TEST_STAND/CRUD/locals.tf`. Платформа сама клонирует код из этих
репозиториев — вручную ничего собирать и заливать не нужно.
## Провайдер и запуск
```hcl
terraform {
required_providers {
nubes = {
source = "{{PROVIDER_SOURCE}}"
version = "{{VERSION}}"
}
}
}
provider "nubes" {
api_token = var.api_token
api_endpoint = "{{NUBES_API_ENDPOINT}}"
}
```
```hcl
# terraform.tfvars
api_token = "***" # ЛК → Профиль → Токены → «Технический»
realm = "k8s-4-sandbox-nubes-ru" # кластер Kubernetes (из списка в ЛК)
s3_name = "naeel-s3" # имя экземпляра S3 для бэкапов
```
```bash
terraform init
terraform apply # 1-й прогон: PostgreSQL + пользователь + база
terraform apply # 2-й прогон: приложения (пароль БД уже в Vault)
```
!!! warning "Нужны два `apply` — это не ошибка"
Пароль пользователя БД платформа создаёт вместе с пользователем, а `vault_secrets`
у ресурса кластера читаются на этапе его создания — то есть **до** `create_user`.
Поэтому в первом прогоне пароля ещё нет, и приложения получают пустое значение.
Во втором прогоне пароль уже в Vault, и приложения обновляются с верным значением.
В манифестах чтение пароля обёрнуто в `try(...)`, чтобы первый прогон **не падал**
с `Invalid index ... does not identify an element in this collection value`.
Подробности: [PostgreSQL: пароль пользователя](../postgres/pg_user_db.md).
## Доступ приложений к базе
Пароль берётся из выхода `vault_secrets["users"]` — это JSON `{ "<username>": { "password": "..." } }`:
```hcl
locals {
pg_host = nubes_postgres.main_pg.state_out_flat["internalMaster"]
pg_user = nubes_postgres_user.crud_user_0.username
pg_pass = try(nonsensitive(jsondecode(nubes_postgres.main_pg.vault_secrets["users"]).user4crudpg.password), "")
}
```
⚠️ Не путать с выходом `state_out.users` — там **метаданные** (`role`, `rights`, `username`,
`mtlsAccess`) и **пароля нет**. Пароль только в `vault_secrets`.
## Особенности этого примера
- **`adopt_existing_on_create = true`** у всех ресурсов — если инстанс с таким именем уже есть
(например после `destroy`, который приостанавливает, а не удаляет), провайдер его **усыновит**,
а не упадёт с «ресурс с таким именем уже существует».
- **Домены приложений должны быть уникальными** — `lucee_domain`, `flask_domain`, `nodejs_domain`
в `locals.tf`. Иначе платформа откажет.
- **`postgres_conf`** — ключи строго как в спецификации платформы: `paramName` / `paramValue`
(camelCase). Допустимы только `log_connections` и `log_disconnections`.
- **`realm`** — рабочая ресурсная платформа. Если у неё нет ёмкости, операция падает
с сообщением «Невозможно развернуть приложение в данной ресурсной платформе» — смотрите
журнал операции, а не только текст ошибки.
## Диагностика, если `apply` упал
Текст ошибки в `errorLog` у платформы **не всегда отражает суть**. Реальная причина — в журнале
операции:
```bash
GET {api_endpoint}/instanceOperations/<UID>?fields=cfsParams,errorLog,stages
```
`stages[].stageMsg` — массив пар `[заголовок, лог]`, смотреть последнюю запись. Там же
`cfsParams` — фактический набор параметров, ушедший на платформу. Разбор реального случая:
`HISTORY/2026-10-01_test_crud_pg_create_failure.md`.