Files
tf_provider/docs/60_strategy/adopt_ref_validation.md
T
2026-06-30 15:45:24 +04:00

13 KiB
Raw Blame History

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=trueFindInstanceByDisplayName находит 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 → убедиться что ошибка понятная