polygon v0.5.18

Эмулятор REST API облачной платформы Nubes для интеграционных тестов app-autotest.

Отдельный managed-сервис на Nubes pythonk8s: polygon.pythonk8s.dev.nubes.ru. Притворяется реальным Nubes API (префикс /api/v1/svc).

Архитектура

polygon/
├── requirements.txt      # Flask>=3.0, gunicorn>=21.2, PyYAML>=6.0
├── tests/
│   ├── test_converter.py         # тесты from_stands.py
│   ├── test_loader.py            # тесты CWD-independent loader
│   ├── test_state_machine.py     # тесты state_machine.py
│   └── test_route_regressions.py # локальные регрессионные тесты API
└── site/
    ├── app.py                # Flask: только blueprint'ы + app.run() (57 строк)
    ├── routes/               # blueprint-файлы по доменам
    │   ├── root.py           #   /health, /, /swagger
    │   ├── services_routes.py#   /api/v1/svc/services
    │   ├── instances_routes.py#  /api/v1/svc/instances
    │   ├── operations_routes.py# /api/v1/svc/instanceOperations/*
    │   ├── run.py            #   /api/v1/svc/instanceOperations/<uid>/run
    │   ├── mock_routes.py    #   /api/v1/svc/_mock/*
    │   └── openapi.py        #   /api/v1/svc/openapi.json
    ├── mock_state.py         # MockState: instances, operations, op_params
    ├── state_machine.py      # apply_effect() — мутация состояния
    ├── config/
    │   └── loader.py         # загрузка YAML + SERVICES/OPS_INDEX/DELAY/VERSION
    ├── utils/
    │   ├── now.py            # now() — UTC ISO с 'Z'
    │   └── pluralize.py      # pluralize() — плюрализация
    ├── from_stands.py        # конвертер STANDS YAML → polygon config
    ├── static/
    │   └── style.css         # тёмная тема
    ├── templates/
    │   ├── index.html        # главная и инструкции API
    │   └── swagger.html      # Swagger UI и инструкция Authorize
    └── services/             # fallback YAML, также генерируются from_stands.py

⛔ Ровно 1 gunicorn-воркер. Состояние в памяти, не shared.

Запуск

# Локально
cd site && python app.py                          # порт 5000

# Тесты
pytest tests/ -v                                  # локальные тесты Polygon

Откуда берутся сервисы (YAML)

Polygon эмулирует API Nubes. Чтобы знать какие сервисы, операции и параметры существуют — он читает YAML-конфиги. Но Nubes генерит YAML в другом формате, неудобном для полигона. Поэтому есть двухэтапный процесс:

