add: documentation
This commit is contained in:
@@ -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 → убедиться что ошибка понятная
|
||||
Reference in New Issue
Block a user