Skip to content

Commit

Permalink
Allow resizing the source image for a volume
Browse files Browse the repository at this point in the history
I've put a restriction of only resizing when the desired size is larger
than the original size. Even though libvirt allows shrinking (but only
when VIR_STORAGE_VOL_RESIZE_SHRINK is specified), this is not an
operation that is readily supported by most VM images.
  • Loading branch information
wfdewith committed Oct 27, 2024
1 parent 1cd90b7 commit 7e38270
Show file tree
Hide file tree
Showing 3 changed files with 141 additions and 9 deletions.
23 changes: 18 additions & 5 deletions libvirt/resource_libvirt_volume.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,12 +121,7 @@ func resourceLibvirtVolumeCreate(ctx context.Context, d *schema.ResourceData, me
}

var img image
// an source image was given, this mean we can't choose size
if source, ok := d.GetOk("source"); ok {
// source and size conflict
if _, ok := d.GetOk("size"); ok {
return diag.Errorf("'size' can't be specified when also 'source' is given (the size will be set to the size of the source image")
}
if _, ok := d.GetOk("base_volume_id"); ok {
return diag.Errorf("'base_volume_id' can't be specified when also 'source' is given")
}
Expand Down Expand Up @@ -158,6 +153,12 @@ func resourceLibvirtVolumeCreate(ctx context.Context, d *schema.ResourceData, me
return diag.FromErr(err)
}

if desiredSize, ok := d.GetOk("size"); ok {
if uint64(desiredSize.(int)) < size {
return diag.Errorf("'size' can't be smaller than the size of the 'source'")
}
}

log.Printf("Image %s image is: %d bytes", img, size)
volumeDef.Capacity.Unit = "B"
volumeDef.Capacity.Value = size
Expand Down Expand Up @@ -270,6 +271,18 @@ be smaller than the backing store specified with
return diag.FromErr(err)
}

if _, ok := d.GetOk("source"); ok {
if desiredSize, ok := d.GetOk("size"); ok {
err = virConn.StorageVolResize(volume, uint64(desiredSize.(int)), 0)
if err != nil {
// don't save volume ID in case of error. This will taint the volume after.
// If we don't throw away the id, we will keep instead a broken volume.
d.Set("id", "")
return diag.Errorf("error while resizing image %s: %s", volume.Name, err)
}
}
}

return resourceLibvirtVolumeRead(ctx, d, meta)
}

Expand Down
120 changes: 120 additions & 0 deletions libvirt/resource_libvirt_volume_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"fmt"
"os"
"path/filepath"
"regexp"
"testing"

libvirt "github.com/digitalocean/go-libvirt"
Expand Down Expand Up @@ -408,6 +409,125 @@ func TestAccLibvirtVolume_DownloadFromSourceFormat(t *testing.T) {
})
}

func TestAccLibvirtVolume_DownloadFromSourceSize(t *testing.T) {
size := 128 * 1024 * 1024
var volumeRaw libvirt.StorageVol
var volumeQCOW2 libvirt.StorageVol
randomVolumeNameRaw := acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)
randomVolumeNameQCOW := acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)
randomVolumeResourceRaw := acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)
randomVolumeResourceQCOW := acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)
randomPoolName := acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)
randomPoolPath := "/tmp/terraform-provider-libvirt-pool-" + randomPoolName
qcow2Path, err := filepath.Abs("testdata/test.qcow2")
if err != nil {
t.Fatal(err)
}

rawPath, err := filepath.Abs("testdata/initrd.img")
if err != nil {
t.Fatal(err)
}

