Skip to content

Commit bc02889

Browse files
committed
Allow resizing the source image for a volume
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.
1 parent 11dbcbf commit bc02889

File tree

3 files changed

+141
-10
lines changed

3 files changed

+141
-10
lines changed

libvirt/resource_libvirt_volume.go

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -121,13 +121,7 @@ func resourceLibvirtVolumeCreate(ctx context.Context, d *schema.ResourceData, me
121121
}
122122

123123
var img image
124-
// an source image was given, this mean we can't choose size
125124
if source, ok := d.GetOk("source"); ok {
126-
// source and size conflict
127-
if _, ok := d.GetOk("size"); ok {
128-
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")
129-
}
130-
131125
if _, ok := d.GetOk("base_volume_id"); ok {
132126
return diag.Errorf("'base_volume_id' can't be specified when also 'source' is given")
133127
}
@@ -158,6 +152,12 @@ func resourceLibvirtVolumeCreate(ctx context.Context, d *schema.ResourceData, me
158152
return diag.FromErr(err)
159153
}
160154

155+
if desiredSize, ok := d.GetOk("size"); ok {
156+
if uint64(desiredSize.(int)) < size {
157+
return diag.Errorf("'size' can't be smaller than the size of the 'source'")
158+
}
159+
}
160+
161161
log.Printf("Image %s image is: %d bytes", img, size)
162162
volumeDef.Capacity.Unit = "B"
163163
volumeDef.Capacity.Value = size
@@ -270,6 +270,18 @@ be smaller than the backing store specified with
270270
return diag.FromErr(err)
271271
}
272272

273+
if _, ok := d.GetOk("source"); ok {
274+
if desiredSize, ok := d.GetOk("size"); ok {
275+
err = virConn.StorageVolResize(volume, uint64(desiredSize.(int)), 0)
276+
if err != nil {
277+
// don't save volume ID in case of error. This will taint the volume after.
278+
// If we don't throw away the id, we will keep instead a broken volume.
279+
d.Set("id", "")
280+
return diag.Errorf("error while resizing image %s: %s", volume.Name, err)
281+
}
282+
}
283+
}
284+
273285
return resourceLibvirtVolumeRead(ctx, d, meta)
274286
}
275287

libvirt/resource_libvirt_volume_test.go

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"fmt"
77
"os"
88
"path/filepath"
9+
"regexp"
910
"testing"
1011

1112
libvirt "github.com/digitalocean/go-libvirt"
@@ -408,6 +409,125 @@ func TestAccLibvirtVolume_DownloadFromSourceFormat(t *testing.T) {
408409
})
409410
}
410411

