# 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://registry.kube5s.ru/docs/nubes/nubes/2.0.0/howitwasdone/