# 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‑код не менять без согласования.
### 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.
### 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 до завершения операции
Критично: параметры отправляются все, включая дефолты.
### 1.4 Read / Adopt
- При isDeleted или explainedStatus=deleted — state очищается.
- При совпадении display_name и resume_if_exists=true — adopt/resume.
- Предупреждения показываются только на create и не мешают managed ресурсам.
### 1.5 Update / Modify
- IDs параметров на modify отличаются от create.
- Нельзя хардкодить ID: нужно получать manifest операции и строить маппинг.
- Если modify отсутствует в availableOperations — выдаётся ясная ошибка.
### 1.6 Polling (железные правила)
- Завершение операции определяется только по dtFinish.
- После dtFinish результат определяется isSuccessful.
- Для VM применяется двойной контроль: статус операции + статус инстанса (ERROR/STOPPED).
### 1.7 Нормализация типов
- map/json → "{}"
- list/array → "[]"
- Пустые строки в JSON‑параметрах запрещены (валидаторы на plan).
### 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////
### 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
### 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////
### 4.4 Важные нюансы
- При смене домена обновлять registry и ключи подписи.
- Presigned URL через Ingress может ломаться — использовать proxy mode.
- Технические папки исключаются из публикации.
### 4.5 Рекомендованный порядок работ
1) Discovery параметров сервиса
2) Генерация YAML
3) Генерация Go‑ресурсов
4) Build
5) Тесты create/modify/suspend/resume
6) Документация и публикация
### 4.6 AI‑интеграция (NLI)
Кратко: реализована для ресурса Tubulus через ModifyPlan + askGemini, с защитой от двойного вызова AI.
### 4.7 Прямая ссылка
https://terra.k8c.ru/docs/nubes/nubes/2.0.0/howitwasdone/