@@ -69,7 +69,19 @@ type DomainCreateModel struct {
6969
7070// DomainDestroyModel describes domain shutdown behavior
7171type DomainDestroyModel struct {
72- Graceful types.Bool `tfsdk:"graceful"`
72+ Graceful types.Bool `tfsdk:"graceful"`
73+ Shutdown types.Object `tfsdk:"shutdown"`
74+ }
75+
76+ // DomainDestroyShutdownModel describes optional shutdown wait behavior.
77+ type DomainDestroyShutdownModel struct {
78+ Timeout types.Int64 `tfsdk:"timeout"`
79+ }
80+
81+ type domainDestroyOptions struct {
82+ Flags golibvirt.DomainDestroyFlagsValues
83+ ShutdownEnabled bool
84+ ShutdownTimeout time.Duration
7385}
7486
7587type domainPlanData struct {
@@ -446,6 +458,42 @@ func domainDestroyFlagsFromDestroy(ctx context.Context, destroyVal types.Object)
446458 return flags , nil
447459}
448460
461+ func domainDestroyOptionsFromDestroy (ctx context.Context , destroyVal types.Object ) (domainDestroyOptions , diag.Diagnostics ) {
462+ flags , diags := domainDestroyFlagsFromDestroy (ctx , destroyVal )
463+ options := domainDestroyOptions {
464+ Flags : flags ,
465+ }
466+ if diags .HasError () || destroyVal .IsNull () || destroyVal .IsUnknown () {
467+ return options , diags
468+ }
469+
470+ var destroyModel DomainDestroyModel
471+ diags = destroyVal .As (ctx , & destroyModel , basetypes.ObjectAsOptions {})
472+ if diags .HasError () {
473+ return options , diags
474+ }
475+
476+ if ! destroyModel .Shutdown .IsNull () && ! destroyModel .Shutdown .IsUnknown () {
477+ options .ShutdownEnabled = true
478+ options .ShutdownTimeout = 30 * time .Second
479+
480+ var shutdownModel DomainDestroyShutdownModel
481+ diags = destroyModel .Shutdown .As (ctx , & shutdownModel , basetypes.ObjectAsOptions {})
482+ if diags .HasError () {
483+ return options , diags
484+ }
485+
486+ if ! shutdownModel .Timeout .IsNull () && ! shutdownModel .Timeout .IsUnknown () {
487+ timeout := shutdownModel .Timeout .ValueInt64 ()
488+ if timeout > 0 {
489+ options .ShutdownTimeout = time .Duration (timeout ) * time .Second
490+ }
491+ }
492+ }
493+
494+ return options , nil
495+ }
496+
449497// Metadata returns the resource type name
450498func (r * DomainResource ) Metadata (ctx context.Context , req resource.MetadataRequest , resp * resource.MetadataResponse ) {
451499 resp .TypeName = req .ProviderTypeName + "_domain"
@@ -479,7 +527,20 @@ func (r *DomainResource) Schema(ctx context.Context, req resource.SchemaRequest,
479527 Description : "Destroy behavior when Terraform removes the domain." ,
480528 Optional : true ,
481529 Attributes : map [string ]schema.Attribute {
482- "graceful" : schema.BoolAttribute {Optional : true },
530+ "graceful" : schema.BoolAttribute {
531+ Description : "Experimental: request graceful behavior when using DomainDestroyFlags during domain stop. Subject to change in future releases." ,
532+ Optional : true ,
533+ },
534+ "shutdown" : schema.SingleNestedAttribute {
535+ Description : "Experimental: request a guest shutdown and wait for shutoff before undefine. Subject to change in future releases." ,
536+ Optional : true ,
537+ Attributes : map [string ]schema.Attribute {
538+ "timeout" : schema.Int64Attribute {
539+ Description : "Experimental: seconds to wait for guest shutdown before failing destroy. Defaults to 30." ,
540+ Optional : true ,
541+ },
542+ },
543+ },
483544 },
484545 },
485546 }
@@ -1015,7 +1076,7 @@ func (r *DomainResource) Delete(ctx context.Context, req resource.DeleteRequest,
10151076 return
10161077 }
10171078
1018- destroyFlags , destroyDiags := domainDestroyFlagsFromDestroy (ctx , state .Destroy )
1079+ destroyOptions , destroyDiags := domainDestroyOptionsFromDestroy (ctx , state .Destroy )
10191080 resp .Diagnostics .Append (destroyDiags ... )
10201081 if resp .Diagnostics .HasError () {
10211082 return
@@ -1040,13 +1101,33 @@ func (r *DomainResource) Delete(ctx context.Context, req resource.DeleteRequest,
10401101
10411102 // DomainState values: 0=nostate, 1=running, 2=blocked, 3=paused, 4=shutdown, 5=shutoff, 6=crashed, 7=pmsuspended
10421103 if uint32 (domainState ) == uint32 (golibvirt .DomainRunning ) {
1043- err = r .client .Libvirt ().DomainDestroyFlags (domain , destroyFlags )
1044- if err != nil {
1045- resp .Diagnostics .AddError (
1046- "Failed to Destroy Domain" ,
1047- "Failed to stop running domain: " + err .Error (),
1048- )
1049- return
1104+ if destroyOptions .ShutdownEnabled {
1105+ err = r .client .Libvirt ().DomainShutdown (domain )
1106+ if err != nil {
1107+ resp .Diagnostics .AddError (
1108+ "Failed to Shutdown Domain" ,
1109+ "Failed to request guest shutdown: " + err .Error (),
1110+ )
1111+ return
1112+ }
1113+
1114+ err = waitForDomainState (r .client , domain , uint32 (golibvirt .DomainShutoff ), destroyOptions .ShutdownTimeout )
1115+ if err != nil {
1116+ resp .Diagnostics .AddError (
1117+ "Timeout Waiting for Domain Shutdown" ,
1118+ fmt .Sprintf ("Domain did not reach shutoff state within %s: %s" , destroyOptions .ShutdownTimeout , err ),
1119+ )
1120+ return
1121+ }
1122+ } else {
1123+ err = r .client .Libvirt ().DomainDestroyFlags (domain , destroyOptions .Flags )
1124+ if err != nil {
1125+ resp .Diagnostics .AddError (
1126+ "Failed to Destroy Domain" ,
1127+ "Failed to stop running domain: " + err .Error (),
1128+ )
1129+ return
1130+ }
10501131 }
10511132 }
10521133
0 commit comments