diff --git a/docs/resources/oxide_instance.md b/docs/resources/oxide_instance.md index 05b0e0f..2d36d7c 100644 --- a/docs/resources/oxide_instance.md +++ b/docs/resources/oxide_instance.md @@ -6,7 +6,7 @@ page_title: "oxide_instance Resource - terraform-provider-oxide" This resource manages instances. --> Boot disk updates will stop and reboot the instance. +-> Boot disk, disk attachment, and network interface updates will stop and reboot the instance. -> When setting a boot disk, the boot disk ID should also be included as part of `disk_attachments`. diff --git a/internal/provider/resource_instance.go b/internal/provider/resource_instance.go index 518c7e7..6c55314 100644 --- a/internal/provider/resource_instance.go +++ b/internal/provider/resource_instance.go @@ -568,6 +568,28 @@ func (r *instanceResource) Update(ctx context.Context, req resource.UpdateReques ctx, cancel := context.WithTimeout(ctx, updateTimeout) defer cancel() + // The instance must be stopped for all updates + stopParams := oxide.InstanceStopParams{ + Instance: oxide.NameOrId(state.ID.ValueString()), + } + _, err := r.client.InstanceStop(ctx, stopParams) + if err != nil { + if !is404(err) { + resp.Diagnostics.AddError( + "Unable to stop instance:", + "API error: "+err.Error(), + ) + return + } + } + + diags = waitForInstanceStop(ctx, r.client, updateTimeout, state.ID.ValueString()) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + tflog.Trace(ctx, fmt.Sprintf("stopped instance with ID: %v", state.ID.ValueString()), map[string]any{"success": true}) + // Update disk attachments // // We attach new disks first in case the new boot disk is one of the newly added @@ -584,26 +606,6 @@ func (r *instanceResource) Update(ctx context.Context, req resource.UpdateReques // Update instance only if boot_disk_id changes if state.BootDiskID != plan.BootDiskID { - stopParams := oxide.InstanceStopParams{ - Instance: oxide.NameOrId(state.ID.ValueString()), - } - _, err := r.client.InstanceStop(ctx, stopParams) - if err != nil { - if !is404(err) { - resp.Diagnostics.AddError( - "Unable to stop instance:", - "API error: "+err.Error(), - ) - return - } - } - - diags = waitForInstanceStop(ctx, r.client, updateTimeout, state.ID.ValueString()) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - tflog.Trace(ctx, fmt.Sprintf("stopped instance with ID: %v", state.ID.ValueString()), map[string]any{"success": true}) params := oxide.InstanceUpdateParams{ Instance: oxide.NameOrId(state.ID.ValueString()), @@ -620,7 +622,9 @@ func (r *instanceResource) Update(ctx context.Context, req resource.UpdateReques return } - tflog.Trace(ctx, fmt.Sprintf("updated instance with ID: %v", instance.Id), map[string]any{"success": true}) + tflog.Trace(ctx, fmt.Sprintf( + "updated boot disk forinstance with ID: %v", instance.Id), map[string]any{"success": true}, + ) } // Check state and if it has an ID that the plan doesn't then detach it @@ -651,6 +655,18 @@ func (r *instanceResource) Update(ctx context.Context, req resource.UpdateReques return } + startParams := oxide.InstanceStartParams{Instance: oxide.NameOrId(state.ID.ValueString())} + _, err = r.client.InstanceStart(ctx, startParams) + if err != nil { + if !is404(err) { + resp.Diagnostics.AddError( + "Unable to start instance:", + "API error: "+err.Error(), + ) + return + } + } + // Read instance to retrieve modified time value if this is the only update we are doing params := oxide.InstanceViewParams{ Instance: oxide.NameOrId(state.ID.ValueString()), @@ -764,8 +780,11 @@ func waitForInstanceStop(ctx context.Context, client *oxide.Client, timeout time Pending: []string{ string(oxide.InstanceStateCreating), string(oxide.InstanceStateStarting), + string(oxide.InstanceStateRunning), string(oxide.InstanceStateStopping), string(oxide.InstanceStateRebooting), + string(oxide.InstanceStateMigrating), + string(oxide.InstanceStateRepairing), }, Target: []string{string(oxide.InstanceStateStopped)}, Timeout: timeout, diff --git a/internal/provider/resource_instance_test.go b/internal/provider/resource_instance_test.go index 8ae820c..e314dc5 100644 --- a/internal/provider/resource_instance_test.go +++ b/internal/provider/resource_instance_test.go @@ -477,7 +477,7 @@ resource "oxide_instance" "{{.BlockName}}" { host_name = "terraform-acc-myhost" memory = 1073741824 ncpus = 1 - start_on_create = false + start_on_create = true disk_attachments = [oxide_disk.{{.DiskBlockName}}.id, oxide_disk.{{.DiskBlockName2}}.id] } ` @@ -511,7 +511,7 @@ resource "oxide_instance" "{{.BlockName}}" { host_name = "terraform-acc-myhost" memory = 1073741824 ncpus = 1 - start_on_create = false + start_on_create = true disk_attachments = [oxide_disk.{{.DiskBlockName}}.id] } ` @@ -658,7 +658,7 @@ func checkResourceInstanceDisk(resourceName, instanceName string) resource.TestC resource.TestCheckResourceAttr(resourceName, "host_name", "terraform-acc-myhost"), resource.TestCheckResourceAttr(resourceName, "memory", "1073741824"), resource.TestCheckResourceAttr(resourceName, "ncpus", "1"), - resource.TestCheckResourceAttr(resourceName, "start_on_create", "false"), + resource.TestCheckResourceAttr(resourceName, "start_on_create", "true"), resource.TestCheckResourceAttrSet(resourceName, "disk_attachments.0"), resource.TestCheckResourceAttrSet(resourceName, "project_id"), resource.TestCheckResourceAttrSet(resourceName, "time_created"), @@ -675,7 +675,7 @@ func checkResourceInstanceDiskUpdate(resourceName, instanceName string) resource resource.TestCheckResourceAttr(resourceName, "host_name", "terraform-acc-myhost"), resource.TestCheckResourceAttr(resourceName, "memory", "1073741824"), resource.TestCheckResourceAttr(resourceName, "ncpus", "1"), - resource.TestCheckResourceAttr(resourceName, "start_on_create", "false"), + resource.TestCheckResourceAttr(resourceName, "start_on_create", "true"), resource.TestCheckResourceAttrSet(resourceName, "project_id"), resource.TestCheckResourceAttrSet(resourceName, "time_created"), resource.TestCheckResourceAttrSet(resourceName, "time_modified"),