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

6.3 KiB
Raw Blame History

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. Платформа сама клонирует код из этих репозиториев — вручную ничего собирать и заливать не нужно.

Провайдер и запуск

terraform {
  required_providers {
    nubes = {
      source  = "{{PROVIDER_SOURCE}}"
      version = "{{VERSION}}"
    }
  }
}

provider "nubes" {
  api_token    = var.api_token
  api_endpoint = "{{NUBES_API_ENDPOINT}}"
}
# terraform.tfvars
api_token = "***"                     # ЛК → Профиль → Токены → «Технический»
realm     = "k8s-4-sandbox-nubes-ru"  # кластер Kubernetes (из списка в ЛК)
s3_name   = "naeel-s3"                # имя экземпляра S3 для бэкапов
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": "..." } }:

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 у платформы не всегда отражает суть. Реальная причина — в журнале операции:

GET {api_endpoint}/instanceOperations/<UID>?fields=cfsParams,errorLog,stages

stages[].stageMsg — массив пар [заголовок, лог], смотреть последнюю запись. Там же cfsParams — фактический набор параметров, ушедший на платформу. Разбор реального случая: HISTORY/2026-10-01_test_crud_pg_create_failure.md.