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