8.1 KiB
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в namespaceterra
Как стало/становится (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)
{
"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)
{
"gitPath": "https://gitea.services.ngcloud.ru/Nail/tf_registry.git",
"version": "3.12",
"healthPath": ""
}
5. Ключевые детали реализации
S3_PREFIX vs HOSTNAME
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)
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-деплоя!)
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. Что дальше
- Редеплой через UI
- Проверить
curl /.well-known/terraform.json - Проверить
curl /v1/providers/nubes-test/nubes/versions— должны быть версии - Проверить
curl /v1/providers/nubes-test/nubes/5.1.16/download/linux/amd64— shasum не пустой terraform initсsource = "registry.pythonk8s.dev.nubes.ru/nubes-test/nubes"
9. Как продолжить в новом чате
- Открыть workspace
~/tf_registry - Прочитать этот файл
git pull(на всякий случай)- Сказать: «Продолжаем 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-домен!)