docs: пометить отменённый заход модификаторов как LEGACY + исправить ложные факты
- баннеры «ЛОЖНЫЙ ПУТЬ — ОТМЕНЕНО» на 4 файла HISTORY/OPUS/2026-09-22_modifier_* и docs/60_strategy/modifier_resources_ideology_and_specification.md - vIPConfigure: replace-семантика, НЕ накопительная (по тесту docs/ORG_IP_MODIFIER_TEST_2026-09-22.md) - обновлены ссылки на перенесённые материалы (docs/... -> NOTES/..., HOW_TO/...)
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
# DevOps Runbook: Provider Build Pipeline
|
||||
|
||||
This repo root contains the 4 scripts for the full provider build pipeline.
|
||||
|
||||
## Overview
|
||||
|
||||
1) Generate YAML specs from API
|
||||
2) Generate Go resources + documentation files from YAML
|
||||
3) Build and upload provider binaries for 3 OS targets
|
||||
4) Build and publish documentation site
|
||||
|
||||
## Documentation publishing instructions
|
||||
|
||||
The verified documentation generation and publishing pipeline is documented in
|
||||
[`HISTORY/2026-09-03_docs_upload_pipeline_verified.md`](HISTORY/2026-09-03_docs_upload_pipeline_verified.md).
|
||||
It covers the generated docs source, MkDocs build, the separate documentation
|
||||
S3 bucket, VM upload and mirror steps, stand-specific URLs, and the legacy
|
||||
script that must not be used.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Go 1.22+
|
||||
- `python3`
|
||||
- `gpg`
|
||||
- `mc` (MinIO/S3 client)
|
||||
- Docker (for mkdocs build)
|
||||
|
||||
## Shared settings
|
||||
|
||||
S3 environment:
|
||||
- `S3_ENDPOINT` (example: `https://s3.msk-1.ngcloud.ru`)
|
||||
- `S3_ACCESS_KEY`
|
||||
- `S3_SECRET_KEY`
|
||||
|
||||
Provider naming defaults:
|
||||
- `REGISTRY_HOSTNAME`: `tf-registry.containerk8s.services.ngcloud.ru`
|
||||
- `NAMESPACE`: `nubes`
|
||||
- `NAME`: `nubes`
|
||||
|
||||
## Step 1: Generate YAMLs from API
|
||||
|
||||
Script: `01_generate_yamls.sh`
|
||||
|
||||
Input list of services:
|
||||
- `services_list.txt` (service_id only)
|
||||
|
||||
Token options:
|
||||
- `TOKEN_FILE=/home/naeel/terra/HH-MM-SS.token`, or
|
||||
- `NUBES_API_TOKEN` directly
|
||||
|
||||
Example:
|
||||
```bash
|
||||
export TOKEN_FILE=/home/naeel/terra/08-33-41.token
|
||||
./01_generate_yamls.sh
|
||||
```
|
||||
|
||||
## Step 2: Generate Go resources and docs
|
||||
|
||||
Script: `TOOLS/scripts/02_generate_resources_and_docs_v2.sh`
|
||||
|
||||
Example:
|
||||
```bash
|
||||
./TOOLS/scripts/02_generate_resources_and_docs_v2.sh --profile TOOLS/config/dev
|
||||
```
|
||||
|
||||
Outputs:
|
||||
- Go files in `generated/<stand>/go`
|
||||
- Docs in `generated/<stand>/docs`
|
||||
|
||||
Important:
|
||||
- The v2 script always rebuilds `resource-generator` and `docs-generator` from source before running.
|
||||
- Do not invoke stale binaries from `TOOLS/resource-generator/bin/` or `TOOLS/docs-generator/bin/` directly.
|
||||
|
||||
## Step 3: Build and upload provider
|
||||
|
||||
Script: `03_build_and_upload_provider.sh`
|
||||
|
||||
Uses `registry-server-build/build-provider.sh` and signs with:
|
||||
- `secrets/private_key.asc` (ignored by git)
|
||||
|
||||
Example:
|
||||
```bash
|
||||
export S3_ENDPOINT=https://s3.msk-1.ngcloud.ru
|
||||
export S3_ACCESS_KEY=...
|
||||
export S3_SECRET_KEY=...
|
||||
./03_build_and_upload_provider.sh 2.0.2
|
||||
```
|
||||
|
||||
## Step 4: Build and publish docs
|
||||
|
||||
Script: `04_build_and_publish_docs.sh`
|
||||
|
||||
Example:
|
||||
```bash
|
||||
export S3_ENDPOINT=https://s3.msk-1.ngcloud.ru
|
||||
export S3_ACCESS_KEY=...
|
||||
export S3_SECRET_KEY=...
|
||||
./04_build_and_publish_docs.sh 2.0.2
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- The GPG private key must remain stable across releases. Do not regenerate per build.
|
||||
- If the key is regenerated, the registry server must be updated to serve the new public key.
|
||||
- Terraform will fail with `authentication signature from unknown issuer` if the registry public key does not match the signing key.
|
||||
- `services_list.txt` is the source of truth for which services are generated.
|
||||
- If the provider version changes, update `universal_rebuild/main.go`.
|
||||
|
||||
## One-time GPG bootstrap (do this once, keep the key stable)
|
||||
|
||||
1) Generate and export keys (no passphrase):
|
||||
```bash
|
||||
GPG_DIR=${ROOT_DIR}/secrets
|
||||
GNUPGHOME=$(mktemp -d)
|
||||
cat > /tmp/gpg_batch <<'EOF'
|
||||
%no-protection
|
||||
Key-Type: RSA
|
||||
Key-Length: 4096
|
||||
Subkey-Type: RSA
|
||||
Subkey-Length: 4096
|
||||
Name-Real: tazet@narod.ru
|
||||
Name-Email: tazet@narod.ru
|
||||
Expire-Date: 0
|
||||
EOF
|
||||
gpg --batch --homedir "$GNUPGHOME" --gen-key /tmp/gpg_batch
|
||||
gpg --batch --homedir "$GNUPGHOME" --armor --export-secret-keys > "$GPG_DIR/private_key.asc"
|
||||
gpg --batch --homedir "$GNUPGHOME" --armor --export > "$GPG_DIR/public_key.asc"
|
||||
rm -rf "$GNUPGHOME" /tmp/gpg_batch
|
||||
```
|
||||
|
||||
2) Update registry server public key (ASCII Armor) in:
|
||||
- `registry-server-build/main.go`
|
||||
- `operator/cmd/registry/main.go`
|
||||
|
||||
3) Rebuild and redeploy the registry server (see `docs/50_history/00_system_mechanics.md`).
|
||||
|
||||
4) Build and upload provider artifacts as usual.
|
||||
|
||||
# check string
|
||||
@@ -0,0 +1,145 @@
|
||||
# Как собрать и залить провайдер
|
||||
|
||||
## ⛔ Схема версий
|
||||
|
||||
**Первая цифра версии жёстко привязана к стенду. НЕ ПУТАТЬ.**
|
||||
|
||||
| Стенд | Namespace | Первая цифра | Профиль |
|
||||
|---|---|---|---|
|
||||
| **PROD** | `nubes` | `2.*` | `TOOLS/config/prod` |
|
||||
| **DEV** | `nubes-dev` | `3.*` | `TOOLS/config/dev` |
|
||||
| **TEST** | `nubes-test` | `5.*` | `TOOLS/config/test` |
|
||||
|
||||
## Архитектура конфигурации
|
||||
|
||||
```
|
||||
TOOLS/config/
|
||||
├── registry.env ← ЕДИНЫЙ реестр (НЕ зависит от стенда)
|
||||
│ REGISTRY_HOSTNAME = tf-registry.containerk8s.services.ngcloud.ru
|
||||
│ S3_ENDPOINT = https://s3.msk-1.ngcloud.ru
|
||||
│ S3_BUCKET = nubes-terraform-registry
|
||||
│
|
||||
├── dev/profile.env ← стенд-специфика
|
||||
│ NUBES_API_ENDPOINT = ...dev...
|
||||
│ TOKEN_FILE = secrets/dev.token
|
||||
│ NAMESPACE = nubes-dev
|
||||
│ VERSION = 3.x.x
|
||||
│
|
||||
├── test/profile.env
|
||||
└── prod/profile.env
|
||||
```
|
||||
|
||||
Скрипты подгружают ОБА файла: `registry.env` → общие настройки реестра, `profile.env` → стенд-специфика.
|
||||
|
||||
**Чтобы сменить реестр** — правишь только `TOOLS/config/registry.env`.
|
||||
|
||||
## Полный пайплайн (3 шага)
|
||||
|
||||
### Требования
|
||||
|
||||
- Токены в `secrets/{dev,test,prod}.token`
|
||||
- `secrets/private_key.asc` — GPG-ключ для подписи
|
||||
- `secrets/.s3cfg_registry` — S3-креды (или `S3_ACCESS_KEY`/`S3_SECRET_KEY` в env)
|
||||
- `go` установлен
|
||||
- `mc` (MinIO Client) установлен, алиас `prod-s3`
|
||||
|
||||
### Шаг 1 — Сгенерировать YAML из API
|
||||
|
||||
```bash
|
||||
cd ~/tf_provider
|
||||
./TOOLS/scripts/01_generate_yamls.sh --profile TOOLS/config/dev
|
||||
```
|
||||
|
||||
Запрашивает спецификации сервисов из API стенда → пишет YAML в `generated/dev/resources_yaml/`.
|
||||
|
||||
### Шаг 2 — Сгенерировать Go-ресурсы и документацию
|
||||
|
||||
```bash
|
||||
./TOOLS/scripts/02_generate_resources_and_docs_v2.sh --profile TOOLS/config/dev
|
||||
```
|
||||
|
||||
Из YAML генерирует:
|
||||
- `generated/dev/go/` — Go-код ресурсов
|
||||
- `generated/dev/docs/` — Markdown-документацию
|
||||
|
||||
### Шаг 3 — Собрать и залить в реестр
|
||||
|
||||
```bash
|
||||
./TOOLS/scripts/03_build_and_upload_provider.sh --profile TOOLS/config/dev 3.1.13
|
||||
```
|
||||
|
||||
Компилирует (linux/windows/darwin), подписывает GPG, заливает в S3.
|
||||
|
||||
### Одной командой (для ленивых)
|
||||
|
||||
```bash
|
||||
./TOOLS/scripts/01_generate_yamls.sh --profile TOOLS/config/dev && \
|
||||
./TOOLS/scripts/02_generate_resources_and_docs_v2.sh --profile TOOLS/config/dev && \
|
||||
./TOOLS/scripts/03_build_and_upload_provider.sh --profile TOOLS/config/dev 3.1.13
|
||||
```
|
||||
|
||||
## Быстрая заливка (без перегенерации YAML/Go)
|
||||
|
||||
Если YAML'ы и Go-код уже сгенерированы и не менялись — только шаг 3:
|
||||
|
||||
```bash
|
||||
# DEV
|
||||
./TOOLS/scripts/03_build_and_upload_provider.sh --profile TOOLS/config/dev 3.1.13
|
||||
|
||||
# TEST
|
||||
./TOOLS/scripts/03_build_and_upload_provider.sh --profile TOOLS/config/test 5.1.17
|
||||
|
||||
# PROD
|
||||
./TOOLS/scripts/03_build_and_upload_provider.sh --profile TOOLS/config/prod 2.1.23
|
||||
```
|
||||
|
||||
Креды S3 подхватываются из `secrets/.s3cfg_registry`. Или через env:
|
||||
```bash
|
||||
export S3_ACCESS_KEY=...
|
||||
export S3_SECRET_KEY=...
|
||||
```
|
||||
|
||||
## Структура S3
|
||||
|
||||
```
|
||||
nubes-terraform-registry/
|
||||
└── tf-registry.containerk8s.services.ngcloud.ru/
|
||||
└── {namespace}/
|
||||
└── nubes/
|
||||
└── {version}/
|
||||
├── terraform-provider-nubes_{ver}_linux_amd64.zip
|
||||
├── terraform-provider-nubes_{ver}_windows_amd64.zip
|
||||
├── terraform-provider-nubes_{ver}_darwin_amd64.zip
|
||||
├── terraform-provider-nubes_{ver}_SHA256SUMS
|
||||
└── terraform-provider-nubes_{ver}_SHA256SUMS.sig
|
||||
```
|
||||
|
||||
## Проверка после заливки
|
||||
|
||||
```bash
|
||||
# DEV
|
||||
curl -s https://tf-registry.containerk8s.services.ngcloud.ru/v1/providers/nubes-dev/nubes/versions | python3 -m json.tool
|
||||
|
||||
# TEST
|
||||
curl -s https://tf-registry.containerk8s.services.ngcloud.ru/v1/providers/nubes-test/nubes/versions | python3 -m json.tool
|
||||
|
||||
# PROD
|
||||
curl -s https://tf-registry.containerk8s.services.ngcloud.ru/v1/providers/nubes/nubes/versions | python3 -m json.tool
|
||||
```
|
||||
|
||||
## Актуальные версии
|
||||
|
||||
Файл [`VERSIONS.md`](VERSIONS.md) — единственный источник правды. После каждой заливки — обновить.
|
||||
|
||||
## Terraform-конфиг пользователя
|
||||
|
||||
```hcl
|
||||
terraform {
|
||||
required_providers {
|
||||
nubes = {
|
||||
source = "tf-registry.containerk8s.services.ngcloud.ru/nubes-dev/nubes"
|
||||
version = "3.1.13"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -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) заполнен
|
||||
@@ -0,0 +1,182 @@
|
||||
# Архитектура документации Nubes Terraform Provider
|
||||
|
||||
## Источники истины
|
||||
|
||||
| Уровень | Источник | Роль |
|
||||
|---|---|---|
|
||||
| 1 | **API (YAML-спеки)** | Единственный источник правды. Параметры, типы, defaults, constraints, operations — только из YAML |
|
||||
| 2 | **MAN (service_man)** | Дополнительный контекст. Может устареть или содержать ошибки. Используется для улучшения формулировок, но НЕ переопределяет YAML |
|
||||
| 3 | **LLM (gpt-oss-120b)** | Обрабатывает .md файлы: улучшает читаемость, добавляет логику, переводит HTML→Markdown. Меняет ТОЛЬКО текст описаний, НЕ параметры |
|
||||
|
||||
## Пайплайн генерации
|
||||
|
||||
```
|
||||
YAML-спеки
|
||||
│
|
||||
▼
|
||||
docs-generator (Go) ─── создаёт структуру: Name.md, Name_example.md, Name_params_create.md, ...
|
||||
│ HCL-блоки, HTML-таблицы параметров, navigation, index.md
|
||||
▼
|
||||
LLM (gpt-oss-120b) ─── улучшает описания: читаемый Markdown, логичные формулировки
|
||||
│ вход: YAML + MAN + текущие .md
|
||||
│ выход: переписанные .md (структура и параметры неизменны)
|
||||
▼
|
||||
mkdocs-material ─────── собирает статический сайт
|
||||
│
|
||||
▼
|
||||
S3 (terraform-registry) ── хостинг через registry.kube5s.ru <!-- ⛔ LEGACY: registry.kube5s.ru ЗАКРЫТ. Актуальный хост: tf-registry.containerk8s.services.ngcloud.ru --> <!-- ⛔ LEGACY: registry.kube5s.ru <!-- ⛔ LEGACY: registry.kube5s.ru ЗАКРЫТ. Актуальный хост: tf-registry.containerk8s.services.ngcloud.ru --> ЗАКРЫТ. Актуальный хост: tf-registry.containerk8s.services.ngcloud.ru -->
|
||||
```
|
||||
|
||||
## Структура страниц (на каждый сервис)
|
||||
|
||||
| Файл | Содержание |
|
||||
|---|---|
|
||||
| `Name.md` | Главная: краткое описание + MAN в `??? note` (mkdocs-native admonition) |
|
||||
| `Name_example.md` | HCL-пример с полным манифестом |
|
||||
| `Name_params_create.md` | Таблицы Create-параметров + вложенные sub_params |
|
||||
| `Name_params_modify.md` | Таблицы Modify-параметров |
|
||||
| `Name_outputs.md` | Выходные параметры + реальные ключи из облака |
|
||||
| `Name_ops.md` | Список операций (create/delete/modify/suspend/resume) |
|
||||
| `Name_params.md` | Лендинг: ссылки на create/modify params |
|
||||
| `Name_subresource.md` | Для каждого subresource: параметры |
|
||||
| `Name_subresource_example.md` | HCL-пример subresource |
|
||||
|
||||
## Формат MAN (service_man) — как рендерится
|
||||
|
||||
`service_man` из YAML содержит **смесь Markdown и HTML**: заголовки `#`/`##`, списки `-`, bold `**`, горизонтальные линии `---`, а также `<br/>` и HTML-entities.
|
||||
|
||||
### Конвертация: `htmlToMarkdown()`
|
||||
|
||||
```go
|
||||
// 1. <br/> → \n
|
||||
// 2. <h1>/<h2>/<h3> → # / ## / ###
|
||||
// 3. <strong>/<b> → **...**
|
||||
// 4. <em>/<i> → *...*
|
||||
// 5. <code> → `...`
|
||||
// 6. <a href> → [...](...)
|
||||
// 7. <ul><li> → - ...
|
||||
// 8. Strip remaining HTML tags
|
||||
// 9. Unescape HTML entities (" → ")
|
||||
// 10. Collapse 3+ blank lines → 2
|
||||
```
|
||||
|
||||
### Рендеринг: `??? note` admonition (НЕ `<details>`!)
|
||||
|
||||
**Важно:** `<details>` и `<div markdown="1">` НЕ работают в mkdocs — Markdown внутри них не рендерится.
|
||||
|
||||
Вместо этого используется **нативный mkdocs admonition** `??? note`:
|
||||
|
||||
```markdown
|
||||
??? note "Справка (MAN)"
|
||||
|
||||
# Инструкция по развертыванию
|
||||
|
||||
---
|
||||
## 1. Общая информация
|
||||
Текст параграфа.
|
||||
|
||||
- **bold** — описание
|
||||
- `code` — пример
|
||||
```
|
||||
|
||||
**Критические требования:**
|
||||
1. Пустая строка после `??? note "..."` — обязательно
|
||||
2. Все строки контента с отступом ровно 4 пробела — включая пустые
|
||||
3. `pymdownx.details` в `markdown_extensions` (уже есть)
|
||||
|
||||
Результат: `<details class="note"><summary>Справка (MAN)</summary><h1>...</h1><hr/><h2>...</h2>...</details>`
|
||||
|
||||
## Принципы дизайна (CSS)
|
||||
|
||||
- `max-width: 1800px` — лёгкое ограничение (на 2560px поля ~380px)
|
||||
- 🔵 Синий — переменные верхнего уровня (структуры)
|
||||
- 🟢 Зелёный — поля внутри структур (sub_params)
|
||||
- ID — мелкий, серый (техническая информация)
|
||||
- Default — заметный (юзеру важно что будет если не указать)
|
||||
- Description/Constraints — мелкий серый (доп. информация)
|
||||
- Без переносов в коде, description — с переносами
|
||||
|
||||
## Сервисы с подробным MAN (для LLM-обработки)
|
||||
|
||||
| Сервис | MAN (символов) |
|
||||
|---|---|
|
||||
| rabbitmq | 9811 |
|
||||
| mongodb | 9296 |
|
||||
| postgres | 8252 |
|
||||
| gitea | 6639 |
|
||||
| clickhouse | 6010 |
|
||||
| flask | 5569 |
|
||||
| kafka | 5210 |
|
||||
| vapp | 4659 |
|
||||
| akhq | 3838 |
|
||||
|
||||
## LLM-промпт (на один сервис)
|
||||
|
||||
LLM получает ВСЕ .md файлы сервиса + ключевые поля из YAML + MAN и переписывает их.
|
||||
|
||||
### Формат входа
|
||||
```
|
||||
Сервис: <service_name>
|
||||
YAML (ключевое): параметры, типы, defaults, constraints
|
||||
MAN: <service_man>
|
||||
Файлы:
|
||||
=== Name.md ===
|
||||
<содержимое>
|
||||
...
|
||||
```
|
||||
|
||||
### Формат выхода
|
||||
```json
|
||||
{
|
||||
"Name.md": "полный текст",
|
||||
"Name_example.md": "полный текст",
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
### Правила для LLM
|
||||
1. **YAML = истина. MAN = контекст.** Если противоречат → верить YAML.
|
||||
2. HTML-таблицы: менять ТОЛЬКО текст внутри `<td>`, НЕ трогать структуру тегов.
|
||||
3. HCL-блоки: НЕ трогать.
|
||||
4. Navigation-строки: НЕ трогать.
|
||||
5. Имена ресурсов (nubes_*): НЕ менять.
|
||||
6. MAN-секцию: перевести из HTML в читаемый Markdown.
|
||||
7. Описания: сделать грамотными, логичными, на русском. Добавить контекст из MAN где уместно.
|
||||
8. **Юзер в первую очередь смотрит на: имя переменной, required/default, что делает параметр.** Это должно быть максимально понятно.
|
||||
|
||||
## Сборка mkdocs: слияние статического и динамического nav
|
||||
|
||||
mkdocs-material не имеет встроенного `!include` для `nav:`. Решение (рекомендовано): расширить Python pre-build шаг в `04_build_and_publish_docs.sh`:
|
||||
|
||||
1. `WriteNavFragment()` генерирует `_nav_fragment.yml` с `resources_nav:` (категории + ресурсы)
|
||||
2. Python-блок читает `_nav_fragment.yml`, парсит `mkdocs.yml`, вставляет ресурсы в секцию `Ресурсы` внутри `nav:`
|
||||
3. Одновременно копирует `30_registry/` в `docs_dir` (чтобы guides не ломались при смене `docs_dir`)
|
||||
4. Результат пишется в `.mkdocs.tmp.yml` → `mkdocs build -f .mkdocs.tmp.yml`
|
||||
|
||||
Альтернативы (отвергнуты):
|
||||
- docs-generator пишет полный mkdocs.yml (слишком хрупко)
|
||||
- mkdocs-awesome-pages (не решает проблему merge static+dynamic)
|
||||
|
||||
## Аудит соответствия YAML ↔ Доки (2026-08-10)
|
||||
|
||||
**Метод:** сравнение всех 37 YAML-спеков со сгенерированными `_params_create.md` и `_params_modify.md`.
|
||||
|
||||
### Итоги
|
||||
|
||||
| Метрика | YAML | Доки | Статус |
|
||||
|---------|------|------|--------|
|
||||
| Сервисов | 37 | 37 | ✅ |
|
||||
| CREATE params (top-level) | 204 | 204 | ✅ 1:1 |
|
||||
| MODIFY params (top-level) | 102 | 101 | ⚠️ -1 |
|
||||
| Sub-params (nested) | — | 199 | ✅ развёрнуты |
|
||||
|
||||
### Расхождения
|
||||
|
||||
| # | Сервис | Проблема | Причина | Действие |
|
||||
|---|--------|----------|---------|----------|
|
||||
| 1 | vc_vm_v2 | Нет страниц | Закомментирован в `services_list.txt` (`# нет в TEST UI`) | Не баг |
|
||||
| 2 | s3, s3bucket, dummy, vc_nsxt, vcexternalip, vc_vm_v3 | 8 пустых типов | Поле `type` не заполнено в YAML | Косметика |
|
||||
|
||||
### Вывод
|
||||
|
||||
Все параметры из YAML **полностью** присутствуют в документации. CamelCase-имена корректно конвертируются в snake_case через `ToSnake()`. Единственный «missing» сервис (vc_vm_v2) исключён из генерации намеренно. 8 пустых типов — пробелы в исходных YAML-спеках, не влияют на корректность.
|
||||
@@ -0,0 +1,638 @@
|
||||
# План миграции в Nubes Managed Kubernetes — Инструкции для агента
|
||||
|
||||
**Создан:** 2026-03-13 (Opus 4.6)
|
||||
**Исполнитель:** Sonnet 4.6
|
||||
**Статус:** Ожидает исполнения
|
||||
|
||||
> **ВАЖНО:** Этот документ — пошаговый план. Каждый пункт содержит:
|
||||
> - ЧТО делать (цель)
|
||||
> - ГДЕ делать (файлы)
|
||||
> - КАК делать (конкретные инструкции)
|
||||
> - ОГРАНИЧЕНИЯ (что нельзя трогать)
|
||||
|
||||
---
|
||||
|
||||
## Контекст
|
||||
|
||||
Terraform-провайдер Nubes Cloud сейчас работает на self-managed K8s (registry.kube5s.ru <!-- ⛔ LEGACY: registry.kube5s.ru ЗАКРЫТ. Актуальный хост: tf-registry.containerk8s.services.ngcloud.ru --> <!-- ⛔ LEGACY: registry.kube5s.ru <!-- ⛔ LEGACY: registry.kube5s.ru ЗАКРЫТ. Актуальный хост: tf-registry.containerk8s.services.ngcloud.ru --> ЗАКРЫТ. Актуальный хост: tf-registry.containerk8s.services.ngcloud.ru -->).
|
||||
Цель: подготовить инфраструктуру к передаче в Nubes Managed Kubernetes под управление их DevOps.
|
||||
|
||||
Nubes (nubes.ru) — российский cloud-провайдер, собственный DC Tier III (Москва), имеет Managed K8s, Harbor, S3.
|
||||
|
||||
**Репозиторий:** `/home/naeel/remote_dev/terraform`
|
||||
|
||||
**Обязательно прочитать перед работой:**
|
||||
- `REPO_CONTENTS.md` — карта репозитория
|
||||
- `.github/copilot-instructions.md` — правила работы (IMMUTABILITY POLICY)
|
||||
- `docs/CODEBASE_ANALYSIS_AND_ROADMAP.md` — анализ кодовой базы
|
||||
|
||||
---
|
||||
|
||||
## Группа A: Подготовительные задачи (делать ПЕРВЫМИ)
|
||||
|
||||
### A1. Helm Chart для всех K8s-манифестов
|
||||
|
||||
**Цель:** Конвертировать raw YAML манифесты в Helm chart.
|
||||
|
||||
**Исходные файлы (ТОЛЬКО ЧИТАТЬ, НЕ МЕНЯТЬ):**
|
||||
- `k8s/registry-deployment-new.yaml`
|
||||
- `k8s/registry-ingress-new.yaml`
|
||||
- `operator/manifests/00-namespace.yaml`
|
||||
- `operator/manifests/00-rbac.yaml`
|
||||
- `operator/manifests/02-crd.yaml`
|
||||
- `operator/manifests/03-build-script.yaml`
|
||||
- `operator/manifests/04-operator-deployment.yaml`
|
||||
- `operator/manifests/05-registry-server.yaml`
|
||||
- `operator/manifests/06-registry-server-docs.yaml`
|
||||
|
||||
**Создать:**
|
||||
```
|
||||
charts/
|
||||
terraform-registry/
|
||||
Chart.yaml
|
||||
values.yaml
|
||||
values-dev.yaml
|
||||
values-prod.yaml
|
||||
templates/
|
||||
_helpers.tpl
|
||||
namespace.yaml
|
||||
rbac.yaml
|
||||
crd.yaml
|
||||
build-script-configmap.yaml
|
||||
operator-deployment.yaml
|
||||
registry-server-deployment.yaml
|
||||
registry-server-service.yaml
|
||||
docs-server-deployment.yaml (optional)
|
||||
ingress.yaml
|
||||
pdb.yaml
|
||||
networkpolicy.yaml
|
||||
```
|
||||
|
||||
**Требования к `values.yaml`:**
|
||||
```yaml
|
||||
global:
|
||||
registryHostname: "registry.kube5s.ru <!-- ⛔ LEGACY: registry.kube5s.ru ЗАКРЫТ. Актуальный хост: tf-registry.containerk8s.services.ngcloud.ru -->" # Переопределяется при миграции
|
||||
namespace: "terraform-registry"
|
||||
|
||||
registry:
|
||||
image:
|
||||
repository: "naeel/terraform-registry-server" # → Harbor при миграции
|
||||
tag: "latest"
|
||||
pullPolicy: IfNotPresent
|
||||
replicas: 1 # → 2 в prod
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 128Mi
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 256Mi
|
||||
service:
|
||||
port: 80
|
||||
targetPort: 8080
|
||||
healthcheck:
|
||||
enabled: true
|
||||
path: /healthz
|
||||
port: 8080
|
||||
|
||||
operator:
|
||||
image:
|
||||
repository: "naeel/terraform-registry-operator"
|
||||
tag: "latest"
|
||||
replicas: 1
|
||||
resources:
|
||||
requests:
|
||||
cpu: 50m
|
||||
memory: 64Mi
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 128Mi
|
||||
|
||||
ingress:
|
||||
enabled: true
|
||||
className: "nginx"
|
||||
host: "registry.kube5s.ru <!-- ⛔ LEGACY: registry.kube5s.ru ЗАКРЫТ. Актуальный хост: tf-registry.containerk8s.services.ngcloud.ru -->" # Переопределяется
|
||||
tls:
|
||||
enabled: true
|
||||
issuer: "letsencrypt-prod" # НЕ ТРОГАТЬ LetsEncrypt issuer!
|
||||
secretName: "registry-tls"
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/proxy-body-size: "100m"
|
||||
|
||||
s3:
|
||||
endpoint: "s3.msk-1.ngcloud.ru"
|
||||
bucket: "terraform-registry"
|
||||
useSSL: true
|
||||
# credentials через existingSecret
|
||||
existingSecret: "s3-credentials"
|
||||
accessKeyField: "access-key"
|
||||
secretKeyField: "secret-key"
|
||||
|
||||
pdb:
|
||||
enabled: false # → true в prod
|
||||
minAvailable: 1
|
||||
|
||||
networkPolicy:
|
||||
enabled: false # → true при миграции
|
||||
```
|
||||
|
||||
**Требования к `values-dev.yaml`:**
|
||||
```yaml
|
||||
global:
|
||||
registryHostname: "registry.kube5s.ru <!-- ⛔ LEGACY: registry.kube5s.ru ЗАКРЫТ. Актуальный хост: tf-registry.containerk8s.services.ngcloud.ru -->"
|
||||
registry:
|
||||
replicas: 1
|
||||
pdb:
|
||||
enabled: false
|
||||
```
|
||||
|
||||
**Требования к `values-prod.yaml`:**
|
||||
```yaml
|
||||
global:
|
||||
registryHostname: "registry.nubes.ru" # Целевой домен
|
||||
registry:
|
||||
image:
|
||||
repository: "pearlharbor.registryk8s.services.ngcloud.ru/terraform/registry-server"
|
||||
replicas: 2
|
||||
operator:
|
||||
image:
|
||||
repository: "pearlharbor.registryk8s.services.ngcloud.ru/terraform/registry-operator"
|
||||
pdb:
|
||||
enabled: true
|
||||
minAvailable: 1
|
||||
networkPolicy:
|
||||
enabled: true
|
||||
```
|
||||
|
||||
**Ограничения:**
|
||||
- НЕ менять исходные YAML в `k8s/` и `operator/manifests/` (они могут ещё использоваться)
|
||||
- НЕ трогать CRD-ресурсы с LetsEncrypt issuerRef
|
||||
- НЕ запускать `helm install/upgrade` — только создать файлы
|
||||
- Helm chart — НОВЫЕ файлы в `charts/` (APPEND ONLY)
|
||||
|
||||
---
|
||||
|
||||
### A2. Externalize hardcoded values
|
||||
|
||||
**Цель:** Убрать все hardcoded пути и домены, заменить на env vars.
|
||||
|
||||
**Файл 1: `internal/core/client.go` строка ~18**
|
||||
```go
|
||||
// СЕЙЧАС:
|
||||
f, err := os.OpenFile("/home/naeel/terra/debug_nubes.log", ...)
|
||||
|
||||
// НОВЫЙ КОД (добавить новую функцию в КОНЕЦ файла):
|
||||
func debugLogPath() string {
|
||||
if p := os.Getenv("NUBES_DEBUG_LOG"); p != "" {
|
||||
return p
|
||||
}
|
||||
return filepath.Join(os.TempDir(), "nubes_debug.log")
|
||||
}
|
||||
```
|
||||
|
||||
**Ограничение:** НЕ менять строку 18 напрямую. Добавить функцию `debugLogPath()` в КОНЕЦ файла. Спросить оператора перед заменой вызова.
|
||||
|
||||
**Файл 2: `internal/provider/provider.go` строка ~103**
|
||||
```go
|
||||
// СЕЙЧАС:
|
||||
InsecureSkipVerify: true,
|
||||
|
||||
// НУЖНО: Сделать конфигурируемым через provider schema + env var
|
||||
```
|
||||
|
||||
**Инструкция:**
|
||||
1. Добавить атрибут `insecure` в schema провайдера (Optional, bool, default false)
|
||||
2. Добавить чтение env var `NUBES_INSECURE`
|
||||
3. InsecureSkipVerify = config_value || env_value || false
|
||||
4. Код добавлять В КОНЕЦ секции Configure(), не рефакторить существующий
|
||||
|
||||
**Файл 3: `universal_rebuild/internal/provider/provider.go`**
|
||||
- Проверить аналогичную проблему с InsecureSkipVerify
|
||||
- Применить тот же паттерн
|
||||
|
||||
**Ограничения:**
|
||||
- НЕ менять сигнатуры существующих функций
|
||||
- Новый код — APPEND ONLY
|
||||
- InsecureSkipVerify=false по умолчанию (breaking change для текущих юзеров — СПРОСИТЬ оператора)
|
||||
|
||||
---
|
||||
|
||||
### A3. Health endpoints для registry-server
|
||||
|
||||
**Цель:** Добавить `/healthz`, `/readyz`, `/metrics` endpoints.
|
||||
|
||||
**Файл:** `registry-server-build/main.go`
|
||||
|
||||
**Инструкция:**
|
||||
1. Прочитать текущий `main.go` полностью
|
||||
2. Добавить в КОНЕЦ файла (новые handler-функции):
|
||||
```go
|
||||
func healthzHandler(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte("ok"))
|
||||
}
|
||||
|
||||
func readyzHandler(w http.ResponseWriter, r *http.Request) {
|
||||
// Проверить доступность S3
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte("ok"))
|
||||
}
|
||||
```
|
||||
3. Зарегистрировать handlers в main() — СПРОСИТЬ оператора перед добавлением в mux
|
||||
|
||||
**Ограничения:**
|
||||
- НЕ менять существующие handlers
|
||||
- НЕ запускать docker build
|
||||
- Новые функции — APPEND ONLY
|
||||
|
||||
---
|
||||
|
||||
### A4. Operaционная документация
|
||||
|
||||
**Цель:** Создать набор операционных документов для DevOps Nubes.
|
||||
|
||||
**Создать файлы:**
|
||||
|
||||
**`docs/ops/RUNBOOK.md`:**
|
||||
```markdown
|
||||
# Runbook: Terraform Provider Registry
|
||||
|
||||
## Предпосылки
|
||||
- Kubernetes cluster ≥ 1.27
|
||||
- Helm ≥ 3.12
|
||||
- Доступ к S3 (s3.msk-1.ngcloud.ru)
|
||||
- Harbor registry (для образов)
|
||||
|
||||
## Установка
|
||||
helm install terraform-registry ./charts/terraform-registry \
|
||||
-f charts/terraform-registry/values-prod.yaml \
|
||||
-n terraform-registry --create-namespace
|
||||
|
||||
## Обновление версии
|
||||
1. Собрать новый образ (CI pipeline)
|
||||
2. Обновить tag в values
|
||||
3. helm upgrade terraform-registry ./charts/terraform-registry -f values-prod.yaml
|
||||
|
||||
## Проверка здоровья
|
||||
kubectl -n terraform-registry get pods
|
||||
curl https://<REGISTRY_HOST>/healthz
|
||||
curl https://<REGISTRY_HOST>/.well-known/terraform.json
|
||||
|
||||
## Компоненты
|
||||
- Registry Server — HTTP-сервер протокола Terraform Registry
|
||||
- Operator — K8s controller для сборки provider binaries
|
||||
- S3 — хранилище артефактов (бинарники + документация)
|
||||
```
|
||||
|
||||
**`docs/ops/TROUBLESHOOTING.md`:**
|
||||
```markdown
|
||||
# Troubleshooting
|
||||
|
||||
## Registry Server не отвечает
|
||||
1. kubectl -n terraform-registry get pods -l app=registry-server
|
||||
2. kubectl -n terraform-registry logs -l app=registry-server --tail=100
|
||||
3. Проверить ingress: kubectl get ingress -n terraform-registry
|
||||
4. Проверить S3: curl -s https://s3.msk-1.ngcloud.ru (bucket access)
|
||||
|
||||
## Provider binary не скачивается
|
||||
1. Проверить наличие в S3: s3cmd ls s3://terraform-registry/terraform-providers/...
|
||||
2. Проверить SHA256SUMS сигнатуру
|
||||
3. Проверить GPG ключ
|
||||
|
||||
## Operator не создаёт build job
|
||||
1. kubectl -n terraform-registry get terraformproviderrelease
|
||||
2. kubectl -n terraform-registry describe terraformproviderrelease <name>
|
||||
3. kubectl -n terraform-registry get jobs
|
||||
4. Проверить RBAC: operator ServiceAccount должен иметь права на jobs и secrets
|
||||
|
||||
## TLS / Certificate проблемы
|
||||
- Проверить cert-manager: kubectl get certificates -n terraform-registry
|
||||
- ⚠️ НЕ пересоздавать certificates с LetsEncrypt issuer (rate limits!)
|
||||
- Для отладки использовать self-signed issuer
|
||||
```
|
||||
|
||||
**`docs/ops/MONITORING.md`:**
|
||||
```markdown
|
||||
# Мониторинг
|
||||
|
||||
## Ключевые метрики
|
||||
- registry_http_requests_total — кол-во запросов к registry
|
||||
- registry_http_request_duration_seconds — latency
|
||||
- registry_s3_operations_total — операции с S3
|
||||
- registry_s3_errors_total — ошибки S3
|
||||
|
||||
## Алерты (Prometheus)
|
||||
- RegistryDown: up == 0 (>2 min)
|
||||
- RegistryHighLatency: p99 > 5s (>5 min)
|
||||
- RegistryS3Errors: rate > 0.1/s (>5 min)
|
||||
- RegistryPodRestart: увеличение restart count
|
||||
|
||||
## Grafana Dashboard
|
||||
- Import dashboard ID: (создать при установке мониторинга)
|
||||
```
|
||||
|
||||
**`docs/ops/UPGRADE.md`:**
|
||||
```markdown
|
||||
# Процедура обновления
|
||||
|
||||
## Provider version update (без downtime)
|
||||
1. CI собирает новый provider binary
|
||||
2. Создать TerraformProviderRelease CR с новой версией
|
||||
3. Operator создаёт build job → артефакты в S3
|
||||
4. Старые версии остаются доступны (immutable artifacts)
|
||||
|
||||
## Registry Server update (rolling)
|
||||
1. Обновить image tag в Helm values
|
||||
2. helm upgrade --set registry.image.tag=<new> terraform-registry ./charts/...
|
||||
3. Проверить: kubectl rollout status deployment/registry-server -n terraform-registry
|
||||
4. Rollback: helm rollback terraform-registry 1
|
||||
|
||||
## Operator update
|
||||
1. Обновить operator image tag
|
||||
2. helm upgrade ...
|
||||
3. Проверить CRD compatibility: kubectl get crd terraformproviderreleases.terra.core.nubes.ru
|
||||
```
|
||||
|
||||
**`docs/ops/ROLLBACK.md`:**
|
||||
```markdown
|
||||
# Процедура отката
|
||||
|
||||
## Helm rollback
|
||||
helm rollback terraform-registry <revision>
|
||||
helm history terraform-registry -n terraform-registry
|
||||
|
||||
## Emergency: Direct image rollback
|
||||
kubectl -n terraform-registry set image deployment/registry-server \
|
||||
registry-server=<HARBOR>/terraform/registry-server:<PREV_TAG>
|
||||
|
||||
## S3 artifacts (immutable — откат не нужен)
|
||||
Все версии provider binary хранятся бессрочно.
|
||||
Удаление только вручную через s3cmd.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Группа B: Инфраструктурная подготовка
|
||||
|
||||
### B1. Dockerfile оптимизация
|
||||
|
||||
**Цель:** Убедиться что Dockerfile для registry-server и operator готовы к Harbor.
|
||||
|
||||
**Инструкция:**
|
||||
1. Прочитать `registry-server-build/` и `operator/build/`
|
||||
2. Проверить что Dockerfile использует multi-stage build
|
||||
3. Проверить что нет hardcoded путей
|
||||
4. Убедиться что base image — official (golang:1.24 + alpine/scratch)
|
||||
5. НЕ запускать docker build — только проверить файлы
|
||||
|
||||
**Создать (если отсутствует):** `registry-server-build/.dockerignore`, `operator/.dockerignore`
|
||||
|
||||
---
|
||||
|
||||
### B2. CI Pipeline definition
|
||||
|
||||
**Цель:** Создать файл CI pipeline (GitLab CI / Tekton) для автосборки.
|
||||
|
||||
**Создать:** `devops/ci/pipeline.yaml`
|
||||
|
||||
```yaml
|
||||
# GitLab CI - пример (адаптировать под конкретный CI Nubes)
|
||||
stages:
|
||||
- test
|
||||
- build
|
||||
- sign
|
||||
- publish
|
||||
|
||||
variables:
|
||||
HARBOR_HOST: "pearlharbor.registryk8s.services.ngcloud.ru"
|
||||
S3_BUCKET: "terraform-registry"
|
||||
PROVIDER_NAME: "nubes"
|
||||
PROVIDER_NAMESPACE: "nubes"
|
||||
|
||||
test:
|
||||
stage: test
|
||||
image: golang:1.24
|
||||
script:
|
||||
- cd universal_rebuild
|
||||
- go test ./...
|
||||
- go vet ./...
|
||||
|
||||
build-provider:
|
||||
stage: build
|
||||
image: golang:1.24
|
||||
script:
|
||||
- cd universal_rebuild
|
||||
- GOOS=linux GOARCH=amd64 go build -o bin/terraform-provider-${PROVIDER_NAME}_linux_amd64
|
||||
- GOOS=darwin GOARCH=amd64 go build -o bin/terraform-provider-${PROVIDER_NAME}_darwin_amd64
|
||||
- GOOS=windows GOARCH=amd64 go build -o bin/terraform-provider-${PROVIDER_NAME}_windows_amd64.exe
|
||||
artifacts:
|
||||
paths: [universal_rebuild/bin/]
|
||||
|
||||
build-images:
|
||||
stage: build
|
||||
script:
|
||||
- docker build -t ${HARBOR_HOST}/terraform/registry-server:${CI_COMMIT_TAG} registry-server-build/
|
||||
- docker build -t ${HARBOR_HOST}/terraform/registry-operator:${CI_COMMIT_TAG} operator/
|
||||
- docker push ${HARBOR_HOST}/terraform/registry-server:${CI_COMMIT_TAG}
|
||||
- docker push ${HARBOR_HOST}/terraform/registry-operator:${CI_COMMIT_TAG}
|
||||
|
||||
sign:
|
||||
stage: sign
|
||||
script:
|
||||
- cd universal_rebuild/bin
|
||||
- sha256sum terraform-provider-* > SHA256SUMS
|
||||
- gpg --import $GPG_PRIVATE_KEY
|
||||
- gpg --detach-sign SHA256SUMS
|
||||
|
||||
publish-to-s3:
|
||||
stage: publish
|
||||
script:
|
||||
- s3cmd put bin/* s3://${S3_BUCKET}/terraform-providers/...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### B3. NetworkPolicy template
|
||||
|
||||
**Цель:** Подготовить NetworkPolicy для изоляции namespace.
|
||||
|
||||
**Включить в Helm chart:** `charts/terraform-registry/templates/networkpolicy.yaml`
|
||||
|
||||
```yaml
|
||||
{{- if .Values.networkPolicy.enabled }}
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: {{ include "terraform-registry.fullname" . }}-netpol
|
||||
namespace: {{ .Values.global.namespace }}
|
||||
spec:
|
||||
podSelector: {}
|
||||
policyTypes:
|
||||
- Ingress
|
||||
- Egress
|
||||
ingress:
|
||||
- from:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
kubernetes.io/metadata.name: ingress-nginx
|
||||
ports:
|
||||
- port: {{ .Values.registry.service.targetPort }}
|
||||
protocol: TCP
|
||||
egress:
|
||||
- to: []
|
||||
ports:
|
||||
- port: 443 # S3, Vault
|
||||
protocol: TCP
|
||||
- port: 53 # DNS
|
||||
protocol: UDP
|
||||
- port: 53
|
||||
protocol: TCP
|
||||
{{- end }}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Группа C: Domain Migration Plan
|
||||
|
||||
### C1. Domain migration (порядок действий)
|
||||
|
||||
**Это НЕ код — это инструкция для DevOps. Записать в `docs/ops/DOMAIN_MIGRATION.md`:**
|
||||
|
||||
```markdown
|
||||
# Миграция домена registry.kube5s.ru <!-- ⛔ LEGACY: registry.kube5s.ru ЗАКРЫТ. Актуальный хост: tf-registry.containerk8s.services.ngcloud.ru --> → registry.nubes.ru
|
||||
|
||||
## Фаза 1: Dual-domain (параллельная работа)
|
||||
1. Настроить Ingress с двумя hosts: registry.kube5s.ru <!-- ⛔ LEGACY: registry.kube5s.ru ЗАКРЫТ. Актуальный хост: tf-registry.containerk8s.services.ngcloud.ru --> + registry.nubes.ru
|
||||
2. Оба домена указывают на один Registry Server
|
||||
3. Обновить provider main.go: Address → registry.nubes.ru
|
||||
4. Старый адрес registry.kube5s.ru <!-- ⛔ LEGACY: registry.kube5s.ru ЗАКРЫТ. Актуальный хост: tf-registry.containerk8s.services.ngcloud.ru --> продолжает работать
|
||||
|
||||
## Фаза 2: Миграция клиентов
|
||||
1. Документировать новый registry address для пользователей
|
||||
2. .terraformrc mirror config для переходного периода:
|
||||
provider_installation {
|
||||
direct {
|
||||
exclude = ["registry.kube5s.ru <!-- ⛔ LEGACY: registry.kube5s.ru ЗАКРЫТ. Актуальный хост: tf-registry.containerk8s.services.ngcloud.ru -->/*/*"]
|
||||
}
|
||||
network_mirror {
|
||||
url = "https://registry.nubes.ru/v1/providers/"
|
||||
}
|
||||
}
|
||||
|
||||
## Фаза 3: Редирект
|
||||
1. registry.kube5s.ru <!-- ⛔ LEGACY: registry.kube5s.ru ЗАКРЫТ. Актуальный хост: tf-registry.containerk8s.services.ngcloud.ru --> Ingress → 301 redirect на registry.nubes.ru
|
||||
2. Мониторинг: отслеживать запросы на старый домен
|
||||
|
||||
## Фаза 4: Деком (через 6+ месяцев)
|
||||
1. Убрать registry.kube5s.ru <!-- ⛔ LEGACY: registry.kube5s.ru ЗАКРЫТ. Актуальный хост: tf-registry.containerk8s.services.ngcloud.ru --> из Ingress
|
||||
2. DNS → удалить A/CNAME запись
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Группа D: Code Quality (из CODEBASE_ANALYSIS_AND_ROADMAP.md)
|
||||
|
||||
### D1. Deprecated-маркеры для legacy CreateGenericInstance
|
||||
|
||||
**Файл:** `internal/core/client.go`
|
||||
|
||||
**Инструкция:** Добавить КОММЕНТАРИИ (не код) перед каждым методом V1-V5:
|
||||
```go
|
||||
// Deprecated: Use CreateGenericInstanceUniversalV5 instead.
|
||||
// This method is kept for backward compatibility and will be removed in v3.0.
|
||||
func (c *UniversalClient) CreateGenericInstance(...) ...
|
||||
```
|
||||
|
||||
**Методы для пометки:**
|
||||
- `CreateGenericInstance` (V1)
|
||||
- `CreateGenericInstanceUniversal` (V2)
|
||||
- `CreateGenericInstanceUniversalV2` (V3)
|
||||
- `CreateGenericInstanceUniversalV3` (V4)
|
||||
- `CreateGenericInstanceUniversalV4` (V5)
|
||||
|
||||
**Ограничения:** ТОЛЬКО комментарии. НЕ менять код методов. НЕ удалять.
|
||||
|
||||
---
|
||||
|
||||
### D2. .gitignore для secrets
|
||||
|
||||
**Файл:** `.gitignore` (корень репозитория)
|
||||
|
||||
**Добавить в КОНЕЦ файла:**
|
||||
```gitignore
|
||||
# Secrets (should be in Vault, not in git)
|
||||
secrets/*.asc
|
||||
secrets/*.token
|
||||
secrets/*.key
|
||||
!secrets/.gitkeep
|
||||
```
|
||||
|
||||
**Создать:** `secrets/.gitkeep` (пустой файл, чтобы директория осталась в git)
|
||||
|
||||
---
|
||||
|
||||
### D3. Unit tests для core layer
|
||||
|
||||
**Цель:** Создать минимальный набор тестов.
|
||||
|
||||
**Создать файлы:**
|
||||
- `universal_rebuild/internal/core/client_test.go`
|
||||
- `universal_rebuild/internal/resources_core/crud_test.go`
|
||||
|
||||
**Минимальные тесты для `client_test.go`:**
|
||||
- `TestNormalizeValue_EmptyString`
|
||||
- `TestNormalizeValue_NullString`
|
||||
- `TestNormalizeValue_MapType`
|
||||
- `TestNormalizeValue_ArrayType`
|
||||
- `TestNormalizeValue_TrimSpace`
|
||||
|
||||
**Минимальные тесты для `crud_test.go`:**
|
||||
- `TestIsStatusSuspended`
|
||||
- `TestIsStatusNonAdoptable`
|
||||
- `TestDeleteBehaviorDefault`
|
||||
|
||||
**Ограничения:**
|
||||
- Тесты — НОВЫЕ файлы (не менять существующие)
|
||||
- Использовать стандартный `testing` пакет Go
|
||||
- НЕ запускать тесты (`go test`) без разрешения оператора
|
||||
|
||||
---
|
||||
|
||||
## Порядок выполнения
|
||||
|
||||
```
|
||||
ФАЗА 0 (быстрые wins):
|
||||
D1 → Deprecated комментарии [5 мин]
|
||||
D2 → .gitignore для secrets [2 мин]
|
||||
A2 → debugLogPath() function [10 мин]
|
||||
|
||||
ФАЗА 1 (Helm chart):
|
||||
A1 → Полный Helm chart [30-60 мин]
|
||||
|
||||
ФАЗА 2 (Ops docs):
|
||||
A4 → RUNBOOK, TROUBLESHOOTING и др. [20 мин]
|
||||
C1 → DOMAIN_MIGRATION.md [10 мин]
|
||||
|
||||
ФАЗА 3 (Code quality):
|
||||
D3 → Unit tests [30 мин]
|
||||
A3 → Health endpoints [15 мин]
|
||||
|
||||
ФАЗА 4 (CI/CD):
|
||||
B2 → CI pipeline definition [15 мин]
|
||||
B1 → Dockerfile audit [10 мин]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Правила для агента (напоминание)
|
||||
|
||||
1. **IMMUTABILITY POLICY** — НЕ менять существующий рабочий код
|
||||
2. **APPEND ONLY** — новый код только в конец файла
|
||||
3. **Комментарии — ЭТО НЕ ПРАВКА КОДА**, их можно и нужно добавлять
|
||||
4. **НЕ запускать** docker build, kubectl apply, helm install
|
||||
5. **НЕ трогать** ресурсы с LetsEncrypt issuerRef
|
||||
6. **Спрашивать разрешения** перед изменением сигнатур функций
|
||||
7. **Secrets в коде** — КАТЕГОРИЧЕСКИ нет
|
||||
8. **Перед любой работой** — прочитать `REPO_CONTENTS.md`
|
||||
9. **Файлы в `internal/resources_gen/`** — НЕ МЕНЯТЬ ВРУЧНУЮ (только через генератор)
|
||||
10. **Минимальные ресурсы** — при создании K8s ресурсов ставить минимальный CPU/memory
|
||||
@@ -0,0 +1,268 @@
|
||||
<!-- ⛔⛔⛔ LEGACY: deck-api.ngcloud.ru ЗАКРЫВАЕТСЯ! Все примеры ниже — ИСТОРИЧЕСКИЕ. -->
|
||||
<!-- Актуальный API: https://lk-api-gateway.ngcloud.ru/api/v1/svc -->
|
||||
# How It Was Done — Developer Guide (закрытая страница)
|
||||
|
||||
**Filename & Versioning:** howitwasdone.md / 2026‑02‑04 / Draft v1
|
||||
|
||||
Этот документ — единый технический мануал. Он доступен только по прямой ссылке и не включён в публичную навигацию.
|
||||
|
||||
## Оглавление
|
||||
1. Архитектура и методы (CRUD)
|
||||
2. База знаний ошибок
|
||||
3. Глоссарий (со ссылками на места употребления)
|
||||
4. Билд и публикация провайдера и документации
|
||||
|
||||
---
|
||||
|
||||
## 1. Архитектура и методы (CRUD)
|
||||
|
||||
### 1.1 Архитектура универсального rebuild
|
||||
- Ядро: universal_rebuild/internal/core (универсальный клиент API и общий flow операций).
|
||||
- Провайдер: universal_rebuild/internal/provider (schema, конфигурация, подключение ресурсов).
|
||||
- YAML-спеки: universal_rebuild/resources_yaml (источник истины).
|
||||
- Генератор: universal_rebuild/tools/gen (генерация Go-ресурсов и registry).
|
||||
|
||||
Ключевое правило: новая логика — только новые функции/файлы. Существующий Go‑код не менять без согласования.
|
||||
|
||||
<a id="discovery"></a>
|
||||
### 1.2 Источник параметров (discovery)
|
||||
Параметры извлекаются через proxy endpoint API:
|
||||
- /index.cfm?endpoint=/services/{svcId}
|
||||
- /index.cfm?endpoint=/serviceOperation/{svcOperationId}
|
||||
- (опц.) /index.cfm?endpoint=/param-value-list/{svcOperationCfsParamId}
|
||||
|
||||
Схема: сервис → операции → CFS параметры → YAML → генерация Go.
|
||||
|
||||
<a id="create-universal"></a>
|
||||
### 1.3 Create (универсальный 7‑шаговый паттерн)
|
||||
1) POST /instances → instanceUid
|
||||
2) POST /instanceOperations (create) → instanceOperationUid
|
||||
3) GET /instanceOperations/{uid}?fields=cfsParams
|
||||
4) POST /instanceOperationCfsParams для каждого параметра
|
||||
5) GET /instanceOperations/{uid}/validate-cfs
|
||||
6) POST /instanceOperations/{uid}/run с payload {}
|
||||
7) Polling до завершения операции
|
||||
|
||||
Критично: параметры отправляются все, включая дефолты.
|
||||
|
||||
<a id="read-adopt"></a>
|
||||
### 1.4 Read / Adopt
|
||||
- При isDeleted или explainedStatus=deleted — state очищается.
|
||||
- При совпадении display_name и resume_if_exists=true — adopt/resume.
|
||||
- Предупреждения показываются только на create и не мешают managed ресурсам.
|
||||
|
||||
<a id="update-modify"></a>
|
||||
### 1.5 Update / Modify
|
||||
- IDs параметров на modify отличаются от create.
|
||||
- Нельзя хардкодить ID: нужно получать manifest операции и строить маппинг.
|
||||
- Если modify отсутствует в availableOperations — выдаётся ясная ошибка.
|
||||
|
||||
<a id="polling-rules"></a>
|
||||
### 1.6 Polling (железные правила)
|
||||
- Завершение операции определяется только по dtFinish.
|
||||
- После dtFinish результат определяется isSuccessful.
|
||||
- Для VM применяется двойной контроль: статус операции + статус инстанса (ERROR/STOPPED).
|
||||
|
||||
### 1.7 Нормализация типов
|
||||
- map/json → "{}"
|
||||
- list/array → "[]"
|
||||
- Пустые строки в JSON‑параметрах запрещены (валидаторы на plan).
|
||||
|
||||
<a id="soft-delete"></a>
|
||||
### 1.8 Soft Delete
|
||||
- Для тяжёлых ресурсов delete заменяется на suspend (карантин/retention).
|
||||
- Повторный apply при suspend может выполнять resume.
|
||||
|
||||
---
|
||||
|
||||
## 2. База знаний ошибок
|
||||
|
||||
### A. Аутентификация и токены
|
||||
|
||||
**Токены лежат в `secrets/{dev,test,prod}.token`. Срок действия — до декабря 2026.**
|
||||
Проверить дату JWT:
|
||||
```bash
|
||||
python3 -c "import json,base64; t=open('secrets/test.token').read().split('.'); d=json.loads(base64.urlsafe_b64decode(t[1]+'==')); from datetime import datetime,timezone; print(datetime.fromtimestamp(d['exp'],tz=timezone.utc))"
|
||||
```
|
||||
|
||||
**Симптом:** 403 Forbidden (НЕ 401!)
|
||||
**Причина (старый API index.cfm):** DDoS-Guard блокирует — нет Referer или нет User-Agent.
|
||||
**Решение для curl (старый API):**
|
||||
```bash
|
||||
curl -s --max-time 10 \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36" \
|
||||
-H "Referer: https://deck-test.ngcloud.ru/" \
|
||||
"https://deck-api-test.ngcloud.ru/api/v1/index.cfm?endpoint=/services/90"
|
||||
```
|
||||
Referer должен совпадать со стендом (test/dev/prod). Новый Gateway (lk-api-gateway) требует только Authorization.
|
||||
|
||||
**Симптом:** 401 Unauthorized / Authorization header expected
|
||||
**Причина:** токен истёк или не передан в окружение
|
||||
**Решение:** обновить access_token и сохранить в файле HH‑MM‑SS.token; убедиться, что Terraform читает токен
|
||||
|
||||
### B. Поллинг зависает
|
||||
**Симптом:** apply «висит», операция не завершается
|
||||
**Причина:** ожидание по статусу инстанса/операции без dtFinish
|
||||
**Решение:** критерий завершения — dtFinish; далее isSuccessful
|
||||
|
||||
### C. Modify: Invalid CFS parameter (400)
|
||||
**Причина:** отправлены create IDs для modify
|
||||
**Решение:** получать manifest операции и динамически маппить IDs
|
||||
|
||||
### D. Modify недоступен
|
||||
**Симптом:** action modify not available
|
||||
**Причина:** modify нет в availableOperations
|
||||
**Решение:** проверять доступные операции и выдавать явную ошибку
|
||||
|
||||
### E. Map/JSON/List: Invalid format
|
||||
**Симптом:** 400 Invalid format map/array/json
|
||||
**Причина:** пустые строки вместо {} или []
|
||||
**Решение:** нормализация по типу, trim, отправка {} или []
|
||||
|
||||
### F. VM: зависание на FW / 500 String[]→GUID
|
||||
**Симптом:** зависание на стадии FW, 500 checkParam
|
||||
**Причина:** пустые/невалидные JSON‑массивы, ошибки backend
|
||||
**Решение:** валидация JSON массивов, дефолт ["0.0.0.0/0"], фильтрация пустых значений; при повторении — эскалация на поддержку
|
||||
|
||||
### G. VM modify: checkParam instanceOperationCfsParamUid
|
||||
**Симптом:** 500 Invalid call of function checkParam
|
||||
**Причина:** дублирование параметров при POST/PUT
|
||||
**Решение:** гибридный PUT/POST по наличию instanceOperationCfsParamUid; проверить формат эндпойнта
|
||||
|
||||
### H. Postgres: split() on null
|
||||
**Симптом:** Cannot invoke method split() on null object
|
||||
**Причина:** передан UUID конкретного S3‑бакета
|
||||
**Решение:** использовать UUID сервиса S3 (svcId 12), а не бакета
|
||||
|
||||
### I. GPG/Registry
|
||||
**Симптом:** authentication signature from unknown issuer
|
||||
**Причина:** ключ подписи не совпадает с ключом в registry
|
||||
**Решение:** обновить signing_keys на сервере или пересобрать/подменить бинарник
|
||||
|
||||
**Симптом:** openpgp invalid data
|
||||
**Причина:** ASCII‑armor для .sig
|
||||
**Решение:** бинарная detached подпись без --armor
|
||||
|
||||
### J. Registry/S3
|
||||
**Симптом:** presigned URL не работает через Ingress
|
||||
**Причина:** Host заголовок ломает подпись
|
||||
**Решение:** proxy‑режим download на сервере registry
|
||||
|
||||
### K. Документация: 404
|
||||
**Причина:** неверный S3‑ключ (hostname в префиксе)
|
||||
**Решение:** фиксированный префикс docs/<ns>/<name>/<version>/
|
||||
|
||||
### L. Версии Terraform Plugin Framework
|
||||
**Симптом:** ошибки сборки при апгрейде
|
||||
**Причина:** несовместимость framework и plugin‑go
|
||||
**Решение:** использовать совместимые версии
|
||||
|
||||
### M. TLS handshake timeout
|
||||
**Причина:** сетевые условия/VPN
|
||||
**Решение:** повторить apply
|
||||
|
||||
---
|
||||
|
||||
## 3. Глоссарий (с ссылками на места употребления)
|
||||
|
||||
Каждый термин содержит ссылки на разделы этого же документа, где он используется.
|
||||
|
||||
- **instance** — экземпляр сервиса в Nubes Cloud.
|
||||
- Ссылки: [Create](#create-universal)
|
||||
|
||||
- **instanceOperation** — асинхронная операция над instance (create/modify/delete/suspend/resume).
|
||||
- Ссылки: [Create](#create-universal), [Polling](#polling-rules)
|
||||
|
||||
- **cfsParams** — список параметров операции, получаемый через manifest операции.
|
||||
- Ссылки: [Create](#create-universal), [Discovery](#discovery)
|
||||
|
||||
- **svcOperationCfsParamId** — ID параметра операции, различается для create и modify.
|
||||
- Ссылки: [Update / Modify](#update-modify)
|
||||
|
||||
- **dtFinish** — единственный надёжный индикатор завершения операции.
|
||||
- Ссылки: [Polling](#polling-rules)
|
||||
|
||||
- **isSuccessful** — результат операции после dtFinish.
|
||||
- Ссылки: [Polling](#polling-rules)
|
||||
|
||||
- **resourceRealm** — окружение/realm; иногда обязателен и задаётся пользователем.
|
||||
- Ссылки: [Read / Adopt](#read-adopt)
|
||||
|
||||
- **display_name** — человекочитаемое имя; используется для adopt/resume.
|
||||
- Ссылки: [Read / Adopt](#read-adopt)
|
||||
|
||||
- **resume_if_exists** — включение adopt/resume по display_name.
|
||||
- Ссылки: [Read / Adopt](#read-adopt)
|
||||
|
||||
- **delete_mode** — режим удаления (delete/suspend/state_only).
|
||||
- Ссылки: [Soft Delete](#soft-delete)
|
||||
|
||||
- **suspend** — мягкое удаление (карантин/retention).
|
||||
- Ссылки: [Soft Delete](#soft-delete)
|
||||
|
||||
- **validate-cfs** — проверка параметров операции до run.
|
||||
- Ссылки: [Create](#create-universal)
|
||||
|
||||
- **state.out** — выходные данные API; при отсутствии задаются null.
|
||||
- Ссылки: [Read / Adopt](#read-adopt)
|
||||
|
||||
- **computed** — вычисляемые поля, должны быть известны после apply.
|
||||
- Ссылки: [Read / Adopt](#read-adopt)
|
||||
|
||||
- **registry protocol** — API Terraform Registry (discovery + versions + download).
|
||||
- Ссылки: [Публикация](#publish-registry)
|
||||
|
||||
- **signing_keys** — публичные GPG ключи, выдаваемые registry.
|
||||
- Ссылки: [Публикация](#publish-registry)
|
||||
|
||||
- **NLI** — Natural Language Infrastructure (AI‑парсинг инструкций).
|
||||
- Ссылки: [AI‑интеграция](#ai-nli)
|
||||
|
||||
---
|
||||
|
||||
## 4. Билд и публикация провайдера и документации
|
||||
|
||||
### 4.1 Сборка провайдера (universal_rebuild)
|
||||
Общий цикл:
|
||||
1) Генерация YAML параметров сервисов.
|
||||
2) Генерация Go‑ресурсов.
|
||||
3) Сборка бинарника go build.
|
||||
|
||||
Ключевые каталоги:
|
||||
- universal_rebuild/resources_yaml
|
||||
- universal_rebuild/internal/resources_gen
|
||||
- universal_rebuild/tools/gen
|
||||
|
||||
<a id="publish-registry"></a>
|
||||
### 4.2 Публикация провайдера в Registry
|
||||
- Артефакты: zip, SHA256SUMS, SHA256SUMS.sig
|
||||
- Подпись: для .sig использовать бинарную detached подпись
|
||||
- Хранилище: S3 bucket terraform‑registry
|
||||
- Префикс — по правилам registry‑сервера
|
||||
|
||||
### 4.3 Документация (MkDocs)
|
||||
- Сборка: Docker образ squidfunk/mkdocs-material
|
||||
- Результат: директория site/
|
||||
- Публикация: scripts/publish-docs.sh
|
||||
- Путь: docs/<namespace>/<name>/<version>/
|
||||
|
||||
### 4.4 Важные нюансы
|
||||
- При смене домена обновлять registry и ключи подписи.
|
||||
- Presigned URL через Ingress может ломаться — использовать proxy mode.
|
||||
- Технические папки исключаются из публикации.
|
||||
|
||||
### 4.5 Рекомендованный порядок работ
|
||||
1) Discovery параметров сервиса
|
||||
2) Генерация YAML
|
||||
3) Генерация Go‑ресурсов
|
||||
4) Build
|
||||
5) Тесты create/modify/suspend/resume
|
||||
6) Документация и публикация
|
||||
|
||||
<a id="ai-nli"></a>
|
||||
### 4.6 AI‑интеграция (NLI)
|
||||
Кратко: реализована для ресурса Tubulus через ModifyPlan + askGemini, с защитой от двойного вызова AI.
|
||||
|
||||
### 4.7 Прямая ссылка
|
||||
https://registry.kube5s.ru <!-- ⛔ LEGACY: registry.kube5s.ru ЗАКРЫТ. Актуальный хост: tf-registry.containerk8s.services.ngcloud.ru --> <!-- ⛔ LEGACY: registry.kube5s.ru <!-- ⛔ LEGACY: registry.kube5s.ru ЗАКРЫТ. Актуальный хост: tf-registry.containerk8s.services.ngcloud.ru --> ЗАКРЫТ. Актуальный хост: tf-registry.containerk8s.services.ngcloud.ru -->/docs/nubes/nubes/2.0.0/howitwasdone/
|
||||
Reference in New Issue
Block a user