diff --git a/libvirt/resource_libvirt_volume.go b/libvirt/resource_libvirt_volume.go index dbe897b95..c6521af5f 100644 --- a/libvirt/resource_libvirt_volume.go +++ b/libvirt/resource_libvirt_volume.go @@ -121,13 +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") } @@ -158,6 +152,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 @@ -270,6 +270,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) } diff --git a/libvirt/resource_libvirt_volume_test.go b/libvirt/resource_libvirt_volume_test.go index cfe34fb30..6210f5f3f 100644 --- a/libvirt/resource_libvirt_volume_test.go +++ b/libvirt/resource_libvirt_volume_test.go @@ -6,6 +6,7 @@ import ( "fmt" "os" "path/filepath" + "regexp" "testing" libvirt "github.com/digitalocean/go-libvirt" @@ -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) diff --git a/website/docs/r/volume.html.markdown b/website/docs/r/volume.html.markdown index 8534f4d6b..ec9cf0494 100644 --- a/website/docs/r/volume.html.markdown +++ b/website/docs/r/volume.html.markdown @@ -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