diff --git a/provider/internal/resources_core/resource_diagnostics_required.go b/provider/internal/resources_core/resource_diagnostics_required.go index d88e0a1..8a2bb79 100644 --- a/provider/internal/resources_core/resource_diagnostics_required.go +++ b/provider/internal/resources_core/resource_diagnostics_required.go @@ -17,15 +17,25 @@ import ( ) // PlanExistingResourceDiagnosticsWithParams returns diagnostics when resource exists and compares required params. +// Устаревшая обёртка: считает, что сервис поддерживает suspend/resume (старое поведение). func PlanExistingResourceDiagnosticsWithParams(ctx context.Context, client *core.UniversalClient, serviceID int, resourceName string, adoptExistingOnCreate bool, desiredParams map[int]string) diag.Diagnostics { - return PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, client, serviceID, resourceName, adoptExistingOnCreate, desiredParams, "", nil) + return PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, client, serviceID, resourceName, adoptExistingOnCreate, desiredParams, "", nil, true) } +// PlanExistingResourceDiagnosticsWithParamsAndDomain — см. PlanExistingResourceDiagnosticsWithParams. func PlanExistingResourceDiagnosticsWithParamsAndDomain(ctx context.Context, client *core.UniversalClient, serviceID int, resourceName string, adoptExistingOnCreate bool, desiredParams map[int]string, desiredDomain string) diag.Diagnostics { - return PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, client, serviceID, resourceName, adoptExistingOnCreate, desiredParams, desiredDomain, nil) + return PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, client, serviceID, resourceName, adoptExistingOnCreate, desiredParams, desiredDomain, nil, true) } -func PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx context.Context, client *core.UniversalClient, serviceID int, resourceName string, adoptExistingOnCreate bool, desiredParams map[int]string, desiredDomain string, allowedDomainServiceIDs []int) diag.Diagnostics { +// PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices — create-time проверка +// «инстанс с таким именем уже существует». +// +// supportsSuspend — поддерживает ли сервис операции suspend/resume (из YAML-спеки). +// Если НЕТ (например vc_nsxt/Edge), то: +// - советы про resume/adopt для состояния Suspend бессмысленны — такое состояние +// для такого сервиса вообще недостижимо, а усыновление требует resume; +// - корректные действия пользователя: удалить инстанс в ЛК или взять другое имя. +func PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx context.Context, client *core.UniversalClient, serviceID int, resourceName string, adoptExistingOnCreate bool, desiredParams map[int]string, desiredDomain string, allowedDomainServiceIDs []int, supportsSuspend bool) diag.Diagnostics { f, _ := os.OpenFile("/tmp/nubes_find_debug.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) if f != nil { fmt.Fprintf(f, "[FIND-DEBUG] PlanExistingResourceDiagnostics entered: serviceId=%d name=%q adopt=%v client=%v\n", serviceID, resourceName, adoptExistingOnCreate, client != nil) @@ -71,7 +81,7 @@ func PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx context.C } diags.AddError( "РЕСУРС С ТАКИМ ИМЕНЕМ УЖЕ СУЩЕСТВУЕТ (RUNNING)", - "Инстанс с таким resource_name уже работает.\nАвто-усыновление отключено (adopt_existing_on_create=false).\nВыберите другое имя, если нужен новый ресурс.\nИли выполните импорт существующего ресурса через усыновление: установите adopt_existing_on_create=true и повторите apply."+formatInstanceDetails(existing, serviceID, resourceName), + "Инстанс с таким resource_name уже работает.\nАвто-усыновление отключено (adopt_existing_on_create=false).\nВыберите другое имя, если нужен новый ресурс."+runningConflictHint(supportsSuspend)+formatInstanceDetails(existing, serviceID, resourceName), ) return diags } @@ -86,7 +96,7 @@ func PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx context.C if !adoptExistingOnCreate { diags.AddError( "РЕСУРС С ТАКИМ ИМЕНЕМ УЖЕ СУЩЕСТВУЕТ (SUSPEND)", - "Инстанс найден в состоянии Suspend, но adopt_existing_on_create=false.\nДля восстановления/импорта этим ресурсом включите adopt_existing_on_create=true и повторите apply."+formatInstanceDetails(existing, serviceID, resourceName), + "Инстанс найден в состоянии Suspend, но adopt_existing_on_create=false."+suspendConflictHint(supportsSuspend)+formatInstanceDetails(existing, serviceID, resourceName), ) return diags } @@ -192,11 +202,15 @@ func appendRemotePlanInputDiagnostics(ctx context.Context, client *core.Universa } } +// CreateExistingResourceDiagnosticsWithDomain — устаревшая обёртка (suspend считается +// поддерживаемым). См. CreateExistingResourceDiagnosticsWithDomainAndServices. func CreateExistingResourceDiagnosticsWithDomain(ctx context.Context, client *core.UniversalClient, serviceID int, resourceName string, adoptExistingOnCreate bool, desiredDomain string) diag.Diagnostics { - return CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, client, serviceID, resourceName, adoptExistingOnCreate, desiredDomain, nil) + return CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, client, serviceID, resourceName, adoptExistingOnCreate, desiredDomain, nil, true) } -func CreateExistingResourceDiagnosticsWithDomainAndServices(ctx context.Context, client *core.UniversalClient, serviceID int, resourceName string, adoptExistingOnCreate bool, desiredDomain string, allowedDomainServiceIDs []int) diag.Diagnostics { +// CreateExistingResourceDiagnosticsWithDomainAndServices — см. +// PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices (supportsSuspend). +func CreateExistingResourceDiagnosticsWithDomainAndServices(ctx context.Context, client *core.UniversalClient, serviceID int, resourceName string, adoptExistingOnCreate bool, desiredDomain string, allowedDomainServiceIDs []int, supportsSuspend bool) diag.Diagnostics { var diags diag.Diagnostics if client == nil { return diags @@ -230,7 +244,7 @@ func CreateExistingResourceDiagnosticsWithDomainAndServices(ctx context.Context, } diags.AddError( "РЕСУРС С ТАКИМ ИМЕНЕМ УЖЕ СУЩЕСТВУЕТ (RUNNING)", - "Инстанс с таким resource_name уже работает.\nАвто-усыновление отключено (adopt_existing_on_create=false).\nВыберите другое имя, если нужен новый ресурс.\nИли выполните импорт существующего ресурса через усыновление: установите adopt_existing_on_create=true и повторите apply."+formatInstanceDetails(existing, serviceID, resourceName), + "Инстанс с таким resource_name уже работает.\nАвто-усыновление отключено (adopt_existing_on_create=false).\nВыберите другое имя, если нужен новый ресурс."+runningConflictHint(supportsSuspend)+formatInstanceDetails(existing, serviceID, resourceName), ) return diags } @@ -240,7 +254,7 @@ func CreateExistingResourceDiagnosticsWithDomainAndServices(ctx context.Context, } diags.AddError( "РЕСУРС С ТАКИМ ИМЕНЕМ УЖЕ СУЩЕСТВУЕТ (SUSPEND)", - "Инстанс найден в состоянии Suspend, но adopt_existing_on_create=false.\nДля восстановления/импорта этим ресурсом включите adopt_existing_on_create=true и повторите apply."+formatInstanceDetails(existing, serviceID, resourceName), + "Инстанс найден в состоянии Suspend, но adopt_existing_on_create=false."+suspendConflictHint(supportsSuspend)+formatInstanceDetails(existing, serviceID, resourceName), ) return diags } @@ -252,6 +266,30 @@ func CreateExistingResourceDiagnosticsWithDomainAndServices(ctx context.Context, return diags } +// runningConflictHint — что предлагать, если инстанс с таким именем уже работает. +// +// Для сервисов БЕЗ suspend/resume усыновление работающего инстанса технически возможно +// (приостановка не требуется — adopt просто берёт существующий uid), но пользователю +// часто нужен именно destroy, поэтому называем оба пути явно. +func runningConflictHint(supportsSuspend bool) string { + if !supportsSuspend { + return "\nЛибо усыновите существующий инстанс: adopt_existing_on_create=true (он уже работает, suspend не требуется).\nЛибо удалите инстанс в Личном кабинете Nubes и повторите apply." + } + return "\nИли выполните импорт существующего ресурса через усыновление: установите adopt_existing_on_create=true и повторите apply." +} + +// suspendConflictHint — что предлагать, если инстанс найден в состоянии Suspend. +// +// Для сервисов БЕЗ suspend/resume состояние Suspend недостижимо, а adopt требует +// resume — значит совет «включите adopt_existing_on_create=true» заведомо невыполним. +// Корректный выход один: удалить инстанс и/или взять другое имя. +func suspendConflictHint(supportsSuspend bool) string { + if !supportsSuspend { + return "\nСервис НЕ поддерживает suspend/resume, поэтому усыновление (adopt) для него невозможно.\nУдалите инстанс в Личном кабинете Nubes и повторите apply, либо выберите другое resource_name." + } + return "\nДля восстановления/импорта этим ресурсом включите adopt_existing_on_create=true и повторите apply." +} + func appendLocalPlanInputDiagnostics(serviceID int, desiredParams map[int]string, desiredDomain string, diags *diag.Diagnostics) { if diags == nil { return