config := fmt.Sprintf(`
resource "libvirt_pool" "%s" {
name = "%s"
type = "dir"
path = "%s"
}
resource "libvirt_volume" "%s" {
name = "%s"
source = "%s"
size = %d
pool = "${libvirt_pool.%s.name}"
}
resource "libvirt_volume" "%s" {
name = "%s"
source = "%s"
size = %d
pool = "${libvirt_pool.%s.name}"
}`, randomPoolName, randomPoolName, randomPoolPath,
randomVolumeResourceRaw, randomVolumeNameRaw, fmt.Sprintf("file://%s", rawPath), size, randomPoolName,
randomVolumeResourceQCOW, randomVolumeNameQCOW, fmt.Sprintf("file://%s", qcow2Path), size, randomPoolName)
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckLibvirtVolumeDestroy,
Steps: []resource.TestStep{
{
Config: config,
Check: resource.ComposeTestCheckFunc(
testAccCheckLibvirtVolumeExists("libvirt_volume."+randomVolumeResourceRaw, &volumeRaw),
testAccCheckLibvirtVolumeExists("libvirt_volume."+randomVolumeResourceQCOW, &volumeQCOW2),
resource.TestCheckResourceAttr(
"libvirt_volume."+randomVolumeResourceRaw, "name", randomVolumeNameRaw),
resource.TestCheckResourceAttr(
"libvirt_volume."+randomVolumeResourceRaw, "format", "raw"),
resource.TestCheckResourceAttr(
"libvirt_volume."+randomVolumeResourceRaw, "size", fmt.Sprintf("%d", size)),
resource.TestCheckResourceAttr(
"libvirt_volume."+randomVolumeResourceQCOW, "name", randomVolumeNameQCOW),
resource.TestCheckResourceAttr(
"libvirt_volume."+randomVolumeResourceQCOW, "format", "qcow2"),
resource.TestCheckResourceAttr(
"libvirt_volume."+randomVolumeResourceQCOW, "size", fmt.Sprintf("%d", size)),
),
},
},
})
}

func TestAccLibvirtVolume_DownloadFromSourceSize_TooSmall(t *testing.T) {
size := 16
randomVolumeNameRaw := acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)
randomVolumeNameQCOW := acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)
randomVolumeResourceRaw := acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)
randomVolumeResourceQCOW := acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)
randomPoolName := acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)
randomPoolPath := "/tmp/terraform-provider-libvirt-pool-" + randomPoolName
qcow2Path, err := filepath.Abs("testdata/test.qcow2")
if err != nil {
t.Fatal(err)
}

rawPath, err := filepath.Abs("testdata/initrd.img")
if err != nil {
t.Fatal(err)
}

config := fmt.Sprintf(`
resource "libvirt_pool" "%s" {
name = "%s"
type = "dir"
path = "%s"
}
resource "libvirt_volume" "%s" {
name = "%s"
source = "%s"
size = %d
pool = "${libvirt_pool.%s.name}"
}
resource "libvirt_volume" "%s" {
name = "%s"
source = "%s"
size = %d
pool = "${libvirt_pool.%s.name}"
}`, randomPoolName, randomPoolName, randomPoolPath,
randomVolumeResourceRaw, randomVolumeNameRaw, fmt.Sprintf("file://%s", rawPath), size, randomPoolName,
randomVolumeResourceQCOW, randomVolumeNameQCOW, fmt.Sprintf("file://%s", qcow2Path), size, randomPoolName)
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckLibvirtVolumeDestroy,
Steps: []resource.TestStep{
{
Config: config,
ExpectError: regexp.MustCompile(`'size' can't be smaller than the size of the 'source'`),
},
},
})
}

func TestAccLibvirtVolume_Format(t *testing.T) {
var volume libvirt.StorageVol
randomVolumeResource := acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)
Expand Down
7 changes: 3 additions & 4 deletions website/docs/r/volume.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,9 @@ The following arguments are supported:
have to be specified using HTTP(S) urls for now.
* `size` - (Optional) The size of the volume in bytes (if you don't like this,
help fix [this issue](https://github.com/hashicorp/terraform/issues/3287).
If `source` is specified, `size` will be set to the source image file size.
`size` can be omitted if `source` is specified. `size` will then be set to the source image file size.
`size` can be omitted if `base_volume_id` or `base_volume_name` is specified. `size` will then be set to the base volume size.
If `size` is specified to be bigger than `base_volume_id` or `base_volume_name` size, you can use [cloudinit](https://cloudinit.readthedocs.io) if your OS supports it, with `libvirt_cloudinit_disk` and the [growpart](https://cloudinit.readthedocs.io/en/latest/topics/modules.html#growpart) module to resize the partition.
If `source` is specified and `size` is omitted, it will be set to the source image file size.
`size` can also be omitted if `base_volume_id` or `base_volume_name` is specified. `size` will then be set to the base volume size.
If `size` is specified to be bigger than `base_volume_id`, `base_volume_name` or `source` size, you can use [cloudinit](https://cloudinit.readthedocs.io) if your OS supports it, with `libvirt_cloudinit_disk` and the [growpart](https://cloudinit.readthedocs.io/en/latest/topics/modules.html#growpart) module to resize the partition.
* `base_volume_id` - (Optional) The backing volume (CoW) to use for this volume.
* `base_volume_name` - (Optional) The name of the backing volume (CoW) to use
for this volume. Note well: when `base_volume_pool` is not specified the
Expand Down

0 comments on commit 7e38270

Please sign in to comment.