From 272d4051f47b86116b350b07bea907a046d77434 Mon Sep 17 00:00:00 2001 From: Repinoid Date: Fri, 2 Oct 2026 08:33:17 +0300 Subject: [PATCH] =?UTF-8?q?docs(crud):=20=D0=B2=20README=20=D0=BD=D0=B5=20?= =?UTF-8?q?=D0=B1=D1=8B=D0=BB=D0=BE=20=D0=BA=D0=BB=D0=BE=D0=BD=D0=B8=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D1=8F=20=D0=B8=20=D0=B0=D0=B4?= =?UTF-8?q?=D1=80=D0=B5=D1=81=D0=BE=D0=B2=20=D0=BF=D1=80=D0=B8=D0=BB=D0=BE?= =?UTF-8?q?=D0=B6=D0=B5=D0=BD=D0=B8=D0=B9=20=E2=80=94=20=D0=B4=D0=BE=D0=B1?= =?UTF-8?q?=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=BE=20=D0=92=D0=BB=D0=B0=D0=B4?= =?UTF-8?q?=D0=B5=D0=BB=D0=B5=D1=86:=20=C2=AB=D0=B3=D0=B4=D0=B5=20=D0=BF?= =?UTF-8?q?=D1=80=D0=BE=20=D0=BA=D0=BB=D0=BE=D0=BD=D0=B8=D1=80=D0=BE=D0=B2?= =?UTF-8?q?=D0=B0=D0=BD=D0=B8=D0=B5=3F=3F=20=D0=B3=D0=BB=D0=B0=D0=B7=D0=B0?= =?UTF-8?q?=D0=BC=D0=B8=20=D1=8E=D0=B7=D0=B5=D1=80=D0=B0=20=D0=BF=D1=80?= =?UTF-8?q?=D0=BE=D1=81=D0=BC=D0=BE=D1=82=D1=80=D0=B8=20=D0=B2=D0=B5=D1=81?= =?UTF-8?q?=D1=8C=20=D1=82=D0=B5=D0=BA=D1=81=D1=82=C2=BB.=20=D0=9F=D1=80?= =?UTF-8?q?=D0=BE=D0=B9=D0=B4=D0=B5=D0=BD=D0=BE=20=D0=BF=D0=BE=20=D1=82?= =?UTF-8?q?=D0=B5=D0=BA=D1=81=D1=82=D1=83=20=D1=86=D0=B5=D0=BB=D0=B8=D0=BA?= =?UTF-8?q?=D0=BE=D0=BC,=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=BE=20=D1=82=D0=BE,=20=D0=B1=D0=B5=D0=B7=20=D1=87?= =?UTF-8?q?=D0=B5=D0=B3=D0=BE=20=D0=BF=D0=BE=D0=BB=D1=8C=D0=B7=D0=BE=D0=B2?= =?UTF-8?q?=D0=B0=D1=82=D0=B5=D0=BB=D1=8C=20=D0=BD=D0=B5=20=D0=BD=D0=B0?= =?UTF-8?q?=D1=87=D0=BD=D1=91=D1=82=20=D0=B8=20=D0=BD=D0=B5=20=D0=BF=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D0=B5=D1=80=D0=B8=D1=82:=20-=20=D1=80=D0=B0=D0=B7?= =?UTF-8?q?=D0=B4=D0=B5=D0=BB=20=C2=AB=D0=93=D0=B4=D0=B5=20=D0=B2=D0=B7?= =?UTF-8?q?=D1=8F=D1=82=D1=8C=20=D0=BC=D0=B0=D0=BD=D0=B8=D1=84=D0=B5=D1=81?= =?UTF-8?q?=D1=82=D1=8B=C2=BB:=20git=20clone=20terraform/tf=5Fprovider,=20?= =?UTF-8?q?cd=20=20=20TEST=5FSTAND/CRUD,=20=D0=BF=D1=80=D0=BE=D0=B2=D0=B5?= =?UTF-8?q?=D1=80=D0=BA=D0=B0=20terraform=20version,=20=D0=BE=D1=82=D0=BA?= =?UTF-8?q?=D1=83=D0=B4=D0=B0=20=D0=B1=D0=B5=D1=80=D1=91=D1=82=D1=81=D1=8F?= =?UTF-8?q?=20=D0=BF=D1=80=D0=BE=D0=B2=D0=B0=D0=B9=D0=B4=D0=B5=D1=80=20?= =?UTF-8?q?=D0=B8=20=D0=BA=D0=B0=D0=BA=D0=BE=D0=B9=20=20=20=D0=B2=D0=B5?= =?UTF-8?q?=D1=80=D1=81=D0=B8=D0=B8=20(=D0=BE=D0=B1=D0=B5=20=D0=BF=D0=B0?= =?UTF-8?q?=D0=BF=D0=BA=D0=B8:=20nubes-test/nubes=203.0.0);=20-=20=D1=80?= =?UTF-8?q?=D0=B0=D0=B7=D0=B4=D0=B5=D0=BB=20=C2=AB=D0=97=D0=B0=D0=BF=D1=83?= =?UTF-8?q?=D1=81=D0=BA=C2=BB=20=D1=81=D1=82=D0=B0=D0=BB=20=C2=AB=D1=87?= =?UTF-8?q?=D0=B5=D1=82=D1=8B=D1=80=D0=B5=20=D1=88=D0=B0=D0=B3=D0=B0=C2=BB?= =?UTF-8?q?:=20=D1=88=D0=B0=D0=B3=204=20=C2=AB=D0=9F=D1=80=D0=BE=D0=B2?= =?UTF-8?q?=D0=B5=D1=80=D0=B8=D1=82=D1=8C=C2=BB=20=D1=81=20=D1=80=D0=B5?= =?UTF-8?q?=D0=B0=D0=BB=D1=8C=D0=BD=D1=8B=D0=BC=D0=B8=20=D0=B0=D0=B4=D1=80?= =?UTF-8?q?=D0=B5=D1=81=D0=B0=D0=BC=D0=B8=20=20=20(lucee-crud.luceek8s.dev?= =?UTF-8?q?.nubes.ru,=20flask-crud.pythonk8s.dev.nubes.ru,=20=20=20nodejs-?= =?UTF-8?q?crud.nodejsk8s.dev.nubes.ru)=20=D0=B8=20=D0=BA=D0=BE=D0=BC?= =?UTF-8?q?=D0=B0=D0=BD=D0=B4=D0=BE=D0=B9,=20=D0=BA=D0=B0=D0=BA=20=D0=BF?= =?UTF-8?q?=D0=BE=D1=81=D0=BC=D0=BE=D1=82=D1=80=D0=B5=D1=82=D1=8C=20=D1=81?= =?UTF-8?q?=D0=B2=D0=BE=D0=B8=20=D0=B0=D0=B4=D1=80=D0=B5=D1=81=D0=B0=20?= =?UTF-8?q?=D0=B2=20=20=20state;=20=D0=BF=D1=80=D0=BE=D0=B2=D0=B5=D1=80?= =?UTF-8?q?=D0=B5=D0=BD=D0=BE=20=D0=B3=D1=80=D0=B5=D0=BF=D0=BE=D0=BC=20?= =?UTF-8?q?=D0=BF=D0=BE=20apps/terraform.tfstate;=20-=20=D0=B7=D0=B0=D0=BC?= =?UTF-8?q?=D0=B5=D0=BD=D0=B5=D0=BD=D0=B0=20=D0=B7=D0=B0=D0=B3=D0=BB=D1=83?= =?UTF-8?q?=D1=88=D0=BA=D0=B0=20<=D1=81=D1=83=D1=84=D1=84=D0=B8=D0=BA?= =?UTF-8?q?=D1=81>=20=D0=BD=D0=B0=20=D1=84=D0=B0=D0=BA=D1=82=D0=B8=D1=87?= =?UTF-8?q?=D0=B5=D1=81=D0=BA=D0=B8=D0=B9=20=D1=81=D1=83=D1=84=D1=84=D0=B8?= =?UTF-8?q?=D0=BA=D1=81=20nodejsk8s;=20-=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=20=D1=80=D0=B0=D0=B7=D0=B4=D0=B5=D0=BB=20?= =?UTF-8?q?=C2=AB=D0=95=D1=81=D0=BB=D0=B8=20apply=20=D1=83=D0=BF=D0=B0?= =?UTF-8?q?=D0=BB=C2=BB=20(=D0=B6=D1=83=D1=80=D0=BD=D0=B0=D0=BB=20=D0=BE?= =?UTF-8?q?=D0=BF=D0=B5=D1=80=D0=B0=D1=86=D0=B8=D0=B8=20=D0=B2=D0=BC=D0=B5?= =?UTF-8?q?=D1=81=D1=82=D0=BE=20errorLog);=20-=20=D0=B2=20=C2=AB=D0=A4?= =?UTF-8?q?=D0=B0=D0=B9=D0=BB=D1=8B=C2=BB=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D1=8B=20terraform.tfvars.example;=20-=20?= =?UTF-8?q?=D1=88=D0=B0=D0=B3=203:=20=D1=84=D0=BE=D1=80=D0=BC=D1=83=D0=BB?= =?UTF-8?q?=D0=B8=D1=80=D0=BE=D0=B2=D0=BA=D0=B0=20=D0=BF=D1=80=D0=BE=20?= =?UTF-8?q?=D0=B8=D0=BC=D0=B5=D0=BD=D0=B0=20=D0=BF=D1=80=D0=B8=D0=B2=D0=B5?= =?UTF-8?q?=D0=B4=D0=B5=D0=BD=D0=B0=20=D0=BA=20=D0=B8=D1=81=D0=BF=D1=80?= =?UTF-8?q?=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=BD=D0=BE=D0=B9=20(=D0=B7?= =?UTF-8?q?=D0=B0=D0=B4=D0=B0=D1=8E=D1=82=D1=81=D1=8F=20=D0=B2=20locals.tf?= =?UTF-8?q?).=20=D0=9E=D0=B4=D0=BD=D0=BE=D0=B2=D1=80=D0=B5=D0=BC=D0=B5?= =?UTF-8?q?=D0=BD=D0=BD=D0=BE=20=D0=B8=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B0=20=D0=BC=D0=BE=D1=8F=20=D0=BE=D1=88=D0=B8?= =?UTF-8?q?=D0=B1=D0=BA=D0=B0:=20=D0=BF=D1=80=D0=B5=D0=B4=D1=8B=D0=B4?= =?UTF-8?q?=D1=83=D1=89=D0=B0=D1=8F=20=D0=BF=D1=80=D0=B0=D0=B2=D0=BA=D0=B0?= =?UTF-8?q?=20=D1=81=D0=BA=D0=BB=D0=B5=D0=B8=D0=BB=D0=B0=20=D0=B4=D0=B2?= =?UTF-8?q?=D0=B5=20=D1=81=D1=82=D1=80=D0=BE=D0=BA=D0=B8=20=D1=82=D0=B0?= =?UTF-8?q?=D0=B1=D0=BB=D0=B8=D1=86=D1=8B=20=C2=AB=D0=A4=D0=B0=D0=B9=D0=BB?= =?UTF-8?q?=D1=8B=C2=BB=20(pg/main.tf=20=D0=B8=20pg/postgres.tf)=20?= =?UTF-8?q?=E2=80=94=20=D0=B2=D0=BE=D1=81=D1=81=D1=82=D0=B0=D0=BD=D0=BE?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=D0=BE.=20=D0=A2=D0=BE=20=D0=B6=D0=B5?= =?UTF-8?q?=20=D0=BF=D1=80=D0=BE=D0=B4=D1=83=D0=B1=D0=BB=D0=B8=D1=80=D0=BE?= =?UTF-8?q?=D0=B2=D0=B0=D0=BD=D0=BE=20=D0=B2=20docs/curated/crud/three=5Fa?= =?UTF-8?q?pps.md=20(=D1=84=D0=B0=D0=B9=D0=BB=D1=8B=20=D0=BE=D0=B1=D1=8F?= =?UTF-8?q?=D0=B7=D0=B0=D0=BD=D1=8B=20=D1=81=D0=BE=D0=B2=D0=BF=D0=B0=D0=B4?= =?UTF-8?q?=D0=B0=D1=82=D1=8C).=20=D0=9F=D1=80=D0=BE=D0=B2=D0=B5=D1=80?= =?UTF-8?q?=D0=B5=D0=BD=D0=BE:=20260=20=D0=B8=20265=20=D1=81=D1=82=D1=80?= =?UTF-8?q?=D0=BE=D0=BA,=20=D0=B1=D0=BB=D0=BE=D0=BA=D0=B8=20=D0=BA=D0=BE?= =?UTF-8?q?=D0=B4=D0=B0=20=D0=BF=D0=B0=D1=80=D0=BD=D1=8B=D0=B5=20(22=20?= =?UTF-8?q?=D0=B8=2024),=20=D0=B7=D0=B0=D0=B3=D0=BE=D0=BB=D0=BE=D0=B2?= =?UTF-8?q?=D0=BA=D0=B8=20=D0=BD=D0=B0=20=D0=BC=D0=B5=D1=81=D1=82=D0=B5.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TEST_STAND/CRUD/README.md | 64 ++++++++++++++++++++++++++++++--- docs/curated/crud/three_apps.md | 47 +++++++++++++++++++++--- 2 files changed, 102 insertions(+), 9 deletions(-) diff --git a/TEST_STAND/CRUD/README.md b/TEST_STAND/CRUD/README.md index 86653ad..9b7b205 100644 --- a/TEST_STAND/CRUD/README.md +++ b/TEST_STAND/CRUD/README.md @@ -9,6 +9,27 @@ CRUD = Create / Read / Update / Delete — создать, прочитать, --- +## Где взять манифесты + +Каталог примера — `TEST_STAND/CRUD` в репозитории провайдера. Клонируйте и перейдите в него: + +```bash +git clone https://gitea.services.ngcloud.ru/terraform/tf_provider.git +cd tf_provider/TEST_STAND/CRUD +``` + +Нужен установленный Terraform: + +```bash +terraform version +``` + +Провайдер берётся из реестра — источник и версия заданы в `pg/main.tf` и `apps/main.tf` +(test-стенд: `tf-registry.containerk8s.services.ngcloud.ru/nubes-test/nubes`, версия `3.0.0`). +Дальше все команды выполняются из каталога `TEST_STAND/CRUD`. + +--- + ## Как это устроено Два каталога — два отдельных файла состояния Terraform: @@ -50,7 +71,7 @@ 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` | здесь задаётся **не полный домен, а имя** — суффикс платформа добавляет сама: `lucee-crud` → `lucee-crud.luceek8s.dev.nubes.ru`, `flask-crud` → `flask-crud.pythonk8s.dev.nubes.ru`, `nodejs-crud` → `nodejs-crud.<суффикс>.dev.nubes.ru`. Полные имена уникальны (это обычные DNS-имена), поэтому и задаваемое имя должно быть уникальным | +| `lucee_domain`, `flask_domain`, `nodejs_domain` | `apps/locals.tf` | здесь задаётся **не полный домен, а имя** — суффикс платформа добавляет сама: `lucee-crud` → `lucee-crud.luceek8s.dev.nubes.ru`, `flask-crud` → `flask-crud.pythonk8s.dev.nubes.ru`, `nodejs-crud` → `nodejs-crud.nodejsk8s.dev.nubes.ru`. Полные имена уникальны (это обычные DNS-имена), поэтому и задаваемое имя должно быть уникальным | Почему это важно: все ресурсы создаются с `adopt_existing_on_create = true`, а провайдер ищет инстанс **по имени внутри своего сервиса** (`provider/internal/resources_core/crud.go:171`, @@ -85,7 +106,7 @@ cd ../apps && cp terraform.tfvars.example terraform.tfvars --- -## Запуск — три шага +## Запуск — четыре шага ### 1. База данных (`pg/`) @@ -128,8 +149,27 @@ terraform apply Создадутся три приложения, подключённые к общей БД. -Обязательные значения — `api_token` и `realm` (те же, что в `pg/`). Имена доменов нужно -придумать самому, они должны быть уникальны в облаке — см. раздел «Что пользователь задаёт сам». +Обязательные значения — `api_token` и `realm` (те же, что в `pg/`). Имена доменов и имена +ресурсов приложений задаются в `apps/locals.tf` — см. раздел «Что пользователь задаёт сам». + +### 4. Проверить + +Откройте в браузере три адреса (это полные домены, которые платформа построила из имён в +`apps/locals.tf`): + +| Приложение | Адрес | +|---|---| +| Lucee | | +| Flask | | +| Node.js | | + +Свои адреса можно посмотреть в state: + +```bash +grep -o 'https://[a-z0-9.-]*\.dev\.nubes\.ru' apps/terraform.tfstate | sort -u +``` + +Добавьте запись в одном приложении — она появится в двух других (таблица `crud_items` общая). --- @@ -147,6 +187,21 @@ terraform apply --- +## Если `apply` упал + +Текст ошибки в поле `errorLog` у платформы **не всегда отражает суть** — реальная причина видна +в журнале операции: + +```bash +GET {api_endpoint}/instanceOperations/?fields=cfsParams,errorLog,stages +``` + +`stages[].stageMsg` — массив пар `[заголовок, лог]`, смотреть последнюю запись; `cfsParams` — +фактический набор параметров, ушедший на платформу. Разбор реального случая: +`HISTORY/60_stands/2026-10-01_test_crud_pg_create_failure.md`. + +--- + ## Файлы | Файл | Что делает | @@ -158,6 +213,7 @@ terraform apply | `apps/main.tf` | провайдер, переменные приложений | | `apps/locals.tf` | чтение `creds.json`, домены, версии, размеры | | `apps/lucee.tf`, `apps/flask.tf`, `apps/nodejs.tf` | три приложения | +| `pg/terraform.tfvars.example`, `apps/terraform.tfvars.example` | шаблоны для копирования в `terraform.tfvars` | | `apps/creds.json` | генерируется шагом 2, **не в git** | --- diff --git a/docs/curated/crud/three_apps.md b/docs/curated/crud/three_apps.md index f71ff2e..9290b94 100644 --- a/docs/curated/crud/three_apps.md +++ b/docs/curated/crud/three_apps.md @@ -4,9 +4,27 @@ (Lucee/CFML, Python, Node.js) и одна таблица `crud_items`. Запись, добавленная в любом из трёх приложений, видна в двух других. -Манифесты: `TEST_STAND/CRUD/` (test-стенд) — два каталога: `pg/` (база) и `apps/` (приложения). +Манифесты: `TEST_STAND/CRUD/` в репозитории провайдера — два каталога: `pg/` (база) и `apps/` (приложения). `DEV_STAND/CRUD/` — тот же пример, но по старой схеме: всё в одной папке. +## Где взять манифесты + +Клонируйте репозиторий провайдера и перейдите в каталог примера: + +```bash +git clone https://gitea.services.ngcloud.ru/terraform/tf_provider.git +cd tf_provider/TEST_STAND/CRUD +``` + +Нужен установленный Terraform: + +```bash +terraform version +``` + +Провайдер берётся из реестра — источник и версия заданы в `pg/main.tf` и `apps/main.tf`. +Дальше все команды выполняются из каталога `TEST_STAND/CRUD`. + ## Что создаётся | Ресурс | Имя (по умолчанию) | Что это | @@ -76,7 +94,7 @@ 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` | здесь задаётся **не полный домен, а имя** — суффикс платформа добавляет сама: `lucee-crud` → `lucee-crud.luceek8s.dev.nubes.ru`, `flask-crud` → `flask-crud.pythonk8s.dev.nubes.ru`, `nodejs-crud` → `nodejs-crud.<суффикс>.dev.nubes.ru`. Полные имена уникальны (это обычные DNS-имена), поэтому и задаваемое имя должно быть уникальным | +| `lucee_domain`, `flask_domain`, `nodejs_domain` | `apps/locals.tf` | здесь задаётся **не полный домен, а имя** — суффикс платформа добавляет сама: `lucee-crud` → `lucee-crud.luceek8s.dev.nubes.ru`, `flask-crud` → `flask-crud.pythonk8s.dev.nubes.ru`, `nodejs-crud` → `nodejs-crud.nodejsk8s.dev.nubes.ru`. Полные имена уникальны (это обычные DNS-имена), поэтому и задаваемое имя должно быть уникальным | Почему это важно: все ресурсы создаются с `adopt_existing_on_create = true`, а провайдер ищет инстанс **по имени внутри своего сервиса** (`provider/internal/resources_core/crud.go:171`, @@ -113,7 +131,7 @@ provider "nubes" { } ``` -## Запуск — три шага +## Запуск — четыре шага ### 1. База данных (`pg/`) @@ -153,8 +171,27 @@ terraform apply Создадутся три приложения, подключённые к общей БД. -Обязательные значения — `api_token` и `realm` (те же, что в `pg/`). Имена доменов нужно -придумать самому, они должны быть уникальны в облаке — см. раздел «Что пользователь задаёт сам». +Обязательные значения — `api_token` и `realm` (те же, что в `pg/`). Имена доменов и имена +ресурсов приложений задаются в `apps/locals.tf` — см. раздел «Что пользователь задаёт сам». + +### 4. Проверить + +Откройте в браузере три адреса (это полные домены, которые платформа построила из имён в +`apps/locals.tf`): + +| Приложение | Адрес | +|---|---| +| Lucee | | +| Flask | | +| Node.js | | + +Свои адреса можно посмотреть в state: + +```bash +grep -o 'https://[a-z0-9.-]*\.dev\.nubes\.ru' apps/terraform.tfstate | sort -u +``` + +Добавьте запись в одном приложении — она появится в двух других (таблица `crud_items` общая). ## Справка: выходные параметры `pg/`