~/TF/tf_provider/generated/{dev,test,prod}/resources_yaml/*.yaml   ← источник истины
        │
        │  from_stands.py  —  конвертация формата, дедупликация, нормализация
        │  Запускается ВРУЧНУЮ при изменении терраформ-репы
        │
        ▼
polygon/site/services/{dev,test,prod}/*.yaml   ← скоммичены в git-репу полигона
        │
        │  config/loader.py  —  загрузка ВСЕХ YAML в память при старте
        │
        ▼
Память Flask-процесса  (конфиги загружаются один раз при старте)

Если POLYGON_STANDS не задан, loader использует один стенд default из site/services/. Значения относительных путей в POLYGON_STANDS разрешаются относительно site/.

Почему нельзя читать терраформ-формат напрямую?

Терраформ-формат хранит параметры внутри операций, с дубликатами. Например параметр resourceRealm повторяется в create, modify и delete — три копии одного и того же.

Полигону нужен плоский список уникальных параметров (cfsParams), плюс индекс «какой параметр к какой операции» (cfsParamsByOp). Плюс stateParams (дефолты для create), stateOut (subresource-ключи), раскодированные HTML-entities (&quot; → ").

Всё это делает from_stands.py один раз, а не на лету при каждом запросе.

Когда запускать обновление?

Когда терраформ-репа обновилась (добавили/удалили сервис, изменили параметры) — нужно перегенерить YAML и передеплоить полигон:

cd site
python from_stands.py ~/TF/tf_provider/generated/dev/resources_yaml  services/dev
python from_stands.py ~/TF/tf_provider/generated/test/resources_yaml services/test
python from_stands.py ~/TF/tf_provider/generated/prod/resources_yaml services/prod

cd ..
git add services/
git commit -m "regenerate YAML from terraform (добавлен сервис X)"
git push
# → редеплоить polygon через Nubes UI

После успешной конвертации генератор удаляет из целевого каталога YAML, которых больше нет в источнике. Если конвертация завершилась ошибками, устаревшие файлы не удаляются. Если SERVICES_DIR не передан, генератор пишет в site/services/ независимо от CWD. Явно переданные относительные STANDS_DIR и SERVICES_DIR остаются относительно CWD.

Как проверить что всё загрузилось?

curl https://polygon.pythonk8s.dev.nubes.ru/dev/api/v1/svc/services | python3 -m json.tool | head -20

API

Метод Путь Назначение
GET /health Healthcheck
GET / HTML с версией
GET /swagger Swagger UI
GET /api/v1/svc/openapi.json OpenAPI JSON
GET /api/v1/svc/services Список сервисов
GET /api/v1/svc/services/<id> Операции сервиса
GET /api/v1/svc/instances Пагинация
GET /api/v1/svc/instances/<uid> Инстанс + state
POST /api/v1/svc/instances Создать → 201 + Location
GET /api/v1/svc/instanceOperations/default/<id> Шаблон операции и cfsParams
POST /api/v1/svc/instanceOperations Создать операцию → 201 + Location
GET /api/v1/svc/instanceOperations/<uid> Статус + cfsParams
POST /api/v1/svc/instanceOperationCfsParams param → value
GET /api/v1/svc/instanceOperations/<uid>/validate-cfs 200, пустое тело
POST /api/v1/svc/instanceOperations/<uid>/run Выполнить
POST /api/v1/svc/_mock/reset Сброс
GET /api/v1/svc/_mock/state Отладка: состояние
GET /api/v1/svc/_mock/services Отладка: сервисы
POST /api/v1/svc/_mock/delay/<s> MOCK_OP_DELAY
POST /api/v1/svc/_mock/fail-next Следующий run завершится ошибкой

Основные API-маршруты не требуют авторизации. Если задан MOCK_AUTH_TOKEN, запросы к /_mock/* должны передавать заголовок X-Mock-Auth.

Где взять и куда вводить токен

MOCK_AUTH_TOKEN задаёт администратор в переменных окружения запущенного сервиса Polygon. Приложение не генерирует и не показывает это значение. Если у вас нет доступа к настройкам сервиса, запросите токен у его администратора.

В Swagger нажмите Authorize, в поле X-Mock-Auth → Value вставьте выданный токен и нажмите Authorize в диалоге. После этого Swagger автоматически добавит заголовок к запросам /_mock/*. В curl передавайте его явно: -H "X-Mock-Auth: <token>". Если MOCK_AUTH_TOKEN на сервере не задан, токен вводить не нужно.

Переменные окружения

Переменная По умолчанию Описание
POLYGON_STANDS default:services Список стендов name:path,name:path; относительные пути считаются от site/
MOCK_OP_DELAY 0.1 Задержка операции в секундах
MOCK_AUTH_TOKEN пусто Опциональный токен для /_mock/*; при пустом значении локальные mock endpoints открыты
POLYGON_ENDPOINT https://polygon.pythonk8s.dev.nubes.ru Базовый URL для серверов в OpenAPI
WEB_CONCURRENCY 1 Использовать один gunicorn-воркер, поскольку состояние хранится в памяти

Деплой

Сервис деплоится в Nubes как Python Flask managed service.

appConfiguration:

{
        "version": "3.12",
        "gitPath": "https://gitea.services.ngcloud.ru/forcloud/polygon.git",
        "healthPath": ""
}

version должен совпадать с Python runtime, выбранным в настройках существующего сервиса Nubes. Репозиторий Polygon не хранит фактически выбранное там значение; 3.12 приведено по конфигурации app-autotest.

jsonEnv:

{
        "POLYGON_STANDS": "dev:services/dev,test:services/test,prod:services/prod",
        "WEB_CONCURRENCY": "1",
        "POLYGON_ENDPOINT": "https://polygon.pythonk8s.dev.nubes.ru"
}

POLYGON_STANDS загружает три каталога из site/services/; пути разрешаются относительно site/. WEB_CONCURRENCY должен быть равен 1, потому что состояние хранится в памяти одного процесса. POLYGON_ENDPOINT задаёт публичный base URL серверов в OpenAPI; его default уже совпадает с URL сервиса. MOCK_OP_DELAY необязателен (default 0.1). MOCK_AUTH_TOKEN задавайте только если нужно закрыть служебные /_mock/* endpoints.

URL сервиса: https://polygon.pythonk8s.dev.nubes.ru/.

Связанные документы

S
Description
No description provided
Readme
582 KiB
Languages
Python 83.8%
HTML 12%
CSS 4.2%