Skip to content

Commit 0c7fad9

Browse files
authored
fix(vm): do not re-import existing disks during update (#2401)
The import_from attribute should only be used during initial disk creation, not during updates. This reverts the re-import logic introduced in v0.88.0 and restores the behavior from v0.87.0 where import_from is only used for initial disk creation. Signed-off-by: Pavel Boldyrev <627562+bpg@users.noreply.github.com>
1 parent 4b5652f commit 0c7fad9

File tree

2 files changed

+67
-40
lines changed

2 files changed

+67
-40
lines changed

proxmoxtf/resource/vm/disk/disk.go

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -631,12 +631,8 @@ func Update(
631631
tmp.AIO = disk.AIO
632632
}
633633

634-
if disk.ImportFrom != nil && *disk.ImportFrom != "" {
635-
shutdownBeforeUpdate = true
636-
tmp.DatastoreID = disk.DatastoreID
637-
tmp.ImportFrom = disk.ImportFrom
638-
tmp.Size = disk.Size
639-
}
634+
// Never re-import existing disks - import_from is only for initial disk creation.
635+
// See https://github.com/bpg/terraform-provider-proxmox/issues/2385
640636

641637
tmp.Backup = disk.Backup
642638
tmp.BurstableReadSpeedMbps = disk.BurstableReadSpeedMbps
@@ -658,5 +654,7 @@ func Update(
658654
}
659655
}
660656

657+
// shutdownBeforeUpdate is always false now, as we're not updating a boot disk that was imported.
658+
// but let's keep this api / flag in place for future use.
661659
return shutdownBeforeUpdate, rebootRequired, nil
662660
}

proxmoxtf/resource/vm/disk/disk_test.go

Lines changed: 63 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -358,67 +358,96 @@ func TestDiskUpdateSkipsUnchangedDisks(t *testing.T) {
358358
require.NoError(t, err)
359359
require.False(t, shutdownBeforeUpdate)
360360

361-
// Check that only the changed disk (scsi1) is in the update body
362-
// scsi0 should NOT be in the update body since it hasn't changed
363-
require.NotNil(t, updateBody)
364-
365361
// The update body should only contain scsi1, not scsi0
366362
// This prevents the "can't unplug bootdisk 'scsi0'" error
367-
// Note: We can't directly inspect the updateBody content in this test framework,
368-
// but the fact that no error occurred means the logic worked correctly
363+
require.Contains(t, updateBody.CustomStorageDevices, "scsi1", "Update body should contain the changed disk scsi1")
364+
require.NotContains(t, updateBody.CustomStorageDevices, "scsi0", "Update body should not contain the unchanged disk scsi0")
365+
}
369366

