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
This commit is contained in:
@@ -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`.
|
||||
@@ -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) заполнен
|
||||
Reference in New Issue
Block a user