412+
func TestAccLibvirtVolume_DownloadFromSourceSize(t *testing.T) {
413+
size := 128 * 1024 * 1024
414+
var volumeRaw libvirt.StorageVol
415+
var volumeQCOW2 libvirt.StorageVol
416+
randomVolumeNameRaw := acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)
417+
randomVolumeNameQCOW := acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)
418+
randomVolumeResourceRaw := acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)
419+
randomVolumeResourceQCOW := acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)
420+
randomPoolName := acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)
421+
randomPoolPath := "/tmp/terraform-provider-libvirt-pool-" + randomPoolName
422+
qcow2Path, err := filepath.Abs("testdata/test.qcow2")
423+
if err != nil {
424+
t.Fatal(err)
425+
}
426+
427+
rawPath, err := filepath.Abs("testdata/initrd.img")
428+
if err != nil {
429+
t.Fatal(err)
430+
}
431+
432+
config := fmt.Sprintf(`
433+
resource "libvirt_pool" "%s" {
434+
name = "%s"
435+
type = "dir"
436+
path = "%s"
437+
}
438+
resource "libvirt_volume" "%s" {
439+
name = "%s"
440+
source = "%s"
441+
size = %d
442+
pool = "${libvirt_pool.%s.name}"
443+
}
444+
resource "libvirt_volume" "%s" {
445+
name = "%s"
446+
source = "%s"
447+
size = %d
448+
pool = "${libvirt_pool.%s.name}"
449+
}`, randomPoolName, randomPoolName, randomPoolPath,
450+
randomVolumeResourceRaw, randomVolumeNameRaw, fmt.Sprintf("file://%s", rawPath), size, randomPoolName,
451+
randomVolumeResourceQCOW, randomVolumeNameQCOW, fmt.Sprintf("file://%s", qcow2Path), size, randomPoolName)
452+
resource.Test(t, resource.TestCase{
453+
PreCheck: func() { testAccPreCheck(t) },
454+
Providers: testAccProviders,
455+
CheckDestroy: testAccCheckLibvirtVolumeDestroy,
456+
Steps: []resource.TestStep{
457+
{
458+
Config: config,
459+
Check: resource.ComposeTestCheckFunc(
460+
testAccCheckLibvirtVolumeExists("libvirt_volume."+randomVolumeResourceRaw, &volumeRaw),
461+
testAccCheckLibvirtVolumeExists("libvirt_volume."+randomVolumeResourceQCOW, &volumeQCOW2),
462+
resource.TestCheckResourceAttr(
463+
"libvirt_volume."+randomVolumeResourceRaw, "name", randomVolumeNameRaw),
464+
resource.TestCheckResourceAttr(
465+
"libvirt_volume."+randomVolumeResourceRaw, "format", "raw"),
466+
resource.TestCheckResourceAttr(
467+
"libvirt_volume."+randomVolumeResourceRaw, "size", fmt.Sprintf("%d", size)),
468+
resource.TestCheckResourceAttr(
469+
"libvirt_volume."+randomVolumeResourceQCOW, "name", randomVolumeNameQCOW),
470+
resource.TestCheckResourceAttr(
471+
"libvirt_volume."+randomVolumeResourceQCOW, "format", "qcow2"),
472+
resource.TestCheckResourceAttr(
473+
"libvirt_volume."+randomVolumeResourceQCOW, "size", fmt.Sprintf("%d", size)),
474+
),
475+
},
476+
},
477+
})
478+
}
479+
480+
func TestAccLibvirtVolume_DownloadFromSourceSize_TooSmall(t *testing.T) {
481+
size := 16
482+
randomVolumeNameRaw := acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)
483+
randomVolumeNameQCOW := acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)
484+
randomVolumeResourceRaw := acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)
485+
randomVolumeResourceQCOW := acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)
486+
randomPoolName := acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)
487+
randomPoolPath := "/tmp/terraform-provider-libvirt-pool-" + randomPoolName
488+
qcow2Path, err := filepath.Abs("testdata/test.qcow2")
489+
if err != nil {
490+
t.Fatal(err)
491+
}
492+
493+
rawPath, err := filepath.Abs("testdata/initrd.img")
494+
if err != nil {
495+
t.Fatal(err)
496+
}
497+
498+
config := fmt.Sprintf(`
499+
resource "libvirt_pool" "%s" {
500+
name = "%s"
501+
type = "dir"
502+
path = "%s"
503+
}
504+
resource "libvirt_volume" "%s" {
505+
name = "%s"
506+
source = "%s"
507+
size = %d
508+
pool = "${libvirt_pool.%s.name}"
509+
}
510+
resource "libvirt_volume" "%s" {
511+
name = "%s"
512+
source = "%s"
513+
size = %d
514+
pool = "${libvirt_pool.%s.name}"
515+
}`, randomPoolName, randomPoolName, randomPoolPath,
516+
randomVolumeResourceRaw, randomVolumeNameRaw, fmt.Sprintf("file://%s", rawPath), size, randomPoolName,
517+
randomVolumeResourceQCOW, randomVolumeNameQCOW, fmt.Sprintf("file://%s", qcow2Path), size, randomPoolName)
518+
resource.Test(t, resource.TestCase{
519+
PreCheck: func() { testAccPreCheck(t) },
520+
Providers: testAccProviders,
521+
CheckDestroy: testAccCheckLibvirtVolumeDestroy,
522+
Steps: []resource.TestStep{
523+
{
524+
Config: config,
525+
ExpectError: regexp.MustCompile(`'size' can't be smaller than the size of the 'source'`),
526+
},
527+
},
528+
})
529+
}
530+
411531
func TestAccLibvirtVolume_Format(t *testing.T) {
412532
var volume libvirt.StorageVol
413533
randomVolumeResource := acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)

website/docs/r/volume.html.markdown

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,9 @@ The following arguments are supported:
5454
have to be specified using HTTP(S) urls for now.
5555
* `size` - (Optional) The size of the volume in bytes (if you don't like this,
5656
help fix [this issue](https://github.com/hashicorp/terraform/issues/3287).
57-
If `source` is specified, `size` will be set to the source image file size.
58-
`size` can be omitted if `source` is specified. `size` will then be set to the source image file size.
59-
`size` can be omitted if `base_volume_id` or `base_volume_name` is specified. `size` will then be set to the base volume size.
60-
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.
57+
If `source` is specified and `size` is omitted, it will be set to the source image file size.
58+
`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.
59+
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.
6160
* `base_volume_id` - (Optional) The backing volume (CoW) to use for this volume.
6261
* `base_volume_name` - (Optional) The name of the backing volume (CoW) to use
6362
for this volume. Note well: when `base_volume_pool` is not specified the

0 commit comments

Comments
 (0)