From 0c8223dd11272feb1e988d6bb796dea5873e71b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CNaeel=E2=80=9D?= Date: Thu, 2 Jul 2026 17:38:42 +0400 Subject: [PATCH] docs: HOWTO for cloud devops + update TEST_STAND manifests for new API Gateway - HOWTO_IMPLEMENT_NEW_CLOUD_SERVICE.md: rules for implementing new managed services (naming, params, map-fixed blocks, operations, subresources, validation, checklist) - HOWTO_ADD_NEW_SERVICE.md: how to add service to terraform provider pipeline - TEST_STAND/*/main.tf: version 5.0.57, api_endpoint -> lk-api-gateway-test - TEST_STAND/POSTGRES/resources.tf: rewritten for new map-fixed param structure --- TEST_STAND/LUCEE/main.tf | 4 +- TEST_STAND/MARIA_DB/main.tf | 4 +- TEST_STAND/POSTGRES/main.tf | 4 +- TEST_STAND/POSTGRES/resources.tf | 67 ++--- TEST_STAND/buck0/main.tf | 7 +- docs/HOWTO_ADD_NEW_SERVICE.md | 221 +++++++++++++++++ docs/HOWTO_IMPLEMENT_NEW_CLOUD_SERVICE.md | 282 ++++++++++++++++++++++ 7 files changed, 550 insertions(+), 39 deletions(-) create mode 100644 docs/HOWTO_ADD_NEW_SERVICE.md create mode 100644 docs/HOWTO_IMPLEMENT_NEW_CLOUD_SERVICE.md diff --git a/TEST_STAND/LUCEE/main.tf b/TEST_STAND/LUCEE/main.tf index b3ea881..dec103f 100644 --- a/TEST_STAND/LUCEE/main.tf +++ b/TEST_STAND/LUCEE/main.tf @@ -2,7 +2,7 @@ terraform { required_providers { nubes = { source = "terra.k8c.ru/nubes/nubes" - version = "2.0.6" + version = "5.0.57" } } } @@ -15,6 +15,6 @@ variable "api_token" { provider "nubes" { api_token = var.api_token - api_endpoint = "https://deck-api-test.ngcloud.ru/api/v1/index.cfm" + api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" } diff --git a/TEST_STAND/MARIA_DB/main.tf b/TEST_STAND/MARIA_DB/main.tf index 9417488..dec103f 100644 --- a/TEST_STAND/MARIA_DB/main.tf +++ b/TEST_STAND/MARIA_DB/main.tf @@ -2,7 +2,7 @@ terraform { required_providers { nubes = { source = "terra.k8c.ru/nubes/nubes" - version = "2.0.8" + version = "5.0.57" } } } @@ -15,6 +15,6 @@ variable "api_token" { provider "nubes" { api_token = var.api_token - api_endpoint = "https://deck-api-test.ngcloud.ru/api/v1/index.cfm" + api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" } diff --git a/TEST_STAND/POSTGRES/main.tf b/TEST_STAND/POSTGRES/main.tf index 46cca82..f01c370 100644 --- a/TEST_STAND/POSTGRES/main.tf +++ b/TEST_STAND/POSTGRES/main.tf @@ -2,7 +2,7 @@ terraform { required_providers { nubes = { source = "terra.k8c.ru/nubes-test/nubes" - version = "5.0.75" + version = "5.0.57" } } } @@ -25,7 +25,7 @@ variable "realm" { provider "nubes" { api_token = var.api_token - api_endpoint = "https://deck-api-test.ngcloud.ru/api/v1/index.cfm" + api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" # log_level = "debug" # none | info | debug, default = "none" } diff --git a/TEST_STAND/POSTGRES/resources.tf b/TEST_STAND/POSTGRES/resources.tf index bb34f43..753ce20 100644 --- a/TEST_STAND/POSTGRES/resources.tf +++ b/TEST_STAND/POSTGRES/resources.tf @@ -1,38 +1,46 @@ resource "nubes_postgres" "npg" { - resource_name = "pg4tf033" - s3_uid = var.s3_uid - resource_realm = var.realm - resource_instances = 1 - resource_memory = 512 - resource_c_p_u = 500 - resource_disk = "1" - app_version = "17" - json_parameters = jsonencode({ - log_connections = "off" - log_disconnections = "off" - }) - enable_pg_pooler_master = false - enable_pg_pooler_slave = false - allow_no_s_s_l = false - auto_scale = false - auto_scale_percentage = 10 - auto_scale_tech_window = 0 - auto_scale_quota_gb = "1" - need_external_address_master = false + resource_name = "pg4tf033" + + cluster_configuration = jsonencode({ + platform = var.realm + instances = 1 + memory = 512 + cpu = 500 + disk = "1" + }) + + startup_configuration = jsonencode({ + appVersion = "17" + }) + + access_configuration = jsonencode({ + needExternalAddressMaster = false + }) + + postgres_configuration = jsonencode({ + enablePgPoolerMaster = false + enablePgPoolerSlave = false + allowNoSsl = false + s3Uid = var.s3_uid + jsonParameters = jsonencode({ + log_connections = "off" + log_disconnections = "off" + }) + }) + + backup_configuration = jsonencode({}) + + autoscale_configuration = jsonencode({ + autoScale = false + percentage = 10 + techWindow = 0 + quotaGb = "1" + }) - # suspend_on_destroy = false operation_timeout = "11m" adopt_existing_on_create = true } - -# resource "nubes_s3bucket" "baba_bucket" { -# resource_name = "baba-buck-test1" -# s3_user_uid = var.s3_uid -# bucket_name = "baba000" -# adopt_existing_on_create=true -# } - resource "nubes_postgres_user" "pg_user" { postgres_id = nubes_postgres.npg.id username = "user0" @@ -45,6 +53,5 @@ resource "nubes_postgres_database" "db2_app" { db_name = "db_terra" db_owner = nubes_postgres_user.pg_user.username adopt_existing_on_create = true - # suspend_on_destroy = false } diff --git a/TEST_STAND/buck0/main.tf b/TEST_STAND/buck0/main.tf index 382d7cd..5e0ddd3 100644 --- a/TEST_STAND/buck0/main.tf +++ b/TEST_STAND/buck0/main.tf @@ -2,20 +2,21 @@ terraform { required_providers { nubes = { source = "terra.k8c.ru/nubes-test/nubes" - version = "5.0.75" + version = "5.0.57" } } } provider "nubes" { api_token = var.api_token - api_endpoint = "https://deck-api-test.ngcloud.ru/api/v1/index.cfm" + api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" insecure = true } # S3 bucket — замени "buck0" на своё имя везде ниже resource "nubes_s3bucket" "bukka" { # ← замени buck0 на своё имя ресурса resource_name = "Бакет тест1" # ← замени buck0 на своё имя ресурса - s3_user_uid = var.s3_user_uid + s3_user_uid = "naeel-s3" + # s3_user_uid = var.s3_user_uid bucket_name = "buck0" # ← замени buck0 на своё имя бакета } diff --git a/docs/HOWTO_ADD_NEW_SERVICE.md b/docs/HOWTO_ADD_NEW_SERVICE.md new file mode 100644 index 0000000..b4fa293 --- /dev/null +++ b/docs/HOWTO_ADD_NEW_SERVICE.md @@ -0,0 +1,221 @@ +# Инструкция по добавлению нового сервиса в Terraform-провайдер Nubes + +## 1. Где прописывать + +Единственная точка входа — `devops/config/services_list.txt` (или профильный `profiles/{stand}/services_list.txt`). + +Формат строки: +``` +{service_id} {service_name} # Описание (опционально) +``` + +Пример: +``` +90 postgres # Управляемая база данных PostgreSQL +``` + +- **service_id** — число, ID сервиса из API Nubes (`/services/{id}`) +- **service_name** — snake_case, латиница. Будет использоваться как имя ресурса `nubes_{name}` + +Исключение сервиса — закомментировать строку `#`. + +--- + +## 2. Что происходит после добавления в список + +Пайплайн (3 шага): + +``` +services_list.txt + → 01_generate_yamls.sh → запрос к API → resources_yaml/{id}_{name}.yaml + → 02_generate_resources_and_docs_v2.sh → internal/resources_gen/{name}_resource.go + docs + → 03_build_and_upload_provider.sh → сборка + S3 +``` + +**Никаких ручных правок YAML или сгенерированного Go-кода.** Всё из API. + +--- + +## 3. Структура YAML (что генерируется) + +```yaml +name: postgres # snake_case, из services_list.txt +service_id: 90 # ID сервиса +service_display_name: PostgreSQL # человекочитаемое имя +service_short_name: postgres # краткое имя из API +service_man: "описание..." # MAN-руководство (HTML) + +lifecycle: + suspend_on_destroy_default: true # есть ли операция suspend + adopt_existing_on_create_default: false + +outputs: + params: # выходные параметры (всегда одинаковые) + - code: state_params; type: map + - code: state_out; type: map + - code: vault_secrets; type: map; sensitive: true + - code: vault_url; type: string + ... + +operations: + - name: create # имя операции из API (snake_case) + id: 19 # svcOperationId + kind: instance # instance | subresource | action + action: create # create | modify | delete | suspend | ... + man: "руководство" # описание операции + params: + - id: 788 # svcOperationCfsParamId + code: clusterConfiguration + data_type: map-fixed + required: true + sort: 20 + is_modifiable: true +``` + +--- + +## 4. Типы параметров (data_type) + +| Тип | Пример | Когда использовать | +|---|---|---| +| `string` | `resourceRealm`, `domain`, `dbName` | Имена, домены, UUID, строки | +| `integer > 0` | `resourceCPU`, `resourceMemory` | Числовые значения больше нуля | +| `integer >= 0` | `autoScaleTechWindow` | Числовые, допускающие 0 | +| `boolean` | `enablePgPoolerMaster`, `allowNoSsl` | Флаги вкл/выкл | +| `uuid` | `s3Uid`, `vdcUid` | Ссылки на другие сервисы (ref) | +| `map-fixed` | `clusterConfiguration` | **Сложные K8s-сервисы**: JSON-объект с фиксированным набором полей | +| `array-map-fixed` | `postgresConf` | Массив JSON-объектов (доп. конфигурации) | +| `json` | `jsonEnv`, `jsonParameters` | Произвольный JSON | +| `map` | `state_params` | Только в outputs | +| `yaml` | — | YAML-строка (редко) | + +--- + +## 5. Виды операций (kind) + +### instance — управление жизненным циклом сервиса + +| action | Terraform | Пример | +|---|---|---| +| `create` | `resource "nubes_X" "Y" {}` | Создание сервиса | +| `delete` | `terraform destroy` | Удаление | +| `modify` | изменение параметров → `apply` | Модификация | +| `suspend` | `suspend_on_destroy = true` | Остановка (без удаления) | +| `resume` | `adopt_existing_on_create = true` | Запуск остановленного | + +**Правило**: если у сервиса есть `suspend` И `resume` → `suspend_on_destroy_default = true`. + +### subresource — вложенные объекты + +Стандартные subresource'ы: +- **user** → `nubes_{service}_user` (create/delete) + - Параметры: `username` (string, required), `role` (string, required, value_list) +- **database** → `nubes_{service}_database` (create/delete) + - Параметры: `dbName` (string, required, regex), `dbOwner` (string, required) +- **topic** → Kafka +- **backup** → S3, PostgreSQL +- **vdc** → vcOrg + +**Правило**: subresource всегда ссылается на родительский ресурс через `{service}_id`. + +### action — разовые операции + +| action | Когда | +|---|---| +| `reconcile` | Синхронизация состояния (у 20 сервисов) | +| `redeploy` | Переразвёртывание (приложения: flask, nodejs, lucee, ...) | +| `restart` | Перезапуск (postgres, mariadb, ...) | +| `recovery` | Восстановление из бэкапа | + +**Правило**: action-ресурсы используют trigger-поле (`run_id`/`nonce`) для идемпотентности. + +--- + +## 6. Валидация параметров (автоматически из API) + +| Механизм | Параметров | Пример | +|---|---|---| +| `regex` | 29 | `dbName: ^[A-Za-z0-9]+$` | +| `value_list` | 38 | `role: [app_user, ddl_user]` | +| `minlength`/`maxlength` | 87/74 | `username: min 2, max 62` | +| `minvalue`/`maxvalue` | — | `resourceCPU > 0` | +| `default` | 120 | `deleteS3Bucket: true` | +| `is_modifiable` | 106 | Можно менять после create | +| `is_sensitive` | 2 | `vault_secrets` | + +--- + +## 7. Что запрещено + +- ❌ **Править сгенерированные YAML вручную** — источник истины только API +- ❌ **Править `internal/resources_gen/*.go` вручную** — перезапишется при следующей генерации +- ❌ **Использовать кириллицу в `service_name`** — только латиница, snake_case +- ❌ **Менять `embed.go`** — генерируется автоматически +- ❌ **Пропускать сервис через комментарий без причины** — лучше явно указать причину в комментарии: `# 24 DEPRECATED ...` + +--- + +## 8. Быстрый старт: добавляем новый сервис + +```bash +# 1. Добавить строку в services_list.txt +echo "200 my_new_service # Моя новая услуга" >> devops/config/services_list.txt + +# 2. Сгенерировать YAML (test-стенд) +devops/01_generate_yamls.sh --profile devops/profiles/test + +# 3. Сгенерировать Go-код + доки +devops/02_generate_resources_and_docs_v2.sh --profile devops/profiles/test + +# 4. Проверить что появился файл +ls devops/profiles/test/generated/resources_yaml/200_my_new_service.yaml +ls devops/profiles/test/generated/go/200_my_new_service_resource.go + +# 5. Собрать и задеплоить провайдер +devops/03_build_and_upload_provider.sh --profile devops/profiles/test + +# 6. Написать тестовый манифест в TEST_STAND/my_new_service/main.tf +# 7. terraform init && terraform plan && terraform apply +``` + +--- + +## 9. Как писать тестовый манифест + +### Простой сервис (flat-параметры) + +```hcl +resource "nubes_s3bucket" "test" { + resource_name = "Мой бакет" + s3_user_uid = var.s3_uid + bucket_name = "test-bucket" +} +``` + +### Сложный сервис (map-fixed) + +```hcl +resource "nubes_postgres" "test" { + resource_name = "pg-test" + + cluster_configuration = jsonencode({ + platform = "k8s-3.ext.nubes.ru" + instances = 1 + memory = 512 + cpu = 500 + disk = "1" + }) + + startup_configuration = jsonencode({ + appVersion = "17" + }) + + access_configuration = jsonencode({ + needExternalAddressMaster = false + }) + + # ... остальные блоки +} +``` + +**JSON-ключи внутри map-fixed** — camelCase-версии старых плоских названий параметров. Точные имена полей смотри в сгенерированном YAML (поле `code` у параметров в `operations.params`) или в `generated/docs/{service}_params_create.md`. diff --git a/docs/HOWTO_IMPLEMENT_NEW_CLOUD_SERVICE.md b/docs/HOWTO_IMPLEMENT_NEW_CLOUD_SERVICE.md new file mode 100644 index 0000000..356012c --- /dev/null +++ b/docs/HOWTO_IMPLEMENT_NEW_CLOUD_SERVICE.md @@ -0,0 +1,282 @@ +# Инструкция для DevOps облака: имплементация нового managed-сервиса + +> На основе анализа 43 сервисов Nubes Cloud (июль 2026). +> Цель: единый стандарт, чтобы любой новый сервис был консистентен с существующими. + +--- + +## 1. Классификация сервиса + +Выбери один из двух классов ДО начала проектирования параметров: + +| Класс | Признак | Размещение | Примеры | +|---|---|---|---| +| **Простой** | Не требует оркестрации K8s | VM, сеть, хранилище | s3bucket, vc_vm, dnszone, vcexternalip, harbor | +| **Сложный (K8s)** | Разворачивается в Kubernetes через оператор | Pods на кластере | postgres, redis, kafka, clickhouse, flask, nextcloud | + +**Правило**: если сервис крутится в K8s → используй **map-fixed** блоки (раздел 3). Если нет → плоские параметры (раздел 2). + +--- + +## 2. Простой сервис: плоские параметры + +### Обязательный минимум + +Каждый сервис ДОЛЖЕН иметь эти параметры в create: + +| code | data_type | required | Назначение | +|---|---|---|---| +| `resourceRealm` | `string` | true | Платформа/K8s-кластер для развёртывания | +| `resourceName` | `string` | true | Человекочитаемое имя инстанса (displayName) | + +### Стандартные ресурсные параметры + +Добавляй по необходимости: + +| code | data_type | Назначение | +|---|---|---| +| `resourceInstances` | `integer > 0` | Количество реплик/нод (default: 1) | +| `resourceMemory` | `integer > 0` | Память в MB | +| `resourceCPU` | `integer > 0` | CPU в милликорах (1000 = 1 vCPU) | +| `resourceDisk` | `string` | Диск в GB | + +### Прочие частые параметры + +| code | data_type | Где используется | +|---|---|---| +| `domain` | `string` | Сервисы с доменным именем (9 из 43) | +| `ipSpaceName` | `string` | Сервисы с внешним IP | +| `appConfiguration` | `map-fixed` | Приложения (nextcloud, superset, harbor, ...) | +| `jsonEnv` | `json` | Переменные окружения (flask, nodejs) | +| `storageConfig` | `map-fixed` | Хранилище (kafka, clickhouse) | + +--- + +## 3. Сложный сервис (K8s): map-fixed блоки + +**Правило**: группируй параметры в логические блоки. Используй этот стандартный набор: + +### 3.1. startupConfiguration (sort: 10) +**Назначение**: версия ПО, образ, всё что задаётся до старта. +``` +appVersion, image, imageTag, ... +``` +**required**: true. **is_modifiable**: false (не меняется после создания). + +### 3.2. clusterConfiguration (sort: 20) +**Назначение**: размер кластера, ресурсы. +``` +platform (= resourceRealm), instances, memory, cpu, disk +``` +**required**: true. **is_modifiable**: true. + +### 3.3. accessConfiguration (sort: 30) +**Назначение**: сетевой доступ. +``` +needExternalAddressMaster, ipSpaceNameMaster, +needExternalAddressSlave, ipSpaceNameSlave, +allowNoSsl +``` +**required**: true. **is_modifiable**: true. + +### 3.4. {service}Configuration (sort: 40) +**Назначение**: специфичные для сервиса настройки. +``` +enablePgPoolerMaster, enablePgPoolerSlave, s3Uid, jsonParameters, ... +``` +**required**: true. **is_modifiable**: true. + +### 3.5. {service}Conf (sort: 50) +**Назначение**: массив дополнительных конфигураций. +Тип: `array-map-fixed`. +**required**: false. **is_modifiable**: true. + +### 3.6. backupConfiguration (sort: 60) +**Назначение**: политика резервного копирования. +``` +schedule (cron), numToRetain, ... +``` +**required**: true. **is_modifiable**: true. + +### 3.7. autoscaleConfiguration (sort: 70) +**Назначение**: автоскейлинг. +``` +autoScale (boolean), percentage, techWindow, quotaGb +``` +**required**: true. **is_modifiable**: true. + +### Пример структуры для нового K8s-сервиса + +``` +create params (sort order): + 10: startupConfiguration map-fixed required + 20: clusterConfiguration map-fixed required modifiable + 30: accessConfiguration map-fixed required modifiable + 40: {name}Configuration map-fixed required modifiable + 50: {name}Conf array-map optional modifiable + 60: backupConfiguration map-fixed required modifiable + 70: autoscaleConfiguration map-fixed required modifiable +``` + +--- + +## 4. Именование параметров + +### ⛔ Жёсткие правила + +- **camelCase** для ВСЕХ кодов параметров: `resourceRealm`, `clusterConfiguration`, `dbName` +- **Никакого snake_case**: ❌ `resource_realm`, ✅ `resourceRealm` +- **Никакого хаотичного нейминга**: если параметр про память — везде `resourceMemory`, не `memoryQuota` или `memLimit` +- **Префиксы**: общие параметры с префиксом `resource*` (resourceRealm, resourceCPU, resourceMemory, resourceDisk, resourceInstances) + +### Стандартный словарь + +| Концепт | Код параметра | +|---|---| +| Платформа/K8s-кластер | `resourceRealm` | +| Количество реплик | `resourceInstances` | +| Память (MB) | `resourceMemory` | +| CPU (millicore) | `resourceCPU` | +| Диск (GB) | `resourceDisk` | +| Версия ПО | `appVersion` | +| Домен | `domain` | +| S3-ссылка | `s3Uid` | +| IP-space | `ipSpaceName` | +| Внешний IP для master | `needExternalAddressMaster` | +| Внешний IP для slave | `needExternalAddressSlave` | +| PgBouncer master | `enablePgPoolerMaster` | +| PgBouncer slave | `enablePgPoolerSlave` | +| Отключить SSL | `allowNoSsl` | +| Автоскейлинг | `autoScale` | +| Cron бэкапа | `backupSchedule` | + +--- + +## 5. Операции + +### Обязательные (каждый сервис) + +| operation | kind | action | +|---|---|---| +| `create` | instance | create | +| `delete` | instance | delete | + +### Настоятельно рекомендуемые + +| operation | kind | action | Зачем | +|---|---|---|---| +| `modify` | instance | modify | Изменение параметров без удаления | +| `suspend` | instance | suspend | Остановка без удаления (биллинг!) | +| `resume` | instance | resume | Запуск после suspend | + +**Правило**: если реализовал `suspend` → ОБЯЗАТЕЛЬНО реализовать `resume`. И наоборот. + +### Опциональные + +| operation | kind | action | У кого есть | +|---|---|---|---| +| `restart` | action | restart | postgres, mariadb, redis, kafka | +| `recovery` | action | recovery | postgres, clickhouse | +| `reconcile` | action | reconcile | 20 сервисов (универсальная синхронизация) | +| `redeploy` | action | redeploy | flask, nodejs, lucee, nifi, superset | + +--- + +## 6. Subresource'ы + +### Стандартные + +| subresource | operations | Параметры | У скольких сервисов | +|---|---|---|---| +| **user** | create_user, delete_user | `username` (string, regex), `role` (string, value_list) | 16 | +| **database** | create_database, delete_database | `dbName` (string, regex), `dbOwner` (string) | 6 | + +### Специфичные + +| subresource | Где | +|---|---| +| `topic` | kafka (3 операции) | +| `backup` | s3, postgres | +| `vdc` | vcOrg | +| `sub_user` | openwhisk | + +### Правила subresource'ов + +- **Именование операций**: `create_{subresource}`, `delete_{subresource}` (snake_case глагол + имя) +- **Именование subresource**: одно слово, snake_case: `user`, `database`, `topic` +- **Ссылка на родителя**: обязательный UUID-параметр, ссылающийся на родительский инстанс +- **Параметр `role` для user**: ОБЯЗАТЕЛЬНО `value_list` с вариантами (например `[app_user, ddl_user]`) +- **Параметр `dbName` для database**: ОБЯЗАТЕЛЬНО `regex: ^[A-Za-z0-9]+$` + +--- + +## 7. Валидация параметров + +### Обязательно (где применимо) + +| Механизм | Когда | Пример | +|---|---|---| +| `value_list` | Ограниченный набор значений | `role: [app_user, ddl_user]` | +| `regex` | Имена, идентификаторы | `dbName: ^[A-Za-z0-9]+$` | +| `minlength` | Минимальная длина строки | `username: min 2` | +| `maxlength` | Максимальная длина строки | `username: max 62` | +| `minvalue` | Минимальное число | `resourceCPU: > 0` | +| `maxvalue` | Максимальное число | — | +| `default` | Значение по умолчанию | `deleteS3Bucket: true` | + +### ⛔ Запрещено + +- Параметр без `descr` (описание) — ВСЕГДА заполнять +- Булевы параметры без `default` — если не указан, поведение неопределено +- `required: true` для параметра с `default` — бессмысленно + +--- + +## 8. Модифицируемость (is_modifiable) + +### Правило + +| Категория параметра | is_modifiable | +|---|---| +| Имя, версия ПО, платформа (startup) | **false** | +| Ресурсы (CPU, память, диск, реплики) | **true** | +| Доступ (IP, SSL, pooler) | **true** | +| Бэкапы, автоскейлинг | **true** | +| Всё что в modify-операции | **true** | + +106 из 435 параметров (24%) имеют `is_modifiable: true`. + +--- + +## 9. Outputs + +**Не трогать.** Стандартный набор выходных параметров един для всех сервисов: + +``` +state_params map +state_out map +state_params_flat map +state_out_flat map +vault_secrets map sensitive +vault_url string +vault_user_path string +vault_fields list +``` + +--- + +## 10. Чек-лист перед сдачей сервиса + +- [ ] `resourceRealm` есть в create (required) +- [ ] `resourceName` есть в create (required) +- [ ] Все коды параметров — camelCase +- [ ] Для K8s-сервиса: 6 стандартных map-fixed блоков с правильными sort +- [ ] `suspend` + `resume` либо есть оба, либо нет ни одного +- [ ] `modify` содержит все `is_modifiable: true` параметры из create +- [ ] У всех параметров заполнен `descr` +- [ ] Булевы параметры имеют `default` +- [ ] `username` для user-subresource имеет regex и minlength/maxlength +- [ ] `role` для user-subresource имеет value_list +- [ ] `dbName` для database-subresource имеет regex +- [ ] MAN (service_man) заполнен: описание, параметры, примеры +- [ ] MAN для каждой операции (operation.man) заполнен