add: documentation

This commit is contained in:
“Naeel”
2026-06-30 15:45:24 +04:00
parent 540c1f7293
commit ca276d200f
1055 changed files with 47294 additions and 0 deletions
+178
View File
@@ -0,0 +1,178 @@
# Adopt/Resume: валидация ref-параметров и защита от внешних изменений
> Дата: 2026-03-29
> Статус: **реализовано в v5.0.50**
> History: `docs/50_history/22_adopt_ref_validation_duplicate_detection_5_0_50.md`
> Триггер: баг `inconsistent result after apply` при adopt VM с протухшим vapp_uid
---
## 1. ОПИСАНИЕ БАГА
### Цепочка событий
1. `terraform destroy` → vApp suspend, VM suspend. Terraform state обнулён.
2. Юзер удаляет vApp из ЛК (или vApp удалён 14-дневным auto-delete). UUID: `fba91c08`.
3. `terraform apply`
- `nubes_vapp.vapp` Create → новый инстанс, UUID: `f037ddea` (running).
- `nubes_vc_vm_v3.vm` Create → `adopt_existing_on_create=true`
`FindInstanceByDisplayName` находит suspended VM → resume.
4. Resumed VM хранит в `state.params.vappUid` = `fba91c08` (старый deleted vApp).
5. `RefreshResourceState` читает `vappUid` из API → `fba91c08`.
6. Terraform план ожидал `f037ddea` → получил `fba91c08`**"inconsistent result after apply"**.
### Корневая причина
При adopt/resume **не проверяются ref-параметры**. Провайдер молча adopt-ит
инстанс с протухшими ссылками на deleted/suspended зависимости.
### Доказательства из API
- `f037ddea` (vApp) — `explainedStatus: "running"`, создан 29.03, serviceId=26
- `fba91c08` (vApp) — `explainedStatus: "deleted"`, `isDeleted: true`, создан 27.03, serviceId=26
- Оба с `displayName: "vm-sless-vapp"` — дубликат по имени (один deleted, один running)
---
## 2. ПОЛНАЯ МАТРИЦА ПРОБЛЕМ
### A. Ref-параметры протухли
| # | Ситуация | Текущее поведение | Целевое поведение |
|---|----------|-------------------|-------------------|
| A1 | VM.vappUid → deleted vApp | `inconsistent result after apply` | **Error**: "VM (resource_name='vm-sless-1') ссылается на vApp (fba91c08) со статусом 'deleted'. vApp был удалён из облака. Варианты: (1) удалите suspended VM из облака и пересоздайте всё; (2) восстановите vApp через ЛК." |
| A2 | VM.vappUid → suspended vApp | Непредсказуемо | **Warning**: "VM ссылается на vApp (xxx) в статусе 'suspended'. Убедитесь что зависимые ресурсы активны перед apply." |
| A3 | UUID в плане ≠ UUID в API (старый deleted, новый создан) | `inconsistent result` | **Error**: "VM хранит ссылку vapp_uid=fba91c08 (deleted), но конфигурация указывает f037ddea (running). Adopt невозможен — ref-параметры не совпадают. Удалите suspended VM из облака." |
| A4 | Ref-инстанс 404 (не найден в API) | Паника или мусор в стейте | **Error**: "Инстанс fba91c08, на который ссылается параметр vapp_uid, не найден в API (404). Проверьте облако." |
### B. Множественные инстансы с одним display_name
| # | Ситуация | Текущее поведение | Целевое поведение |
|---|----------|-------------------|-------------------|
| B1 | 2 инстанса: один deleted, один running | Пропускает deleted → OK | OK + **Warning**: "Обнаружено 2 инстанса с именем 'vm-sless-vapp' (serviceId=26). Используется running (f037ddea). Deleted: fba91c08." |
| B2 | 2 инстанса оба suspended | Adopt первый попавшийся | **Error**: "Найдено 2 suspended инстанса с именем 'vm-sless-1' (serviceId=28). Невозможно определить какой adopt-ить. Удалите лишний через ЛК." |
| B3 | 2 инстанса оба running | Adopt первый попавшийся | **Error**: "Найдено 2 running инстанса с именем 'xxx'. Укажите конкретный UUID через `terraform import`." |
### C. Порядок resume зависимых ресурсов
| # | Ситуация | Текущее поведение | Целевое поведение |
|---|----------|-------------------|-------------------|
| C1 | VM resume раньше vApp resume | VM может не стартовать | **Warning**: "vApp (f037ddea) в статусе 'suspended'. VM может не запуститься. Рекомендуется: depends_on или сначала resume vApp." |
| C2 | vApp resume, но Edge deleted | vApp стартует без сети | **Warning**: "Edge (0fe88e2a), на который ссылается vApp, в статусе 'deleted'. Сетевые функции могут быть недоступны." |
### D. Внешние изменения (ЛК / другой оператор)
| # | Ситуация | Текущее поведение | Целевое поведение |
|---|----------|-------------------|-------------------|
| D1 | Юзер изменил CPU/RAM в ЛК | Plan покажет diff → modify обратно | **Warning при Read**: "Обнаружено расхождение: vm_cpu в облаке=4, в конфигурации=2. Возможно параметры изменены вручную через ЛК." |
| D2 | Юзер переименовал ресурс в ЛК | Read по ID работает | **Warning**: "display_name в облаке 'new-name' не совпадает с конфигурацией 'vm-sless-1'." |
| D3 | Юзер suspend из ЛК | Read видит suspended | **Warning**: "Инстанс (xxx) в статусе 'suspended' но в Terraform state не удалён. Возможно suspend выполнен вручную." |
| D4 | Операция in_progress при plan/apply | Конфликт операций | **Error**: "Инстанс (xxx): операция в процессе (operation_in_progress=true). Дождитесь завершения." |
### E. State corruption / drift
| # | Ситуация | Текущее поведение | Целевое поведение |
|---|----------|-------------------|-------------------|
| E1 | Terraform state → 404 инстанс | Read → ошибка | `RemoveResource` + **Warning**: "Инстанс (xxx) не найден в облаке (404). Удалён из Terraform state." |
| E2 | ID в стейте есть, `isDeleted=true` | Read видит deleted | `RemoveResource` + **Warning**: "Инстанс (xxx) в статусе 'deleted'. Удалён из Terraform state." |
---
## 3. ПЛАН ПРАВОК
### Уровень 1 — критические (ломают apply)
#### 1.1 Валидация ref-параметров при adopt/resume
- **Файл**: `universal_rebuild/internal/resources_core/crud.go`
- **Где**: новая функция `ValidateRefParamsOnAdopt`, вызов из `adoptExistingInstanceOnCreate` после resume, перед return
- **Логика**:
1. Получить `state.params` resumed-инстанса (уже есть через GetInstanceState)
2. Для каждого param из `params map[int]string`, у которого значение UUID-like:
- Запросить `/instances/{uuid}` → проверить `explainedStatus`
- Если deleted/404 → hard error с описанием
- Если suspended → warning
3. Сравнить плановые ref-значения с фактическими из API
- Если не совпадают → hard error: "план ожидает X, API вернул Y (статус: Z)"
- **Затрагивает**: все сгенерированные ресурсы с ref-параметрами (VM, и любые будущие)
- **НЕ затрагивает**: сгенерированный код ресурсов (они вызывают crud.go)
#### 1.2 Проверка operation_in_progress перед любой операцией
- **Файл**: `universal_rebuild/internal/resources_core/crud.go`
- **Где**: начало `CreateResourceWithTimeout`, перед `FindInstanceByDisplayName`
- **Логика**: если existing != nil && (OperationIsInProgress || OperationIsPending) → hard error
- **Также**: в `RefreshResourceState` — если operation_in_progress → warning
#### 1.3 Обработка множественных инстансов
- **Файл**: `universal_rebuild/internal/core/instance_lookup.go`
- **Где**: `FindInstanceByDisplayName` — собирать ВСЕ совпадения, не только первое
- **Логика**:
- Если 1 non-deleted → вернуть его
- Если >1 non-deleted → hard error "найдено N инстансов с именем X"
- Если 0 non-deleted → вернуть nil (как сейчас)
- Если есть deleted дубликаты → warning с их UUID
### Уровень 2 — защита от ЛК-вмешательства
#### 2.1 Warning при drift параметров в Read
- **Файл**: `universal_rebuild/internal/resources_core/state_refresh.go`
- **Где**: в `RefreshResourceState`, при обработке InputFields
- **Логика**: если значение из API != значение из текущего state → добавить Warning diagnostic
- **Важно**: только warning, не error — Terraform сам обработает diff
#### 2.2 Warning при suspended зависимостях
- **Файл**: `universal_rebuild/internal/resources_core/crud.go`
- **Где**: после resume в `adoptExistingInstanceOnCreate`
- **Логика**: для каждого ref-param UUID → проверить статус → если suspended → warning
#### 2.3 Обработка 404/deleted в Read
- **Файл**: `universal_rebuild/internal/resources_core/state_refresh.go`
- **Где**: в начале `RefreshResourceState` или в `ShouldRemoveFromState`
- **Логика**: если инстанс 404 или isDeleted → RemoveResource + warning
- **Примечание**: частично уже есть через `ShouldRemoveFromState`, проверить полноту
### Уровень 3 — качество диагностик
#### 3.1 Единый формат diagnostics
- **Файл**: новый файл `universal_rebuild/internal/resources_core/diagnostics.go`
- **Содержание**: хелпер-функции для формирования стандартизированных многострочных сообщений
- **Формат каждой диагностики**:
```
ПРОБЛЕМА: <краткое описание>
Детали:
resource_name: vm-sless-1
instance_uid: <uuid>
service_id: 28
параметр: vapp_uid
ожидалось: f037ddea (running)
фактически: fba91c08 (deleted)
Рекомендации:
1. Удалите suspended VM из облака через ЛК
2. Повторите terraform apply
-- ИЛИ --
1. Восстановите vApp через ЛК
2. Повторите terraform apply
```
---
## 4. ЗАТРАГИВАЕМЫЕ ФАЙЛЫ (итого)
| Файл | Тип правки | Уровень |
|------|-----------|---------|
| `universal_rebuild/internal/resources_core/crud.go` | Добавить `ValidateRefParamsOnAdopt`, проверку operation_in_progress | 1 |
| `universal_rebuild/internal/core/instance_lookup.go` | Расширить `FindInstanceByDisplayName` — множественные инстансы | 1 |
| `universal_rebuild/internal/resources_core/state_refresh.go` | Warning при drift, обработка 404/deleted | 2 |
| `universal_rebuild/internal/resources_core/diagnostics.go` | **Новый файл** — хелперы для стандартных диагностик | 3 |
**НЕ затрагиваются**: файлы в `internal/resources_gen/` — они перегенерируются автоматически.
**НЕ затрагиваются**: генераторы — логика целиком в core/resources_core.
---
## 5. ПОРЯДОК РЕАЛИЗАЦИИ
1. `diagnostics.go` (новый файл — хелперы, от которых зависят остальные правки)
2. `instance_lookup.go` — множественные инстансы
3. `crud.go` — ValidateRefParamsOnAdopt + operation_in_progress
4. `state_refresh.go` — drift warnings + 404/deleted
5. `go build` — проверка компиляции
6. Тест на стенде: destroy → delete vApp в ЛК → apply → убедиться что ошибка понятная