mirror of
https://github.com/bpg/terraform-provider-proxmox.git
synced 2025-06-30 18:42:58 +00:00
feat(vm): do not force VM re-create on initialization.user_account
changes (#1885)
Signed-off-by: Pavel Boldyrev <627562+bpg@users.noreply.github.com>
This commit is contained in:
parent
51e6d7b8db
commit
d631ccbf90
@ -13,6 +13,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
|
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
|
||||||
|
"github.com/hashicorp/terraform-plugin-testing/plancheck"
|
||||||
|
|
||||||
"github.com/bpg/terraform-provider-proxmox/utils"
|
"github.com/bpg/terraform-provider-proxmox/utils"
|
||||||
)
|
)
|
||||||
@ -479,19 +480,6 @@ func TestAccResourceVMInitialization(t *testing.T) {
|
|||||||
overwrite_unmanaged = true
|
overwrite_unmanaged = true
|
||||||
}`),
|
}`),
|
||||||
}}},
|
}}},
|
||||||
{"native cloud-init: do not upgrade packages", []resource.TestStep{{
|
|
||||||
Config: te.RenderConfig(`
|
|
||||||
resource "proxmox_virtual_environment_vm" "test_vm_cloudinit3" {
|
|
||||||
node_name = "{{.NodeName}}"
|
|
||||||
started = false
|
|
||||||
initialization {
|
|
||||||
upgrade = false
|
|
||||||
}
|
|
||||||
}`),
|
|
||||||
Check: ResourceAttributes("proxmox_virtual_environment_vm.test_vm_cloudinit3", map[string]string{
|
|
||||||
"initialization.0.upgrade": "false",
|
|
||||||
}),
|
|
||||||
}}},
|
|
||||||
{"native cloud-init: username should not change", []resource.TestStep{{
|
{"native cloud-init: username should not change", []resource.TestStep{{
|
||||||
Config: te.RenderConfig(`
|
Config: te.RenderConfig(`
|
||||||
resource "proxmox_virtual_environment_vm" "test_vm_cloudinit4" {
|
resource "proxmox_virtual_environment_vm" "test_vm_cloudinit4" {
|
||||||
@ -547,6 +535,36 @@ func TestAccResourceVMInitialization(t *testing.T) {
|
|||||||
"initialization.0.user_account.0.password": `\*\*\*\*\*\*\*\*\*\*`,
|
"initialization.0.user_account.0.password": `\*\*\*\*\*\*\*\*\*\*`,
|
||||||
}),
|
}),
|
||||||
}}},
|
}}},
|
||||||
|
{"native cloud-init: username update should not cause replacement", []resource.TestStep{{
|
||||||
|
Config: te.RenderConfig(`
|
||||||
|
resource "proxmox_virtual_environment_vm" "test_vm" {
|
||||||
|
node_name = "{{.NodeName}}"
|
||||||
|
started = false
|
||||||
|
initialization {
|
||||||
|
user_account {
|
||||||
|
username = "ubuntu"
|
||||||
|
password = "password"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`),
|
||||||
|
}, {
|
||||||
|
Config: te.RenderConfig(`
|
||||||
|
resource "proxmox_virtual_environment_vm" "test_vm" {
|
||||||
|
node_name = "{{.NodeName}}"
|
||||||
|
started = false
|
||||||
|
initialization {
|
||||||
|
user_account {
|
||||||
|
username = "ubuntu-updated"
|
||||||
|
password = "password"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`),
|
||||||
|
ConfigPlanChecks: resource.ConfigPlanChecks{
|
||||||
|
PreApply: []plancheck.PlanCheck{
|
||||||
|
plancheck.ExpectResourceAction("proxmox_virtual_environment_vm.test_vm", plancheck.ResourceActionUpdate),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
|
@ -290,6 +290,16 @@ func (c *Client) ListVMs(ctx context.Context) ([]*ListResponseData, error) {
|
|||||||
return resBody.Data, nil
|
return resBody.Data, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RebuildCloudInitDisk regenerates and changes cloud-init config drive.
|
||||||
|
func (c *Client) RebuildCloudInitDisk(ctx context.Context) error {
|
||||||
|
err := c.DoRequest(ctx, http.MethodPut, c.ExpandPath("cloudinit"), nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error rebuilding cloud-init drive: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// RebootVM reboots a virtual machine.
|
// RebootVM reboots a virtual machine.
|
||||||
func (c *Client) RebootVM(ctx context.Context, d *RebootRequestBody) error {
|
func (c *Client) RebootVM(ctx context.Context, d *RebootRequestBody) error {
|
||||||
taskID, err := c.RebootVMAsync(ctx, d)
|
taskID, err := c.RebootVMAsync(ctx, d)
|
||||||
|
@ -843,7 +843,6 @@ func VM() *schema.Resource {
|
|||||||
Type: schema.TypeList,
|
Type: schema.TypeList,
|
||||||
Description: "The user account configuration",
|
Description: "The user account configuration",
|
||||||
Optional: true,
|
Optional: true,
|
||||||
ForceNew: true,
|
|
||||||
DefaultFunc: func() (interface{}, error) {
|
DefaultFunc: func() (interface{}, error) {
|
||||||
return []interface{}{}, nil
|
return []interface{}{}, nil
|
||||||
},
|
},
|
||||||
@ -853,14 +852,12 @@ func VM() *schema.Resource {
|
|||||||
Type: schema.TypeList,
|
Type: schema.TypeList,
|
||||||
Description: "The SSH keys",
|
Description: "The SSH keys",
|
||||||
Optional: true,
|
Optional: true,
|
||||||
ForceNew: true,
|
|
||||||
Elem: &schema.Schema{Type: schema.TypeString},
|
Elem: &schema.Schema{Type: schema.TypeString},
|
||||||
},
|
},
|
||||||
mkInitializationUserAccountPassword: {
|
mkInitializationUserAccountPassword: {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Description: "The SSH password",
|
Description: "The SSH password",
|
||||||
Optional: true,
|
Optional: true,
|
||||||
ForceNew: true,
|
|
||||||
Sensitive: true,
|
Sensitive: true,
|
||||||
Default: dvInitializationUserAccountPassword,
|
Default: dvInitializationUserAccountPassword,
|
||||||
DiffSuppressFunc: func(_, oldVal, _ string, _ *schema.ResourceData) bool {
|
DiffSuppressFunc: func(_, oldVal, _ string, _ *schema.ResourceData) bool {
|
||||||
@ -872,7 +869,6 @@ func VM() *schema.Resource {
|
|||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Description: "The SSH username",
|
Description: "The SSH username",
|
||||||
Optional: true,
|
Optional: true,
|
||||||
ForceNew: true,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -4786,7 +4782,7 @@ func vmUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.D
|
|||||||
if d.HasChange(mkNodeName) {
|
if d.HasChange(mkNodeName) {
|
||||||
migrateTimeoutSec := d.Get(mkTimeoutMigrate).(int)
|
migrateTimeoutSec := d.Get(mkTimeoutMigrate).(int)
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(ctx, time.Duration(migrateTimeoutSec)*time.Second)
|
migrateCtx, cancel := context.WithTimeout(ctx, time.Duration(migrateTimeoutSec)*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
oldNodeNameValue, _ := d.GetChange(mkNodeName)
|
oldNodeNameValue, _ := d.GetChange(mkNodeName)
|
||||||
@ -4800,7 +4796,7 @@ func vmUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.D
|
|||||||
OnlineMigration: &trueValue,
|
OnlineMigration: &trueValue,
|
||||||
}
|
}
|
||||||
|
|
||||||
err := vmAPI.MigrateVM(ctx, migrateBody)
|
err := vmAPI.MigrateVM(migrateCtx, migrateBody)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return diag.FromErr(err)
|
return diag.FromErr(err)
|
||||||
}
|
}
|
||||||
@ -5113,11 +5109,12 @@ func vmUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.D
|
|||||||
|
|
||||||
// Prepare the new cloud-init configuration.
|
// Prepare the new cloud-init configuration.
|
||||||
stoppedBeforeUpdate := false
|
stoppedBeforeUpdate := false
|
||||||
|
cloudInitRebuildRequired := false
|
||||||
|
|
||||||
if d.HasChange(mkInitialization) {
|
if d.HasChange(mkInitialization) {
|
||||||
initializationConfig := vmGetCloudInitConfig(d)
|
cloudInitConfig := vmGetCloudInitConfig(d)
|
||||||
|
|
||||||
updateBody.CloudInitConfig = initializationConfig
|
updateBody.CloudInitConfig = cloudInitConfig
|
||||||
|
|
||||||
initialization := d.Get(mkInitialization).([]interface{})
|
initialization := d.Get(mkInitialization).([]interface{})
|
||||||
|
|
||||||
@ -5158,12 +5155,12 @@ func vmUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.D
|
|||||||
if mustMove || mustChangeDatastore || existingInterface == "" {
|
if mustMove || mustChangeDatastore || existingInterface == "" {
|
||||||
// CloudInit must be moved, either from a device to another or from a datastore
|
// CloudInit must be moved, either from a device to another or from a datastore
|
||||||
// to another (or both). This requires the VM to be stopped.
|
// to another (or both). This requires the VM to be stopped.
|
||||||
if err := vmShutdown(ctx, vmAPI, d); err != nil {
|
if er := vmShutdown(ctx, vmAPI, d); er != nil {
|
||||||
return err
|
return er
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := deleteIdeDrives(ctx, vmAPI, initializationInterface, existingInterface); err != nil {
|
if er := deleteIdeDrives(ctx, vmAPI, initializationInterface, existingInterface); er != nil {
|
||||||
return err
|
return er
|
||||||
}
|
}
|
||||||
|
|
||||||
stoppedBeforeUpdate = true
|
stoppedBeforeUpdate = true
|
||||||
@ -5179,6 +5176,7 @@ func vmUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.D
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cloudInitRebuildRequired = true
|
||||||
rebootRequired = true
|
rebootRequired = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5402,14 +5400,20 @@ func vmUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.D
|
|||||||
return diags
|
return diags
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if e := vmShutdown(ctx, vmAPI, d); e != nil {
|
if er := vmShutdown(ctx, vmAPI, d); er != nil {
|
||||||
return e
|
return er
|
||||||
}
|
}
|
||||||
|
|
||||||
rebootRequired = false
|
rebootRequired = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if cloudInitRebuildRequired {
|
||||||
|
if er := vmAPI.RebuildCloudInitDisk(ctx); er != nil {
|
||||||
|
return diag.FromErr(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Change the disk locations and/or sizes, if necessary.
|
// Change the disk locations and/or sizes, if necessary.
|
||||||
return vmUpdateDiskLocationAndSize(
|
return vmUpdateDiskLocationAndSize(
|
||||||
ctx,
|
ctx,
|
||||||
|
Loading…
Reference in New Issue
Block a user