# tf_registry — Полная история (для нового чата) > **Дата**: 2026-08-06 > **Проект**: Переписываем Go-реестр Terraform-провайдеров на Python/Flask (managed-сервис Nubes) > **Репозиторий**: `https://gitea.services.ngcloud.ru/Nail/tf_registry.git` (master) > **Управляемый сервис**: `registry` (instanceUid: `309fdf08-8544-407f-96bd-91c8ae70baf8`) > **URL**: `https://registry.pythonk8s.dev.nubes.ru` --- ## 1. Контекст ### Что такое реестр Свой реестр Terraform-провайдеров для `nubes` (кастомный провайдер NGCloud). Работает по HashiCorp Provider Registry Protocol. ### Как было (Go) - Go-сервер в K8s (личный кластер `iot-naeel`, ВМ `5.172.178.213`) - Домен: `registry.kube5s.ru` - S3: `s3.msk-1.ngcloud.ru`, бакет `terraform-registry` - Креды S3: Secret `s3-credentials` в namespace `terra` ### Как стало/становится (Flask) - Managed-сервис Nubes `pythonk8s` - Домен: `registry.pythonk8s.dev.nubes.ru` - Тот же S3 --- ## 2. Структура проекта (как сейчас в master) ``` tf_registry/ ├── site/ │ └── app.py # Flask-приложение (7 роутов) ├── requirements.txt # flask, boto3 ├── secrets/ │ └── env.txt # S3-креды + jsonEnv (в .gitignore) ├── HISTORY/ # Документация │ ├── plan-sonnet-full.md │ ├── 2026-08-06-plan-review.md │ └── 2026-08-06-first-deploy.md ├── .gitignore # secrets/, __pycache__/ ├── server/ # [LEGACY] Go-код (оставлен для справки) ├── operator/ # [LEGACY] ├── k8s/ # [LEGACY] ├── charts/ # [LEGACY] ├── registrykeys/ # [LEGACY] GPG-ключ (устарел!) └── scripts/ # [LEGACY] ``` ### Ключевое правило Nubes для Flask ``` repo/ ├── requirements.txt └── site/ └── app.py ← точка входа, `app.run(host="0.0.0.0", port=5000)` ``` - **НЕЛЬЗЯ** `site/__init__.py` — конфликтует со stdlib - **НЕЛЬЗЯ** factory pattern — платформа ждёт `app` на уровне модуля - **НЕЛЬЗЯ** `from site.xxx import` — импорт без префикса --- ## 3. Эндпоинты (все реализованы) | # | Маршрут | Что делает | |---|---|---| | 1 | `GET /.well-known/terraform.json` | `{"providers.v1": "/v1/providers/"}` | | 2 | `GET /v1/providers/{ns}/{name}/versions` | Список версий + платформы из S3 | | 3 | `GET /v1/providers/{ns}/{name}/{ver}/download/{os}/{arch}` | JSON: download_url, shasums, сигнатуры, GPG | | 4 | `GET /v1/proxy?bucket=...&key=...` | Стриминг файла из S3 | | 5 | `GET /docs/{ns}/{name}/{ver}/...` | Статика из S3 (кандидаты ключей) | | 6 | `GET /healthz`, `GET /readyz` | `"ok"` 200 | | 7 | `GET /` | HTML с версией | --- ## 4. Конфигурация (jsonEnv в UI) ```json { "REGISTRY_HOSTNAME": "registry.pythonk8s.dev.nubes.ru", "S3_ENDPOINT": "s3.msk-1.ngcloud.ru", "S3_ACCESS_KEY": "0GLQRD38H4I6RBDB0EWJ", "S3_SECRET_KEY": "eTFibiHmBd96IApj9PYsboTR6OBoD7osxoarHykw", "S3_BUCKET": "terraform-registry" } ``` ### Параметры managed-сервиса (appConfiguration) ```json { "gitPath": "https://gitea.services.ngcloud.ru/Nail/tf_registry.git", "version": "3.12", "healthPath": "" } ``` --- ## 5. Ключевые детали реализации ### S3_PREFIX vs HOSTNAME ```python HOSTNAME = os.getenv("REGISTRY_HOSTNAME", "localhost:8080") # для URL S3_PREFIX = "registry.kube5s.ru" # для S3-ключей (старый Go-хостнейм!) ``` S3-ключи лежат под `registry.kube5s.ru/...`, а managed-домен — `registry.pythonk8s.dev.nubes.ru`. Поэтому S3-префикс жёстко `registry.kube5s.ru`. ### S3-клиент (boto3 + MinIO) ```python s3 = boto3.client( "s3", endpoint_url=f"https://{S3_ENDPOINT}", config=Config(signature_version="s3v4"), # важно для MinIO use_ssl=True, ) ``` ### Пагинация S3 Используется `get_paginator("list_objects_v2")` — не `list_objects_v2` напрямую (макс 1000). ### Платформы Обнаруживаются по имени файла: `_darwin_amd64.zip`, `_linux_amd64.zip`, `_windows_amd64.zip`. ### GPG-ключи (ДВА — из живого Go-деплоя!) ```python GPG_PRIMARY_KEY_ID = "3EC4673EB798238A" # основной (живой деплой) GPG_LEGACY_KEY_ID = "CB3A0DF161ECC416" # старый (из исходников) ``` Оба возвращаются в `signing_keys.gpg_public_keys`. ### Docs-кандидаты (1:1 с Go) ``` /docs/{ns}/{name}/{ver}/style.css 1) docs/{ns}/{name}/{ver}/style.css (exact) 2) НЕ пробует style.css/index.html (есть расширение .css) 3) docs/{ns}/{name}/{ver}/index.html (fallback) /docs/{ns}/{name}/{ver}/folder/ 1) docs/{ns}/{name}/{ver}/folder/ (exact) 2) docs/{ns}/{name}/{ver}/folder/index.html (заканчивается на /) 3) docs/{ns}/{name}/{ver}/index.html (fallback) ``` ### Content-Length при стриминге `head_object` → `ContentLength` → выставляется в заголовках. ### Порядок роутов `/` — ПОСЛЕДНИЙ, иначе перехватит `/docs/`. --- ## 6. История деплоев | # | Коммит | Что | Результат | |---|---|---|---| | 1 | `7c2b030` | Минимальный Flask (только `/`) | ❌ Init:Error — gitea приватный | | 2 | — | Репо сделан публичным, редеплой | ✅ `tf_registry v1.0.0` | | 3 | `3fa15cd` | Полный Flask (7 роутов) | ✅ Страница ONLINE, API — пустые versions | | 4 | `a89b1f1` | Fix: S3_PREFIX, GPG, darwin | ⏳ Ожидает редеплоя | | 5 | `f8f6343` | Версия на главной | ⏳ | | 6 | `5ed2948` | HISTORY-документация | ✅ | --- ## 7. Текущий статус (последний пуш: `5ed2948`) - app.py: написан, проверен `py_compile`, все 7 роутов ✅ - S3_PREFIX: `registry.kube5s.ru` ✅ - GPG: 2 ключа из живого Go ✅ - Платформы: darwin + linux + windows ✅ - Версия на главной: `v1.0.0` ✅ - **НУЖЕН РЕДЕПЛОЙ** через UI → проверить `versions` и `download` --- ## 8. Что дальше 1. Редеплой через UI 2. Проверить `curl /.well-known/terraform.json` 3. Проверить `curl /v1/providers/nubes-test/nubes/versions` — должны быть версии 4. Проверить `curl /v1/providers/nubes-test/nubes/5.1.16/download/linux/amd64` — shasum не пустой 5. `terraform init` с `source = "registry.pythonk8s.dev.nubes.ru/nubes-test/nubes"` --- ## 9. Как продолжить в новом чате 1. Открыть workspace `~/tf_registry` 2. Прочитать этот файл 3. `git pull` (на всякий случай) 4. Сказать: «Продолжаем tf_registry Flask-реестр. Контекст в HISTORY/README-NEW-CHAT.md» --- ## 10. Правила проекта - После каждого коммита → сразу push - Перед каждым коммитом → `python3 -m py_compile site/app.py` - `gitPath` в UI: `https://gitea.services.ngcloud.ru/Nail/tf_registry.git` - Репо должен быть публичным (или вшить токен в gitPath) - Кубер — только через ВМ: `ssh naeel@5.172.178.213` - Не удалять Go-файлы — оставлены для справки - S3_PREFIX = `registry.kube5s.ru` (НЕ managed-домен!)