Files
autotest/polygon-docs/sonnet-code-review-prompt.md
T

9.9 KiB
Raw Blame History

Задача: Code Review polygon v0.2.0

Адресат: Claude Sonnet 4.6 (новый чат, с нуля) Дата: 2026-07-31 ОТВЕТ — ТОЛЬКО В ЧАТ. Не редактировать файлы. Не создавать файлы.


1. Что такое polygon

Polygon — эмулятор REST API облачной платформы Nubes. Отдельный managed-сервис (polygon.pythonk8s.dev.nubes.ru), притворяется реальным Nubes API для интеграционных тестов приложения app-autotest.

  • Flask 3.0 + gunicorn, деплой на Nubes pythonk8s
  • Все данные в памяти (MockState), без БД
  • Data-driven: конфиги сервисов генерируются из STANDS YAML через from_stands.py
  • 37 сервисов (dummy, postgres, redis, kafka, flask, ...)
  • 17 API-эндпоинтов, префикс /api/v1/svc
  • 19 юнит-тестов (все PASS)

Репозиторий: https://gitea.services.ngcloud.ru/forcloud/polygon.git (ветка master) Деплой: https://polygon.pythonk8s.dev.nubes.ru/ Версия: v0.2.0

2. Структура кода

polygon/
├── requirements.txt          # Flask>=3.0, gunicorn>=21.2, PyYAML>=6.0
├── .gitignore
├── tests/
│   ├── test_converter.py     # 10 юнит-тестов from_stands.py
│   └── test_state_machine.py # 9 юнит-тестов state_machine.py + mock_state
└── site/
    ├── app.py                # Flask-приложение (17 эндпоинтов)
    ├── mock_state.py         # MockState: instances, operations, op_params в памяти
    ├── state_machine.py      # apply_effect() — мутация состояния по kind/action
    ├── config_loader.py      # загрузка services/*.yaml → {svcId: def} + {opId: def}
    ├── from_stands.py        # конвертер STANDS YAML → polygon config
    └── services/             # 37 сгенерированных YAML-конфигов

3. Что делает каждый модуль

app.py — 17 эндпоинтов

Полный эмулятор Nubes API. Порядок маршрутов критичен: default/<int> ДО <uid>.

# Метод Путь Назначение
1 GET /health "OK" (для Nubes healthcheck)
2 GET / HTML с версией, счётчиками
3 GET /api/v1/svc/services список сервисов
4 GET /api/v1/svc/services/<id> операции сервиса
5 GET /api/v1/svc/instances пагинация
6 GET /api/v1/svc/instances/<uid> инстанс + state.params + state.out
7 POST /api/v1/svc/instances создать shell → 201 + Location: ./{uid}
8 GET /api/v1/svc/instanceOperations/default/<id> cfsParams с dataDescriptor, valueList
9 POST /api/v1/svc/instanceOperations создать операцию → 201 + Location
10 GET /api/v1/svc/instanceOperations/<uid> статус + cfsParams (?fields=...)
11 POST /api/v1/svc/instanceOperationCfsParams paramId → value
12 GET /api/v1/svc/instanceOperations/<uid>/validate-cfs пустое тело, 200
13 POST /api/v1/svc/instanceOperations/<uid>/run sleep → apply_effect → dtFinish
14 POST /api/v1/svc/_mock/reset сброс
15 GET /api/v1/svc/_mock/state отладка
16 GET /api/v1/svc/_mock/services отладка
17 POST /api/v1/svc/_mock/delay/<s> MOCK_OP_DELAY

Критические точки:

  • POST /instances и POST /instanceOperations обязаны отдавать Location: ./{uuid} — app-autotest достаёт UUID из заголовка
  • POST /instanceOperations при operation=="create" — тело НЕ содержит svcOperationId, polygon ищет сам
  • validate-cfs: return "", 200 (НЕ jsonify) — app-autotest ждёт пустое тело
  • run: синхронный time.sleep(MOCK_OP_DELAY) + apply_effect + dtFinish = now
  • MOCK_OP_DELAY из env, по умолчанию 0.1с

mock_state.py — состояние в памяти

