docs(opus): исчерпывающий запрос — спроектировать архитектуру модификаторов с нуля (все кейсы)
This commit is contained in:
@@ -0,0 +1,73 @@
|
|||||||
|
# Спроектировать С НУЛЯ архитектуру/логику «ресурсов-модификаторов» (kind: modifier)
|
||||||
|
|
||||||
|
## Цель
|
||||||
|
|
||||||
|
Перепроектировать модификаторы целиком, чтобы исключить ВСЕ классы багов, не латать по одному.
|
||||||
|
Нужна единая, полная модель поведения — без догадок и костылей. Перечислить ВСЕ кейсы.
|
||||||
|
|
||||||
|
## Что такое модификатор (текущая фактура)
|
||||||
|
|
||||||
|
В YAML (генерируется из API) операции бывают:
|
||||||
|
- `kind: instance` (create/modify/suspend/resume/delete) — обычный CRUD-ресурс;
|
||||||
|
- `kind: modifier` + `modifier: <name>` — отдельный TF-ресурс, который вызывает `modify`
|
||||||
|
на родительском инстансе. Сейчас их два: `vc_org.ip_space`, `vc_nsxt.network`.
|
||||||
|
|
||||||
|
Реальные примеры:
|
||||||
|
- `vc_org` → modifier `ip_space` (modify 207), параметр `vIPConfigure` (array-map-fixed);
|
||||||
|
- `vc_nsxt` → modifier `network` (modify 111), параметры `needEnableAVI`(bool),
|
||||||
|
`virtualServicesCount`(int>0), `qosProfile`(string), `ipSpaceName`(string),
|
||||||
|
`routedNetConfiguration`(map-fixed).
|
||||||
|
|
||||||
|
## Текущий механизм (что есть — факты, не догадки)
|
||||||
|
|
||||||
|
1. Генератор: `TOOLS/resource-generator/internal/templates/modifier.go`
|
||||||
|
- Create и Update **идентичны**: оба шлют `modify` с полным набором полей.
|
||||||
|
- `Delete` — **no-op** (комментарий: «no confirmed inverse payload»).
|
||||||
|
- Схема: `id` computed, `<service>_id` required, поля Optional (или Required если нет default).
|
||||||
|
2. `resources_core.CompactParams` — выбрасывает пустые строки из payload.
|
||||||
|
3. `resources_core.BuildActionID(instanceUID, operation, modifierName)` — константный ID,
|
||||||
|
не привязан к реальной операции (opUid не сохраняется).
|
||||||
|
4. `core.RunInstanceOperationUniversalByCode` — резолвит code→id через
|
||||||
|
`GET /instanceOperations/{opUid}?fields=cfsParams` (fallback на `/default/{opId}`);
|
||||||
|
отправляет переданные params, затем дозаполняет остальные их live-значением
|
||||||
|
(guard: пропускает параметр, если нет ни ParamValue, ни DefaultValue).
|
||||||
|
5. `Read` — через `RefreshResourceState`: читает `state_params` инстанса и
|
||||||
|
перезаписывает input-поля из них.
|
||||||
|
|
||||||
|
## Уже выявленные КЛАССЫ багов (все реально случились)
|
||||||
|
|
||||||
|
- **A. Сброс create-поля при modify.** modify со сброшенными (null) параметрами
|
||||||
|
трактуется бэкендом как reset-to-default: `needEnableAVI` стал false после
|
||||||
|
create=true. Причина: модификатор шлёт только свои поля, `CompactParams` выкидывает
|
||||||
|
пустые, бэкенд видит «отсутствующий» и сбрасывает.
|
||||||
|
- **B. Досылка синтетики.** фикс «досылать всё» слал `"0"` для `integer > 0`
|
||||||
|
(параметр `virtualServicesCount`), API 400 «Invalid format integer > 0».
|
||||||
|
- **C. Ложное «Нельзя изменить».** `ComputeCreateOnly` считал `needEnableAVI`
|
||||||
|
CreateOnly (change-forbidden), хотя в YAML `is_modifiable: true` — потому что
|
||||||
|
генератор не учитывал modifier-канал и терял `IsModifiable`. (Зафиксировано отдельно.)
|
||||||
|
- **D. No-op Delete оставляет эффект на платформе.** destroy модификатора убирает
|
||||||
|
ресурс из state, но выделенные IP / включённый ALB остаются на платформе → drift.
|
||||||
|
- **E. Повторный apply после taint/replace** снова гонит modify — риск повторной
|
||||||
|
аллокации (для `ip_space`), идемпотентность не гарантирована.
|
||||||
|
|
||||||
|
## Вопросы к Опусу (ответить ПОЛНО, по пунктам, с точными местами правки)
|
||||||
|
|
||||||
|
1. **Канон «как сравнить и применить».** Должен ли модификатор перед modify
|
||||||
|
читать текущее состояние и слать ДЕЛЬТУ (только реально изменившиеся поля),
|
||||||
|
или ПТЦ полный payload? Как детектить drift в Read?
|
||||||
|
2. **Досылка незаданных полей (паер-заливы A и B).** Какое каноническое правило:
|
||||||
|
когда досылать live-значение, когда дефолт, когда пропускать? Как не сломать
|
||||||
|
`integer > 0` и прочие constraints?
|
||||||
|
3. **Delete/rollback.** Где искать обратный payload? Как правильно поступить, пока
|
||||||
|
обратный payload НЕ подтверждён API (no-op допустим? явная ошибка? suspend?).
|
||||||
|
4. **Idempotency + ID.** Как сделать ID модификатора отражающим фактическую операцию
|
||||||
|
(opUid?) и как предотвратить двойную аллокацию при replace/повторном apply?
|
||||||
|
5. **Связь с родителем.** Должен ли модификатор использовать `<service>_id` как ссылку
|
||||||
|
на родителя (depends_on / borrow state), и как читать UUID родителя?
|
||||||
|
6. **Create vs Update.** Допустимо ли иметь их идентичными, или нужен строго Update-семантик
|
||||||
|
(нет create, только apply-по-десяти)?
|
||||||
|
7. **Полный перечень кейсов.** Перечислить ВСЕ edge-кейсы, которые надо покрыть:
|
||||||
|
create родителя → modifier; remove modifier; replace; partial params; unknown/absent.
|
||||||
|
|
||||||
|
Ответ — архитектурный документ (краткий, структурированный), с конкретными файлами
|
||||||
|
и функциями. НЕ код-ревью, а ПРОЕКТ.
|
||||||
Reference in New Issue
Block a user