13 KiB
13 KiB
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. ОПИСАНИЕ БАГА
Цепочка событий
terraform destroy→ vApp suspend, VM suspend. Terraform state обнулён.- Юзер удаляет vApp из ЛК (или vApp удалён 14-дневным auto-delete). UUID:
fba91c08. terraform apply→nubes_vapp.vappCreate → новый инстанс, UUID:f037ddea(running).nubes_vc_vm_v3.vmCreate →adopt_existing_on_create=true→FindInstanceByDisplayNameнаходит suspended VM → resume.
- Resumed VM хранит в
state.params.vappUid=fba91c08(старый deleted vApp). RefreshResourceStateчитаетvappUidиз API →fba91c08.- Terraform план ожидал
f037ddea→ получилfba91c08→ "inconsistent result after apply".
Корневая причина
При adopt/resume не проверяются ref-параметры. Провайдер молча adopt-ит инстанс с протухшими ссылками на deleted/suspended зависимости.
Доказательства из API
f037ddea(vApp) —explainedStatus: "running", создан 29.03, serviceId=26fba91c08(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 - Логика:
- Получить
state.paramsresumed-инстанса (уже есть через GetInstanceState) - Для каждого param из
params map[int]string, у которого значение UUID-like:- Запросить
/instances/{uuid}→ проверитьexplainedStatus - Если deleted/404 → hard error с описанием
- Если suspended → warning
- Запросить
- Сравнить плановые 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. ПОРЯДОК РЕАЛИЗАЦИИ
diagnostics.go(новый файл — хелперы, от которых зависят остальные правки)instance_lookup.go— множественные инстансыcrud.go— ValidateRefParamsOnAdopt + operation_in_progressstate_refresh.go— drift warnings + 404/deletedgo build— проверка компиляции- Тест на стенде: destroy → delete vApp в ЛК → apply → убедиться что ошибка понятная