From bfa9d5f62690c9832bfa71c48b87aba4ec4ab72e Mon Sep 17 00:00:00 2001 From: Repinoid Date: Fri, 2 Oct 2026 08:46:46 +0300 Subject: [PATCH] =?UTF-8?q?docs(crud):=20=D1=81=D0=BD=D0=B0=D1=87=D0=B0?= =?UTF-8?q?=D0=BB=D0=B0=20=D0=B4=D0=B5=D0=B9=D1=81=D1=82=D0=B2=D0=B8=D1=8F?= =?UTF-8?q?,=20=D0=BF=D0=BE=D1=82=D0=BE=D0=BC=20=D0=BE=D0=BF=D0=B8=D1=81?= =?UTF-8?q?=D0=B0=D0=BD=D0=B8=D0=B5;=20=D1=83=D0=B1=D1=80=D0=B0=D0=BD?= =?UTF-8?q?=D0=BE=20=D0=BD=D0=B5=D0=B4=D0=BE=D1=81=D1=82=D1=83=D0=BF=D0=BD?= =?UTF-8?q?=D0=BE=D0=B5=20=D0=BF=D0=BE=D0=BB=D1=8C=D0=B7=D0=BE=D0=B2=D0=B0?= =?UTF-8?q?=D1=82=D0=B5=D0=BB=D1=8E=20=D0=A2=D1=80=D0=B5=D0=B1=D0=BE=D0=B2?= =?UTF-8?q?=D0=B0=D0=BD=D0=B8=D1=8F=20=D0=B2=D0=BB=D0=B0=D0=B4=D0=B5=D0=BB?= =?UTF-8?q?=D1=8C=D1=86=D0=B0:=20=C2=AB=D0=A1=D0=9D=D0=90=D0=A7=D0=90?= =?UTF-8?q?=D0=9B=D0=90=20=E2=80=94=20=D0=BA=D1=80=D0=B0=D1=82=D0=BA=D0=BE?= =?UTF-8?q?=20=D1=87=D1=91=20=D1=8D=D1=82=D0=BE=20=D0=B2=D0=BE=D0=BE=D0=B1?= =?UTF-8?q?=D1=89=D0=B5=20=D0=B8=20=D0=94=D0=95=D0=99=D0=A1=D0=A2=D0=92?= =?UTF-8?q?=D0=98=D0=AF=20=E2=80=A6=20=D0=B2=D1=81=D1=91=20=D0=BE=D1=81?= =?UTF-8?q?=D1=82=D0=B0=D0=BB=D1=8C=D0=BD=D0=BE=D0=B5=20=D0=BE=D0=BF=D0=B8?= =?UTF-8?q?=D1=81=D0=B0=D0=BD=D0=B8=D0=B5=20=E2=80=94=20=D0=9F=D0=9E=D0=A1?= =?UTF-8?q?=D0=9B=D0=95=C2=BB;=20=C2=AB=D1=8E=D0=B7=D0=B5=D1=80=20=D0=9D?= =?UTF-8?q?=D0=95=20=D0=9C=D0=9E=D0=96=D0=95=D0=A2=20=D0=B2=D0=B2=D0=BE?= =?UTF-8?q?=D0=B4=D0=B8=D1=82=D1=8C=20=D0=BD=D0=B8=D0=BA=D0=B0=D0=BA=D0=B8?= =?UTF-8?q?=D0=B5=20=D0=BA=D0=BE=D0=BC=D0=B0=D0=BD=D0=B4=D1=8B=20=E2=80=A6?= =?UTF-8?q?=20=D0=A3=D0=91=D0=95=D0=A0=D0=98=20=D0=AD=D0=A2=D0=9E=20=D0=B8?= =?UTF-8?q?=20=D0=BF=D0=BE=D0=B4=D0=BE=D0=B1=D0=BD=D0=BE=D0=B5=C2=BB.=20-?= =?UTF-8?q?=20=D1=80=D0=B0=D0=B7=D0=B4=D0=B5=D0=BB=20=C2=AB=D0=91=D1=8B?= =?UTF-8?q?=D1=81=D1=82=D1=80=D1=8B=D0=B9=20=D1=81=D1=82=D0=B0=D1=80=D1=82?= =?UTF-8?q?=C2=BB=20=E2=80=94=20=D0=BF=D0=B5=D1=80=D0=B2=D1=8B=D0=BC:=20?= =?UTF-8?q?=D0=BA=D0=BB=D0=BE=D0=BD=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D0=BD?= =?UTF-8?q?=D0=B8=D0=B5,=20=D0=B7=D0=B0=D0=BF=D0=BE=D0=BB=D0=BD=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D0=BF=D0=B5=D1=80=D0=B5=D0=BC=D0=B5=D0=BD?= =?UTF-8?q?=D0=BD=D1=8B=D1=85,=20apply=20pg,=20=20=20creds.json,=20apply?= =?UTF-8?q?=20apps,=20=D0=B0=D0=B4=D1=80=D0=B5=D1=81=D0=B0=20=D0=BF=D1=80?= =?UTF-8?q?=D0=B8=D0=BB=D0=BE=D0=B6=D0=B5=D0=BD=D0=B8=D0=B9,=20=D0=BF?= =?UTF-8?q?=D1=80=D0=B5=D0=B4=D1=83=D0=BF=D1=80=D0=B5=D0=B6=D0=B4=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D0=BF=D1=80=D0=BE=20=D0=BF=D0=B0=D1=80?= =?UTF-8?q?=D0=BE=D0=BB=D1=8C=20=D0=B2=20=D1=84=D0=B0=D0=B9=D0=BB=D0=B5;?= =?UTF-8?q?=20=20=20=D0=BF=D0=BE=D0=B4=D1=80=D0=BE=D0=B1=D0=BD=D0=BE=D0=B5?= =?UTF-8?q?=20=D0=BE=D0=BF=D0=B8=D1=81=D0=B0=D0=BD=D0=B8=D0=B5=20=E2=80=94?= =?UTF-8?q?=20=D0=BD=D0=B8=D0=B6=D0=B5=20(READ=D0=9CE:=20=D0=9A=D0=B0?= =?UTF-8?q?=D0=BA=20=D1=8D=D1=82=D0=BE=20=D1=83=D1=81=D1=82=D1=80=D0=BE?= =?UTF-8?q?=D0=B5=D0=BD=D0=BE=20/=20=D0=A7=D1=82=D0=BE=20=D0=B7=D0=B0?= =?UTF-8?q?=D0=B4=D0=B0=D1=91=D1=82=20=D0=BF=D0=BE=D0=BB=D1=8C=D0=B7=D0=BE?= =?UTF-8?q?=D0=B2=D0=B0=D1=82=D0=B5=D0=BB=D1=8C=20/=20=20=20=D0=9A=D0=BE?= =?UTF-8?q?=D0=B4=20=D0=BF=D1=80=D0=B8=D0=BB=D0=BE=D0=B6=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D0=B9=20/=20=D0=9F=D0=BE=D0=B2=D1=81=D0=B5=D0=B4=D0=BD=D0=B5?= =?UTF-8?q?=D0=B2=D0=BD=D1=8B=D0=B5=20=D0=BE=D0=BF=D0=B5=D1=80=D0=B0=D1=86?= =?UTF-8?q?=D0=B8=D0=B8=20/=20=D0=A4=D0=B0=D0=B9=D0=BB=D1=8B=20/=20=D0=A1?= =?UTF-8?q?=D0=BF=D1=80=D0=B0=D0=B2=D0=BA=D0=B0);=20-=20=D0=B8=D0=B7=20REA?= =?UTF-8?q?DME=20=D1=83=D0=B1=D1=80=D0=B0=D0=BD=20=D1=80=D0=B0=D0=B7=D0=B4?= =?UTF-8?q?=D0=B5=D0=BB=20=C2=AB=D0=95=D1=81=D0=BB=D0=B8=20apply=20=D1=83?= =?UTF-8?q?=D0=BF=D0=B0=D0=BB=C2=BB,=20=D1=81=D0=BE=20=D1=81=D1=82=D1=80?= =?UTF-8?q?=D0=B0=D0=BD=D0=B8=D1=86=D1=8B=20=E2=80=94=20=C2=AB=D0=94=D0=B8?= =?UTF-8?q?=D0=B0=D0=B3=D0=BD=D0=BE=D1=81=D1=82=D0=B8=D0=BA=D0=B0,=20?= =?UTF-8?q?=D0=B5=D1=81=D0=BB=D0=B8=20=20=20apply=20=D1=83=D0=BF=D0=B0?= =?UTF-8?q?=D0=BB=C2=BB:=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=BC=D0=BE?= =?UTF-8?q?=D0=B6=D0=B5=D1=82=20=D0=B4=D0=B5=D0=BB=D0=B0=D1=82=D1=8C=20?= =?UTF-8?q?=D0=B2=D1=8B=D0=B7=D0=BE=D0=B2=D1=8B=20=D0=B2=D0=B8=D0=B4=D0=B0?= =?UTF-8?q?=20=20=20GET=20{api=5Fendpoint}/instanceOperations/...=20?= =?UTF-8?q?=E2=80=94=20=D0=B2=D0=BC=D0=B5=D1=81=D1=82=D0=BE=20=D0=B8=D0=BD?= =?UTF-8?q?=D1=81=D1=82=D1=80=D1=83=D0=BA=D1=86=D0=B8=D0=B8=20=D0=B1=D1=8B?= =?UTF-8?q?=D0=BB=D0=BE=20=D0=BD=D0=B8=D1=87=D0=B5=D0=B3=D0=BE=20=D0=BD?= =?UTF-8?q?=D0=B5=20=20=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=D1=8E=D1=89?= =?UTF-8?q?=D0=B5=D0=B5;=20=D0=B2=D0=BC=D0=B5=D1=81=D1=82=D0=B5=20=D1=81?= =?UTF-8?q?=20=D1=80=D0=B0=D0=B7=D0=B4=D0=B5=D0=BB=D0=BE=D0=BC=20=D1=83?= =?UTF-8?q?=D0=B1=D1=80=D0=B0=D0=BD=D0=B0=20=D1=81=D1=81=D1=8B=D0=BB=D0=BA?= =?UTF-8?q?=D0=B0=20=D0=BD=D0=B0=20HISTORY/60=5Fstands/...=20(=D0=B2=D0=BD?= =?UTF-8?q?=D1=83=D1=82=D1=80=D0=B5=D0=BD=D0=BD=D0=B8=D0=B9=20=20=20=D1=80?= =?UTF-8?q?=D0=B5=D0=BF=D0=BE=D0=B7=D0=B8=D1=82=D0=BE=D1=80=D0=B8=D0=B9);?= =?UTF-8?q?=20-=20=D1=83=D0=B1=D1=80=D0=B0=D0=BD=D1=8B=20=D1=81=D1=81?= =?UTF-8?q?=D1=8B=D0=BB=D0=BA=D0=B8=20=D0=BD=D0=B0=20=D0=B2=D0=BD=D1=83?= =?UTF-8?q?=D1=82=D1=80=D0=B5=D0=BD=D0=BD=D0=B8=D0=B9=20=D0=BA=D0=BE=D0=B4?= =?UTF-8?q?=20(provider/internal/.../crud.go:171,=20=20=20refsvc=5Ffind.go?= =?UTF-8?q?:47)=20=D0=B8=20=D0=BD=D0=B0=20=D1=81=D1=82=D1=80=D0=BE=D0=BA?= =?UTF-8?q?=D0=B8=20apps/locals.tf:45,57,68;=20=D1=83=D0=B1=D1=80=D0=B0?= =?UTF-8?q?=D0=BD=20=D0=B0=D0=B1=D0=B7=D0=B0=D1=86=20=D0=BF=D1=80=D0=BE=20?= =?UTF-8?q?=D0=BF=D1=80=D0=B5=D0=B6=D0=BD=D0=B8=D0=B5=20=20=20=D0=B8=D0=BC?= =?UTF-8?q?=D0=B5=D0=BD=D0=B0=20tflucee/tfflask/tfnodejs=20(=D0=B2=D0=BD?= =?UTF-8?q?=D1=83=D1=82=D1=80=D0=B5=D0=BD=D0=BD=D1=8F=D1=8F=20=D0=B8=D1=81?= =?UTF-8?q?=D1=82=D0=BE=D1=80=D0=B8=D1=8F);=20-=20=D1=81=D0=BE=20=D1=81?= =?UTF-8?q?=D1=82=D1=80=D0=B0=D0=BD=D0=B8=D1=86=D1=8B=20=D1=83=D0=B1=D1=80?= =?UTF-8?q?=D0=B0=D0=BD=D0=BE=20=C2=AB=D1=81=D0=BC=D0=BE=D1=82=D1=80=D0=B8?= =?UTF-8?q?=D1=82=D0=B5=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=C2=BB=20=D0=B2=20?= =?UTF-8?q?=D1=80=D0=B0=D0=B7=D0=B4=D0=B5=D0=BB=D0=B5=20=C2=AB=D0=9E=D1=81?= =?UTF-8?q?=D0=BE=D0=B1=D0=B5=D0=BD=D0=BD=D0=BE=D1=81=D1=82=D0=B8=C2=BB.?= =?UTF-8?q?=20=D0=9F=D1=80=D0=BE=D0=B2=D0=B5=D1=80=D0=B5=D0=BD=D0=BE=20?= =?UTF-8?q?=D0=BD=D0=B0=20=D0=B6=D0=B8=D0=B2=D0=BE=D0=B9=20=D1=81=D1=82?= =?UTF-8?q?=D1=80=D0=B0=D0=BD=D0=B8=D1=86=D0=B5=20(162257=20=D0=B1=D0=B0?= =?UTF-8?q?=D0=B9=D1=82,=20=D1=81=D0=BE=D0=B2=D0=BF=D0=B0=D0=B4=D0=B0?= =?UTF-8?q?=D0=B5=D1=82=20=D1=81=20=D0=BB=D0=BE=D0=BA=D0=B0=D0=BB=D1=8C?= =?UTF-8?q?=D0=BD=D0=BE=D0=B9=20=D1=81=D0=B1=D0=BE=D1=80=D0=BA=D0=BE=D0=B9?= =?UTF-8?q?):=20instanceOperations=20/=20errorLog=20/=20HISTORY/=20/=20cru?= =?UTF-8?q?d.go=20/=20refsvc=5Ffind=20=E2=80=94=200=20=D0=B2=D1=85=D0=BE?= =?UTF-8?q?=D0=B6=D0=B4=D0=B5=D0=BD=D0=B8=D0=B9;=20=D0=BF=D0=B5=D1=80?= =?UTF-8?q?=D0=B2=D1=8B=D0=B9=20=D1=80=D0=B0=D0=B7=D0=B4=D0=B5=D0=BB=20?= =?UTF-8?q?=E2=80=94=20=C2=AB=D0=91=D1=8B=D1=81=D1=82=D1=80=D1=8B=D0=B9=20?= =?UTF-8?q?=D1=81=D1=82=D0=B0=D1=80=D1=82=C2=BB.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TEST_STAND/CRUD/README.md | 134 +++++++++----------------------- docs/curated/crud/three_apps.md | 124 +++++++++-------------------- 2 files changed, 72 insertions(+), 186 deletions(-) diff --git a/TEST_STAND/CRUD/README.md b/TEST_STAND/CRUD/README.md index 9b7b205..fa0feae 100644 --- a/TEST_STAND/CRUD/README.md +++ b/TEST_STAND/CRUD/README.md @@ -9,24 +9,50 @@ CRUD = Create / Read / Update / Delete — создать, прочитать, --- -## Где взять манифесты - -Каталог примера — `TEST_STAND/CRUD` в репозитории провайдера. Клонируйте и перейдите в него: +## Быстрый старт ```bash +# 1. получить манифесты (нужен установленный Terraform — проверьте `terraform version`) git clone https://gitea.services.ngcloud.ru/terraform/tf_provider.git cd tf_provider/TEST_STAND/CRUD + +# 2. заполнить переменные (что именно — в разделе «Что пользователь задаёт сам») +cd pg && cp terraform.tfvars.example terraform.tfvars # api_token, realm, s3_name +cd ../apps && cp terraform.tfvars.example terraform.tfvars # api_token, realm — те же + +# 3. база данных (кластер создаётся несколько минут) +cd ../pg && terraform init && terraform apply + +# 4. передать креды БД приложениям +terraform output -json > ../apps/creds.json + +# 5. три приложения +cd ../apps && terraform init && terraform apply ``` -Нужен установленный Terraform: +После этого приложения открываются в браузере (имена заданы в `apps/locals.tf`): + +| Приложение | Адрес | +|---|---| +| Lucee | | +| Flask | | +| Node.js | | + +Свои адреса — из state: ```bash -terraform version +grep -o 'https://[a-z0-9.-]*\.dev\.nubes\.ru' apps/terraform.tfstate | sort -u ``` -Провайдер берётся из реестра — источник и версия заданы в `pg/main.tf` и `apps/main.tf` -(test-стенд: `tf-registry.containerk8s.services.ngcloud.ru/nubes-test/nubes`, версия `3.0.0`). -Дальше все команды выполняются из каталога `TEST_STAND/CRUD`. +Добавьте запись в одном приложении — она появится в двух других (таблица `crud_items` общая). + +> ⚠️ В `apps/creds.json` пароль лежит **открытым текстом**: файл в `apps/.gitignore` — не коммитить и не пересылать. + +Провайдер берётся из реестра: источник и версия заданы в `pg/main.tf` и `apps/main.tf` +(test-стенд — `tf-registry.containerk8s.services.ngcloud.ru/nubes-test/nubes`, версия `3.0.0`). +Все команды выполняются из каталога `TEST_STAND/CRUD`. + +Подробности дальше: что задавать руками, как всё устроено, что делать при ошибке. --- @@ -43,7 +69,7 @@ CRUD/ - `terraform apply` и `terraform destroy` в `apps/` меняют **только приложения** (создают, изменяют, удаляют) — база в `pg/` не затрагивается; - `terraform apply` и `terraform destroy` в `pg/` меняют только базу — приложения в `apps/` не затрагиваются; - приложения можно пересоздавать сколько угодно, база при этом не меняется; -- связь между каталогами — файл `apps/creds.json` (см. шаг 2): он делается из `terraform output` в `pg/`, поэтому после смены хоста или пароля его нужно обновить и повторить `apply` в `apps/`. +- связь между каталогами — файл `apps/creds.json` (шаг 4 быстрого старта): он делается из `terraform output` в `pg/`, поэтому после смены хоста или пароля его нужно обновить и повторить `apply` в `apps/`. --- @@ -74,15 +100,11 @@ cd ../apps && cp terraform.tfvars.example terraform.tfvars | `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`, -`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"`, @@ -106,73 +128,6 @@ cd ../apps && cp terraform.tfvars.example terraform.tfvars --- -## Запуск — четыре шага - -### 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/`). Имена доменов и имена -ресурсов приложений задаются в `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` общая). - ---- - ## Повседневные операции | Задача | Команда | @@ -187,21 +142,6 @@ grep -o 'https://[a-z0-9.-]*\.dev\.nubes\.ru' apps/terraform.tfstate | sort -u --- -## Если `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`. - ---- - ## Файлы | Файл | Что делает | @@ -214,7 +154,7 @@ GET {api_endpoint}/instanceOperations/?fields=cfsParams,errorLog,stages | `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** | +| `apps/creds.json` | создаётся шагом 4 быстрого старта, **не в git** | --- diff --git a/docs/curated/crud/three_apps.md b/docs/curated/crud/three_apps.md index 9290b94..779490f 100644 --- a/docs/curated/crud/three_apps.md +++ b/docs/curated/crud/three_apps.md @@ -7,23 +7,49 @@ Манифесты: `TEST_STAND/CRUD/` в репозитории провайдера — два каталога: `pg/` (база) и `apps/` (приложения). `DEV_STAND/CRUD/` — тот же пример, но по старой схеме: всё в одной папке. -## Где взять манифесты - -Клонируйте репозиторий провайдера и перейдите в каталог примера: +## Быстрый старт ```bash +# 1. получить манифесты (нужен установленный Terraform — проверьте `terraform version`) git clone https://gitea.services.ngcloud.ru/terraform/tf_provider.git cd tf_provider/TEST_STAND/CRUD + +# 2. заполнить переменные (что именно — в разделе «Что пользователь задаёт сам») +cd pg && cp terraform.tfvars.example terraform.tfvars # api_token, realm, s3_name +cd ../apps && cp terraform.tfvars.example terraform.tfvars # api_token, realm — те же + +# 3. база данных (кластер создаётся несколько минут) +cd ../pg && terraform init && terraform apply + +# 4. передать креды БД приложениям +terraform output -json > ../apps/creds.json + +# 5. три приложения +cd ../apps && terraform init && terraform apply ``` -Нужен установленный Terraform: +После этого приложения открываются в браузере (имена заданы в `apps/locals.tf`): + +| Приложение | Адрес | +|---|---| +| Lucee | | +| Flask | | +| Node.js | | + +Свои адреса — из state: ```bash -terraform version +grep -o 'https://[a-z0-9.-]*\.dev\.nubes\.ru' apps/terraform.tfstate | sort -u ``` -Провайдер берётся из реестра — источник и версия заданы в `pg/main.tf` и `apps/main.tf`. -Дальше все команды выполняются из каталога `TEST_STAND/CRUD`. +Добавьте запись в одном приложении — она появится в двух других (таблица `crud_items` общая). + +> ⚠️ В `apps/creds.json` пароль лежит **открытым текстом**: файл в `apps/.gitignore` — не коммитить и не пересылать. + +Провайдер берётся из реестра: источник и версия заданы в `pg/main.tf` и `apps/main.tf`. +Все команды выполняются из каталога `TEST_STAND/CRUD`. + +Подробности дальше: что задавать руками, как всё устроено, что делать при ошибке. ## Что создаётся @@ -97,15 +123,11 @@ cd ../apps && cp terraform.tfvars.example terraform.tfvars | `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`, -`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"`, @@ -131,68 +153,6 @@ provider "nubes" { } ``` -## Запуск — четыре шага - -### 1. База данных (`pg/`) - -```bash -cd pg -cp terraform.tfvars.example terraform.tfvars -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`). - -!!! warning "В `creds.json` пароль лежит открытым текстом" - Файл в `apps/.gitignore` — не коммитить и не пересылать. - -### 3. Приложения (`apps/`) - -```bash -cd ../apps -cp terraform.tfvars.example terraform.tfvars -terraform init -terraform apply -``` - -Создадутся три приложения, подключённые к общей БД. - -Обязательные значения — `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/` В `pg/outputs.tf` объявлено шесть выходов: @@ -248,18 +208,4 @@ pg_pass = local.creds.pg_password.value - **`postgres_conf`** — ключи строго как в спецификации платформы: `paramName` / `paramValue` (camelCase). Допустимы только `log_connections` и `log_disconnections`. - **`realm`** — рабочая ресурсная платформа. Если у неё нет ёмкости, операция падает - с сообщением «Невозможно развернуть приложение в данной ресурсной платформе» — смотрите - журнал операции, а не только текст ошибки. - -## Диагностика, если `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`. + с сообщением «Невозможно развернуть приложение в данной ресурсной платформе».