class MockState:
    instances  = {}   # instanceUid → {instanceUid, serviceId, displayName, status, state: {params, out}, ...}
    operations = {}   # opUid → {instanceOperationUid, instanceUid, svcOperationId, operation, kind, action, dtStart, dtFinish, isSuccessful, ...}
    op_params  = {}   # opUid → {paramId(int): paramValue(str)}

Методы: create_instance, get_instance, list_instances (пагинация), create_operation, get_operation, set_param, get_params, reset.

UUID через uuid.uuid4(). Пагинация: pageSize ≤ 200, стоп по len(batch) < pageSize.

state_machine.py — apply_effect

Мутирует MockState после завершения операции:

kind action Эффект
instance create статус running, stateParams из шаблона, stateOut из шаблона
instance modify мерж op_params в state.params через cfsParamsByOp
instance delete удалить инстанс
instance suspend статус suspended
instance resume статус running
instance redeploy статус running
instance restart/recovery/... no-op
subresource create state.out[plural][name] = {}
subresource delete del state.out[plural][name]

_extract_subresource_name: ищет первое непустое строковое значение в op_params, fallback на subresource_name.

config_loader.py — загрузка конфигов

Читает все .yaml из services/. Возвращает:

  • services: {service_id: service_def}
  • ops_index: {svcOperationId: service_def} — для поиска сервиса по ID операции

from_stands.py — конвертер

Конвертирует STANDS YAML (из Terraform-провайдера) → polygon-конфиг. Запуск: python from_stands.py <STANDS_DIR> [SERVICES_DIR]

  • html.unescape() для всех строк (&gt;>, &quot;")
  • Маппинг полей: idsvcOperationCfsParamId, codesvcOperationCfsParam, data_typedataType, value_listvalueList, sub_paramsdataDescriptor
  • dataDescriptor генерируется для всех типов с sub_params (map, map-fixed, array-map-fixed)
  • state_out_template: авто-генерация из операций с kind=subresource, action=create
  • stateParams: defaults из create-операции, JSON-генерация для map-fixed
  • cfsParamsByOp: связка opId → список paramId

4. Что проверять (фокус code review)

Безопасность

  • Все ли входные данные валидируются (JSON body, query params)?
  • Есть ли возможность инъекции через displayName, paramValue?
  • _mock/* эндпоинты — не утечка ли это на проде?

Корректность API

  • Совпадают ли форматы ответов с реальным Nubes API?
  • Правильно ли обрабатываются краевые случаи: отсутствующий сервис, невалидный instanceUid, пустой body?
  • Корректны ли статус-коды (201, 400, 404)?
  • Location-заголовки правильного формата?

Состояние и стейт-машина

  • Нет ли гонок в MockState (хотя воркер один, но Flask debug mode reloads)?
  • Все ли переходы стейт-машины корректны?
  • Не теряются ли данные при modify/reset?
  • Правильно ли работает пагинация при пустом/частичном списке?

Конвертер

  • Все ли краевые случаи STANDS YAML обрабатываются (пустые поля, отсутствующие sub_params)?
  • Правильно ли html.unescape применяется ко всем строковым полям?
  • Не падает ли на нестандартных YAML (template, s3bucket)?

Код и архитектура

  • Нет ли дублирования логики?
  • Понятны ли имена функций/переменных?
  • Нет ли мёртвого кода?
  • Правильно ли обрабатываются ошибки (try/except где нужно)?

Nubes-совместимость

  • site/__init__.py отсутствует?
  • app.run(host="0.0.0.0", port=5000) на месте?
  • from site.xxx нигде нет?
  • /health возвращает "OK"?

5. Формат ответа

Сгруппируй находки по категориям:

  • 🔴 Критические (сломает работу)
  • 🟡 Средние (потенциальная проблема)
  • 🔵 Минорные (стиль, имена)

Для каждой находки: файл, строка (примерная), проблема, предлагаемое исправление.

Если код в порядке — скажи что всё ок по каждой категории.

6. Что можно спрашивать у меня

Можешь задавать уточняющие вопросы. Например:

  • «Какой формат у real Nubes API для эндпоинта X?»
  • «Почему сделано так, а не иначе?»
  • «Какие именно поля ждёт app-autotest в ответе Y?»

Формат вопросов:

### Вопрос N: <краткий заголовок>
<развёрнутый вопрос>