Skip to content

Commit

Permalink
Merge pull request #526 from MusicDin/feat/instance-rename
Browse files Browse the repository at this point in the history
Allow instance rename
  • Loading branch information
MusicDin authored Sep 20, 2024
2 parents ee4297e + 4e5f92b commit eeff26a
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 4 deletions.
39 changes: 35 additions & 4 deletions internal/instance/resource_instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,6 @@ func (r InstanceResource) Schema(ctx context.Context, _ resource.SchemaRequest,
Attributes: map[string]schema.Attribute{
"name": schema.StringAttribute{
Required: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},

"description": schema.StringAttribute{
Expand Down Expand Up @@ -789,13 +786,25 @@ func (r InstanceResource) Update(ctx context.Context, req resource.UpdateRequest
return
}

instanceName := plan.Name.ValueString()
instanceName := state.Name.ValueString()
instanceState, _, err := server.GetInstanceState(instanceName)
if err != nil {
resp.Diagnostics.AddError(fmt.Sprintf("Failed to retrieve state of instance %q", instanceName), err.Error())
return
}

// Handle instance rename if instance is already stopped.
newInstanceName := plan.Name.ValueString()
if instanceName != newInstanceName && isInstanceStopped(*instanceState) {
err := renameInstance(ctx, server, instanceName, newInstanceName)
if err != nil {
resp.Diagnostics.AddError(fmt.Sprintf("Failed to rename instance %q", instanceName), err.Error())
return
}

instanceName = newInstanceName
}

// Indicates if the instance has been just started.
instanceStarted := false
instanceRunning := isInstanceOperational(*instanceState)
Expand Down Expand Up @@ -832,6 +841,17 @@ func (r InstanceResource) Update(ctx context.Context, req resource.UpdateRequest
}
}

// Handle instance rename.
if instanceName != newInstanceName {
err := renameInstance(ctx, server, instanceName, newInstanceName)
if err != nil {
resp.Diagnostics.AddError(fmt.Sprintf("Failed to rename instance %q", instanceName), err.Error())
return
}

instanceName = newInstanceName
}

// Get instance.
instance, etag, err := server.GetInstance(instanceName)
if err != nil {
Expand Down Expand Up @@ -1307,6 +1327,17 @@ func stopInstance(ctx context.Context, server lxd.InstanceServer, instanceName s
return true, nil
}

// renameInstance renames an instance with the given old name to a new name.
// Instance has to be stopped beforehand, otherwise the operation will fail.
func renameInstance(ctx context.Context, server lxd.InstanceServer, oldName string, newName string) error {
op, err := server.RenameInstance(oldName, api.InstancePost{Name: newName})
if err != nil {
return err
}

return op.WaitContext(ctx)
}

// waitInstanceNetwork waits for an instance with the given name to receive
// an IPv4 address on any interface (excluding loopback). This should be
// called only if the instance is running.
Expand Down
66 changes: 66 additions & 0 deletions internal/instance/resource_instance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,62 @@ func TestAccInstance_restartVirtualMachine(t *testing.T) {
})
}

func TestAccInstance_renameInstance(t *testing.T) {
instanceNameA := acctest.GenerateName(3, "-")
instanceNameB := acctest.GenerateName(3, "-")
instanceNameC := acctest.GenerateName(3, "-")
instanceNameD := acctest.GenerateName(3, "-")
instanceNameE := acctest.GenerateName(3, "-")

resource.Test(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(t) },
ProtoV6ProviderFactories: acctest.ProtoV6ProviderFactories,
Steps: []resource.TestStep{
{
// Launch a new instance.
Config: testAccInstance_rename(instanceNameA, true),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("lxd_instance.instance1", "name", instanceNameA),
resource.TestCheckResourceAttr("lxd_instance.instance1", "status", "Running"),
),
},
{
// Stop and rename the instance.
Config: testAccInstance_rename(instanceNameB, false),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("lxd_instance.instance1", "name", instanceNameB),
resource.TestCheckResourceAttr("lxd_instance.instance1", "status", "Stopped"),
),
},
{
// Rename the instance while stopped.
Config: testAccInstance_rename(instanceNameC, false),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("lxd_instance.instance1", "name", instanceNameC),
resource.TestCheckResourceAttr("lxd_instance.instance1", "status", "Stopped"),
),
},
{
// Rename and start the instance.
Config: testAccInstance_rename(instanceNameD, true),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("lxd_instance.instance1", "name", instanceNameD),
resource.TestCheckResourceAttr("lxd_instance.instance1", "status", "Running"),
),
},
{
// Ensure instance rename fails when instance is running.
Config: testAccInstance_rename(instanceNameE, true),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("lxd_instance.instance1", "name", instanceNameD), // Ensure name is unchanged.
resource.TestCheckResourceAttr("lxd_instance.instance1", "status", "Running"),
),
ExpectError: regexp.MustCompile("Renaming of running instance not allowed"),
},
},
})
}

func TestAccInstance_remoteImage(t *testing.T) {
instanceName := acctest.GenerateName(2, "-")

Expand Down Expand Up @@ -1884,6 +1940,16 @@ resource "lxd_instance" "instance1" {
`, instanceName, acctest.TestImage)
}

func testAccInstance_rename(name string, running bool) string {
return fmt.Sprintf(`
resource "lxd_instance" "instance1" {
name = "%s"
running = %v
image = "%s"
}
`, name, running, acctest.TestImage)
}

func testAccInstance_remoteImage(name string) string {
return fmt.Sprintf(`
resource "lxd_instance" "instance1" {
Expand Down

0 comments on commit eeff26a

Please sign in to comment.