Skip to content

Commit

Permalink
feat: allow resizing of virtual disks (#2244)
Browse files Browse the repository at this point in the history
Allows the resizing of virtual disks.

- Increasing the size of a disk will no longer recreate it but modify it in-place.
- Reductions in size are not supported by vCenter and will not be allowed.
- The rest of the properties for `r/virtual_disk` are still marked with `ForceNew` and will continue to recreate the disk when changed.

Signed-off-by: Stoyan Zhelyazkov <[email protected]>
  • Loading branch information
spacegospod authored Aug 9, 2024
1 parent 1dd38f0 commit 45fc773
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 6 deletions.
70 changes: 69 additions & 1 deletion vsphere/resource_vsphere_virtual_disk.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ func resourceVSphereVirtualDisk() *schema.Resource {
return &schema.Resource{
Create: resourceVSphereVirtualDiskCreate,
Read: resourceVSphereVirtualDiskRead,
Update: resourceVSphereVirtualDiskUpdate,
Delete: resourceVSphereVirtualDiskDelete,
Importer: &schema.ResourceImporter{
State: resourceVSphereVirtualDiskImport,
Expand All @@ -48,7 +49,6 @@ func resourceVSphereVirtualDisk() *schema.Resource {
"size": {
Type: schema.TypeInt,
Required: true,
ForceNew: true, // TODO Can this be optional (resize)?
},

// TODO:
Expand Down Expand Up @@ -334,6 +334,51 @@ func resourceVSphereVirtualDiskRead(d *schema.ResourceData, meta interface{}) er
return nil
}

func resourceVSphereVirtualDiskUpdate(d *schema.ResourceData, meta interface{}) error {
log.Printf("[INFO] Updating Virtual Disk")
client := meta.(*Client).vimClient

oldSize, newSize := d.GetChange("size")
if newSize.(int) < oldSize.(int) {
return fmt.Errorf("shrinking a virtual disk is not supported")
}

vDisk := virtualDisk{
size: d.Get("size").(int),
}

if v, ok := d.GetOk("vmdk_path"); ok {
vDisk.vmdkPath = v.(string)
}

if v, ok := d.GetOk("datastore"); ok {
vDisk.datastore = v.(string)
}

if v, ok := d.GetOk("datacenter"); ok {
vDisk.datacenter = v.(string)
}

finder := find.NewFinder(client.Client, true)

dc, err := getDatacenter(client, d.Get("datacenter").(string))
if err != nil {
return fmt.Errorf("error finding Datacenter: %s: %s", vDisk.datacenter, err)
}
finder = finder.SetDatacenter(dc)

ds, err := getDatastore(finder, vDisk.datastore)
if err != nil {
return fmt.Errorf("error finding Datastore: %s: %s", vDisk.datastore, err)
}

if err := extendHardDisk(client, vDisk.size, ds.Path(vDisk.vmdkPath), vDisk.datacenter); err != nil {
return err
}

return resourceVSphereVirtualDiskRead(d, meta)
}

func resourceVSphereVirtualDiskDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*Client).vimClient

Expand Down Expand Up @@ -425,6 +470,29 @@ func createHardDisk(client *govmomi.Client, size int, diskPath string, diskType
return nil
}

func extendHardDisk(client *govmomi.Client, capacity int, diskPath string, dc string) error {
virtualDiskManager := object.NewVirtualDiskManager(client.Client)
datacenter, err := getDatacenter(client, dc)
if err != nil {
return err
}

capacityKb := int64(1024 * 1024 * capacity)
task, err := virtualDiskManager.ExtendVirtualDisk(context.TODO(), diskPath, datacenter, capacityKb, nil)
if err != nil {
return err
}

_, err = task.WaitForResultEx(context.TODO(), nil)
if err != nil {
log.Printf("[INFO] Failed to extend disk: %v", err)
return err
}
log.Printf("[INFO] Extended disk.")

return nil
}

// Searches for the presence of a directory path.
func searchForDirectory(client *govmomi.Client, datacenter string, datastore string, directoryPath string) error {
log.Printf("[DEBUG] Searching for Directory")
Expand Down
56 changes: 54 additions & 2 deletions vsphere/resource_vsphere_virtual_disk_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func TestAccResourceVSphereVirtualDisk_basic(t *testing.T) {
CheckDestroy: testAccVSphereVirtualDiskExists("vsphere_virtual_disk.foo", false),
Steps: []resource.TestStep{
{
Config: testacccheckvspherevirtuadiskconfigBasic(rString),
Config: testacccheckvspherevirtualdiskconfigBasic(rString),
Check: resource.ComposeTestCheckFunc(
testAccVSphereVirtualDiskExists("vsphere_virtual_disk.foo", true),
),
Expand All @@ -41,6 +41,36 @@ func TestAccResourceVSphereVirtualDisk_basic(t *testing.T) {
})
}

func TestAccResourceVSphereVirtualDisk_extend(t *testing.T) {
rString := acctest.RandString(5)

resource.Test(t, resource.TestCase{
PreCheck: func() {
RunSweepers()
testAccPreCheck(t)
testAccResourceVSphereVirtualDiskPreCheck(t)
},
Providers: testAccProviders,
CheckDestroy: testAccVSphereVirtualDiskExists("vsphere_virtual_disk.foo", false),
Steps: []resource.TestStep{
{
Config: testacccheckvspherevirtualdiskconfigBasic(rString),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(
"vsphere_virtual_disk.foo", "size", "1"),
),
},
{
Config: testacccheckvspherevirtualdiskconfigExtended(rString),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(
"vsphere_virtual_disk.foo", "size", "2"),
),
},
},
})
}

func TestAccResourceVSphereVirtualDisk_multi(t *testing.T) {
rString := acctest.RandString(5)

Expand Down Expand Up @@ -177,7 +207,7 @@ func testAccCheckVSphereVirtualDiskIsFileNotFoundError(err error) bool {
return false
}

func testacccheckvspherevirtuadiskconfigBasic(rName string) string {
func testacccheckvspherevirtualdiskconfigBasic(rName string) string {
return fmt.Sprintf(`
%s
Expand All @@ -199,6 +229,28 @@ resource "vsphere_virtual_disk" "foo" {
)
}

func testacccheckvspherevirtualdiskconfigExtended(rName string) string {
return fmt.Sprintf(`
%s
variable "rstring" {
default = "%s"
}
resource "vsphere_virtual_disk" "foo" {
size = 2
vmdk_path = "tfTestDisk-${var.rstring}.vmdk"
adapter_type = "lsiLogic"
type = "thin"
datacenter = "${data.vsphere_datacenter.rootdc1.name}"
datastore = vsphere_nas_datastore.ds1.name
}
`,
testhelper.CombineConfigs(testhelper.ConfigDataRootDC1(), testhelper.ConfigDataRootHost1(), testhelper.ConfigDataRootHost2(), testhelper.ConfigResDS1()),
rName,
)
}

func testacccheckvspherevirtuadiskconfigMulti(rName string) string {
return fmt.Sprintf(`
%s
Expand Down
8 changes: 5 additions & 3 deletions website/docs/r/virtual_disk.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,18 @@ resource "vsphere_virtual_disk" "virtual_disk" {

The following arguments are supported:

~> **NOTE:** All fields in the `vsphere_virtual_disk` resource are currently
~> **NOTE:** Some fields in the `vsphere_virtual_disk` resource are currently
immutable and force a new resource if changed.

* `vmdk_path` - (Required) The path, including filename, of the virtual disk to
be created. This needs to end in `.vmdk`.
* `datastore` - (Required) The name of the datastore in which to create the
disk.
* `size` - (Required) Size of the disk (in GB).
* `size` - (Required) Size of the disk (in GB). Decreasing the size of a disk is not possible.
If a disk of a smaller size is required then the original has to be destroyed along with its data and a new one has to be
created.
* `datacenter` - (Optional) The name of the datacenter in which to create the
disk. Can be omitted when when ESXi or if there is only one datacenter in
disk. Can be omitted when ESXi or if there is only one datacenter in
your infrastructure.
* `type` - (Optional) The type of disk to create. Can be one of
`eagerZeroedThick`, `lazy`, or `thin`. Default: `eagerZeroedThick`. For
Expand Down

0 comments on commit 45fc773

Please sign in to comment.