370-
// Create plan disks (what terraform wants)
371-
importFrom2 := "local:import/test2.qcow2"
372-
planDisks2 := vms.CustomStorageDevices{
367+
// TestImportFromDiskNotReimportedOnSizeChange tests issue #2385:
368+
// when a disk with import_from is resized in Proxmox GUI, terraform should NOT
369+
// attempt to re-import the disk (which would fail with "cannot shrink" error).
370+
func TestImportFromDiskNotReimportedOnSizeChange(t *testing.T) {
371+
t.Parallel()
372+
373+
diskSchema := Schema()
374+
375+
// Terraform config has a disk with import_from and size=20GB
376+
resourceData := schema.TestResourceDataRaw(t, diskSchema, map[string]any{
377+
MkDisk: []any{
378+
map[string]any{
379+
mkDiskInterface: "scsi0",
380+
mkDiskDatastoreID: "nfs-v3-tmp-01",
381+
mkDiskSize: 20,
382+
mkDiskImportFrom: "nfs-v3-tmp-01:iso/ubuntu-22.04-cloud.qcow2",
383+
mkDiskSpeed: []any{},
384+
},
385+
},
386+
})
387+
388+
resourceData.MarkNewResource()
389+
390+
// Proxmox currently has the disk at 30GB (resized via GUI)
391+
// Note: PVE does NOT return import_from for existing disks
392+
datastoreID := "nfs-v3-tmp-01"
393+
currentDisks := vms.CustomStorageDevices{
373394
"scsi0": &vms.CustomStorageDevice{
374-
Size: types.DiskSizeFromGigabytes(10), // Same as current
395+
Size: types.DiskSizeFromGigabytes(30), // larger than plan (resized in GUI)
375396
DatastoreID: &datastoreID,
376-
ImportFrom: &importFrom2, // Different Import file.
397+
// ImportFrom is nil - PVE doesn't return this for existing disks
377398
},
378-
"scsi1": &vms.CustomStorageDevice{
379-
Size: types.DiskSizeFromGigabytes(5), // Same as current
399+
}
400+
401+
// Plan has the original size from terraform config
402+
importFrom := "nfs-v3-tmp-01:iso/ubuntu-22.04-cloud.qcow2"
403+
planDisks := vms.CustomStorageDevices{
404+
"scsi0": &vms.CustomStorageDevice{
405+
Size: types.DiskSizeFromGigabytes(20), // smaller than current
380406
DatastoreID: &datastoreID,
407+
ImportFrom: &importFrom, // preserved from terraform config
381408
},
382409
}
383410

384-
// Mock update body to capture what gets sent to the API
385-
updateBody2 := &vms.UpdateRequestBody{}
411+
updateBody := &vms.UpdateRequestBody{}
412+
var client proxmox.Client
386413

387-
// Force HasChange to return true by setting old and new values
388-
err = resourceData.Set(MkDisk, []any{
414+
ctx := context.Background()
415+
vmID := 1002
416+
nodeName := "test-node"
417+
418+
// Force HasChange to return true
419+
err := resourceData.Set(MkDisk, []any{
389420
map[string]any{
390-
mkDiskInterface: "scsi1",
391-
mkDiskDatastoreID: "local",
392-
mkDiskSize: 5, // Old size
421+
mkDiskInterface: "scsi0",
422+
mkDiskDatastoreID: "nfs-v3-tmp-01",
423+
mkDiskSize: 30, // current size
393424
mkDiskSpeed: []any{},
394425
},
395426
})
396427
require.NoError(t, err)
397428

398429
err = resourceData.Set(MkDisk, []any{
399430
map[string]any{
400-
mkDiskInterface: "scsi1",
401-
mkDiskDatastoreID: "local",
402-
mkDiskSize: 20, // New size
431+
mkDiskInterface: "scsi0",
432+
mkDiskDatastoreID: "nfs-v3-tmp-01",
433+
mkDiskSize: 20, // plan size
403434
mkDiskSpeed: []any{},
404435
},
405436
})
406437
require.NoError(t, err)
407438

408-
// Call the Update function
409-
shutdownBeforeUpdate, _, err = Update(ctx, client, nodeName, vmID, resourceData, planDisks2, currentDisks, updateBody2)
439+
shutdownBeforeUpdate, _, err := Update(ctx, client, nodeName, vmID, resourceData, planDisks, currentDisks, updateBody)
410440
require.NoError(t, err)
411-
require.True(t, shutdownBeforeUpdate)
412441

413-
// Check that only the changed disk (scsi1) is in the update body
414-
// scsi0 should NOT be in the update body since it hasn't changed
415-
require.NotNil(t, updateBody2)
416-
require.Contains(t, updateBody2.CustomStorageDevices, "scsi0", "Update body should contain the changed disk scsi0")
417-
require.NotContains(t, updateBody2.CustomStorageDevices, "scsi1", "Update body should not contain the unchanged disk scsi1")
418-
require.Equal(t, importFrom2, *updateBody2.CustomStorageDevices["scsi0"].ImportFrom)
442+
// shutdownBeforeUpdate should be false - we should NOT shutdown VM to re-import
443+
require.False(t, shutdownBeforeUpdate,
444+
"should not shutdown VM for disk with import_from when size differs")
419445

420-
// The update body should only contain scsi0, not scsi1
421-
// Note: We can't directly inspect the updateBody content in this test framework,
446+
// the update body should NOT contain ImportFrom - we're updating existing disk, not re-importing
447+
if updateBody.CustomStorageDevices != nil && updateBody.CustomStorageDevices["scsi0"] != nil {
448+
require.Nil(t, updateBody.CustomStorageDevices["scsi0"].ImportFrom,
449+
"should not set ImportFrom when updating existing disk")
450+
}
422451
}
423452

424453
func TestDiskDeletionDetectionInGetDiskDeviceObjects(t *testing.T) {

0 commit comments

Comments
 (0)