diff options
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | .travis.yml | 1 | ||||
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | libvirt/cloudinit_def.go | 6 | ||||
-rw-r--r-- | libvirt/cloudinit_def_test.go | 83 | ||||
-rw-r--r-- | libvirt/coreos_ignition_def.go | 4 | ||||
-rw-r--r-- | libvirt/disk_def.go | 4 | ||||
-rw-r--r-- | libvirt/disk_def_test.go | 2 | ||||
-rw-r--r-- | libvirt/network_def.go | 2 | ||||
-rw-r--r-- | libvirt/pool_sync.go | 49 | ||||
-rw-r--r-- | libvirt/pool_sync_test.go | 46 | ||||
-rw-r--r-- | libvirt/provider.go | 4 | ||||
-rw-r--r-- | libvirt/qemu_agent.go | 2 | ||||
-rw-r--r-- | libvirt/resource_libvirt_domain.go | 133 | ||||
-rw-r--r-- | libvirt/resource_libvirt_domain_test.go | 68 | ||||
-rw-r--r-- | libvirt/resource_libvirt_volume.go | 8 | ||||
-rw-r--r-- | libvirt/utils.go | 4 | ||||
-rw-r--r-- | libvirt/utils_volume.go | 3 | ||||
-rwxr-xr-x | travis/setup-guest | 2 | ||||
-rw-r--r-- | website/docs/r/domain.html.markdown | 11 |
20 files changed, 271 insertions, 165 deletions
@@ -1,6 +1,8 @@ ./terraform-provider-libvirt +.terraform/* ./*.tf *.tfstate *.tfstate.backup *.test *.cov +terraform-provider-libvirt diff --git a/.travis.yml b/.travis.yml index ab45ae97..1048239f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,6 +12,7 @@ before_script: - sudo lxc exec libvirt -- bash /code/travis/setup-guest script: - bash ./travis/gofmtcheck.sh + - make vet - golint -set_exit_status libvirt/ - sudo lxc exec libvirt -- bash /code/travis/run-tests-inside-guest - sudo lxc file pull libvirt/root/go/src/github.com/dmacvicar/terraform-provider-libvirt/profile.cov . @@ -1,6 +1,6 @@ default: build -build: gofmtcheck +build: gofmtcheck golint vet go build install: diff --git a/libvirt/cloudinit_def.go b/libvirt/cloudinit_def.go index fb148b75..91b73514 100644 --- a/libvirt/cloudinit_def.go +++ b/libvirt/cloudinit_def.go @@ -65,8 +65,8 @@ func (ci *defCloudInit) CreateAndUpload(virConn *libvirt.Connect) (string, error } defer pool.Free() - PoolSync.AcquireLock(ci.PoolName) - defer PoolSync.ReleaseLock(ci.PoolName) + poolMutexKV.Lock(ci.PoolName) + defer poolMutexKV.Unlock(ci.PoolName) // Refresh the pool of the volume so that libvirt knows it is // not longer in use. @@ -160,7 +160,7 @@ func (ci *defCloudInit) createISO() (string, error) { filepath.Join(tmpDir, USERDATA), filepath.Join(tmpDir, METADATA)) - log.Print("About to execute cmd: %+v", cmd) + log.Printf("About to execute cmd: %+v", cmd) if err = cmd.Run(); err != nil { return "", fmt.Errorf("Error while starting the creation of CloudInit's ISO image: %s", err) } diff --git a/libvirt/cloudinit_def_test.go b/libvirt/cloudinit_def_test.go index ab01f4e7..c2202a75 100644 --- a/libvirt/cloudinit_def_test.go +++ b/libvirt/cloudinit_def_test.go @@ -1,12 +1,16 @@ package libvirt import ( + "fmt" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + libvirt "github.com/libvirt/libvirt-go" + "gopkg.in/yaml.v2" "os" "path/filepath" + "regexp" "strings" "testing" - - "gopkg.in/yaml.v2" ) func TestNewCloudInitDef(t *testing.T) { @@ -161,6 +165,81 @@ ssh_authorized_keys: } } +func TestCreateCloudIsoViaPlugin(t *testing.T) { + var volume libvirt.StorageVol + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: func(s *terraform.State) error { + return nil + }, + Steps: []resource.TestStep{ + { + Config: fmt.Sprintf(` + resource "libvirt_cloudinit" "test" { + name = "test.iso" + local_hostname = "tango1" + pool = "default" + user_data = "#cloud-config\nssh_authorized_keys: []\n" + }`), + + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "libvirt_cloudinit.test", "name", "test.iso"), + resource.TestCheckResourceAttr( + "libvirt_cloudinit.test", "local_hostname", "tango1"), + testAccCheckCloudInitVolumeExists("libvirt_cloudinit.test", &volume), + ), + }, + // 2nd tests Invalid userdata + { + Config: fmt.Sprintf(` + resource "libvirt_cloudinit" "test" { + name = "commoninit2.iso" + local_hostname = "samba2" + pool = "default" + user_data = "invalidgino" + }`), + ExpectError: regexp.MustCompile("Error merging UserData with UserDataRaw: yaml: unmarshal errors"), + }, + }, + }) +} + +func testAccCheckCloudInitVolumeExists(n string, volume *libvirt.StorageVol) resource.TestCheckFunc { + return func(s *terraform.State) error { + virConn := testAccProvider.Meta().(*Client).libvirt + + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No libvirt volume key ID is set") + } + + cikey, err := getCloudInitVolumeKeyFromTerraformID(rs.Primary.ID) + retrievedVol, err := virConn.LookupStorageVolByKey(cikey) + if err != nil { + return err + } + realID, err := retrievedVol.GetKey() + if err != nil { + return err + } + + if realID != cikey { + fmt.Printf("realID is: %s \ncloudinit key is %s", realID, cikey) + return fmt.Errorf("Resource ID and cloudinit volume key does not match") + } + + *volume = *retrievedVol + + return nil + } +} + func exists(path string) (bool, error) { _, err := os.Stat(path) if err == nil { diff --git a/libvirt/coreos_ignition_def.go b/libvirt/coreos_ignition_def.go index 4e77b199..bef01d63 100644 --- a/libvirt/coreos_ignition_def.go +++ b/libvirt/coreos_ignition_def.go @@ -38,8 +38,8 @@ func (ign *defIgnition) CreateAndUpload(virConn *libvirt.Connect) (string, error } defer pool.Free() - PoolSync.AcquireLock(ign.PoolName) - defer PoolSync.ReleaseLock(ign.PoolName) + poolMutexKV.Lock(ign.PoolName) + defer poolMutexKV.Unlock(ign.PoolName) // Refresh the pool of the volume so that libvirt knows it is // not longer in use. diff --git a/libvirt/disk_def.go b/libvirt/disk_def.go index 3283f03b..f4e4e86a 100644 --- a/libvirt/disk_def.go +++ b/libvirt/disk_def.go @@ -1,6 +1,7 @@ package libvirt import ( + "fmt" "math/rand" "github.com/libvirt/libvirt-go-xml" @@ -8,12 +9,13 @@ import ( const oui = "05abcd" -func newDefDisk() libvirtxml.DomainDisk { +func newDefDisk(i int) libvirtxml.DomainDisk { return libvirtxml.DomainDisk{ Type: "file", Device: "disk", Target: &libvirtxml.DomainDiskTarget{ Bus: "virtio", + Dev: fmt.Sprintf("vd%s", DiskLetterForIndex(i)), }, Driver: &libvirtxml.DomainDiskDriver{ Name: "qemu", diff --git a/libvirt/disk_def_test.go b/libvirt/disk_def_test.go index 1e3141d5..a6184cc6 100644 --- a/libvirt/disk_def_test.go +++ b/libvirt/disk_def_test.go @@ -13,7 +13,7 @@ func init() { } func TestDefaultDiskMarshall(t *testing.T) { - b := newDefDisk() + b := newDefDisk(0) buf := new(bytes.Buffer) enc := xml.NewEncoder(buf) enc.Indent(" ", " ") diff --git a/libvirt/network_def.go b/libvirt/network_def.go index 77ead849..c53f2896 100644 --- a/libvirt/network_def.go +++ b/libvirt/network_def.go @@ -52,7 +52,7 @@ func newNetworkDef() libvirtxml.Network { </forward> </network>` if d, err := newDefNetworkFromXML(defNetworkXML); err != nil { - panic(fmt.Sprint("Unexpected error while parsing default network definition: %s", err)) + panic(fmt.Sprintf("Unexpected error while parsing default network definition: %s", err)) } else { return d } diff --git a/libvirt/pool_sync.go b/libvirt/pool_sync.go deleted file mode 100644 index a50528bc..00000000 --- a/libvirt/pool_sync.go +++ /dev/null @@ -1,49 +0,0 @@ -package libvirt - -import ( - "sync" -) - -// LVirtPoolSync makes possible to synchronize operations -// against libvirt pools. -// Doing pool.Refresh() operations while uploading or removing -// a volume into the pool causes errors inside of libvirtd -type LVirtPoolSync struct { - PoolLocks map[string]*sync.Mutex - internalMutex sync.Mutex -} - -// NewLVirtPoolSync allocates a new instance of LVirtPoolSync -func NewLVirtPoolSync() LVirtPoolSync { - pool := LVirtPoolSync{} - pool.PoolLocks = make(map[string]*sync.Mutex) - - return pool -} - -// AcquireLock acquires a lock for the specified pool -func (ps LVirtPoolSync) AcquireLock(pool string) { - ps.internalMutex.Lock() - defer ps.internalMutex.Unlock() - - lock, exists := ps.PoolLocks[pool] - if !exists { - lock = new(sync.Mutex) - ps.PoolLocks[pool] = lock - } - - lock.Lock() -} - -// ReleaseLock releases the look for the specified pool -func (ps LVirtPoolSync) ReleaseLock(pool string) { - ps.internalMutex.Lock() - defer ps.internalMutex.Unlock() - - lock, exists := ps.PoolLocks[pool] - if !exists { - return - } - - lock.Unlock() -} diff --git a/libvirt/pool_sync_test.go b/libvirt/pool_sync_test.go deleted file mode 100644 index 90ad4849..00000000 --- a/libvirt/pool_sync_test.go +++ /dev/null @@ -1,46 +0,0 @@ -package libvirt - -import ( - "testing" -) - -func TestAcquireLock(t *testing.T) { - ps := NewLVirtPoolSync() - - ps.AcquireLock("test") - - _, found := ps.PoolLocks["test"] - - if !found { - t.Errorf("lock not found") - } -} - -func TestReleaseLock(t *testing.T) { - ps := NewLVirtPoolSync() - - ps.AcquireLock("test") - - _, found := ps.PoolLocks["test"] - if !found { - t.Errorf("lock not found") - } - - ps.ReleaseLock("test") - _, found = ps.PoolLocks["test"] - if !found { - t.Errorf("lock not found") - } -} - -func TestReleaseNotExistingLock(t *testing.T) { - ps := NewLVirtPoolSync() - - ps.ReleaseLock("test") - _, found := ps.PoolLocks["test"] - if found { - t.Errorf("lock found") - } - // moreover there should be no runtime error because - // we are not trying to unlock a not-locked mutex -} diff --git a/libvirt/provider.go b/libvirt/provider.go index 2cdb3cb3..07b0c72e 100644 --- a/libvirt/provider.go +++ b/libvirt/provider.go @@ -1,10 +1,14 @@ package libvirt import ( + "github.com/hashicorp/terraform/helper/mutexkv" "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/terraform" ) +// Global poolMutexKV +var poolMutexKV = mutexkv.NewMutexKV() + // Provider libvirt func Provider() terraform.ResourceProvider { return &schema.Provider{ diff --git a/libvirt/qemu_agent.go b/libvirt/qemu_agent.go index 82e76685..b884f7c4 100644 --- a/libvirt/qemu_agent.go +++ b/libvirt/qemu_agent.go @@ -50,7 +50,7 @@ func getDomainInterfacesViaQemuAgent(domain Domain, wait4ipv4 bool) []libvirt.Do response := QemuAgentInterfacesResponse{} if err := json.Unmarshal([]byte(result), &response); err != nil { log.Printf("[DEBUG] Error converting Qemu agent response about domain interfaces: %s", err) - log.Printf("[DEBUG] Original message: %s", response) + log.Printf("[DEBUG] Original message: %v", response) log.Print("[DEBUG] Returning an empty list of interfaces") return interfaces } diff --git a/libvirt/resource_libvirt_domain.go b/libvirt/resource_libvirt_domain.go index 6e8830b4..a3b16792 100644 --- a/libvirt/resource_libvirt_domain.go +++ b/libvirt/resource_libvirt_domain.go @@ -7,6 +7,7 @@ import ( "fmt" "log" "net" + "net/url" "os" "strconv" "strings" @@ -25,9 +26,6 @@ type DomainMeta struct { ifaces chan libvirtxml.DomainInterface } -// PoolSync exported pool sync -var PoolSync = NewLVirtPoolSync() - func init() { spew.Config.Indent = "\t" } @@ -232,8 +230,8 @@ func resourceLibvirtDomainCreate(d *schema.ResourceData, meta interface{}) error return err } ignStr := fmt.Sprintf("name=opt/com.coreos/config,file=%s", ignitionKey) - fwCfg = append(fwCfg, libvirtxml.DomainQEMUCommandlineArg{"-fw_cfg"}) - fwCfg = append(fwCfg, libvirtxml.DomainQEMUCommandlineArg{ignStr}) + fwCfg = append(fwCfg, libvirtxml.DomainQEMUCommandlineArg{Value: "-fw_cfg"}) + fwCfg = append(fwCfg, libvirtxml.DomainQEMUCommandlineArg{Value: ignStr}) QemuCmdline := &libvirtxml.DomainQEMUCommandline{ Args: fwCfg, } @@ -367,22 +365,10 @@ func resourceLibvirtDomainCreate(d *schema.ResourceData, meta interface{}) error var disks []libvirtxml.DomainDisk var scsiDisk = false for i := 0; i < disksCount; i++ { - disk := libvirtxml.DomainDisk{ - Type: "file", - Device: "disk", - Target: &libvirtxml.DomainDiskTarget{ - Bus: "virtio", - Dev: fmt.Sprintf("vd%s", DiskLetterForIndex(i)), - }, - Driver: &libvirtxml.DomainDiskDriver{ - Name: "qemu", - Type: "qcow2", - }, - } + disk := newDefDisk(i) diskKey := fmt.Sprintf("disk.%d", i) diskMap := d.Get(diskKey).(map[string]interface{}) - volumeKey := diskMap["volume_id"].(string) if _, ok := diskMap["scsi"].(string); ok { disk.Target.Bus = "scsi" scsiDisk = true @@ -392,23 +378,53 @@ func resourceLibvirtDomainCreate(d *schema.ResourceData, meta interface{}) error disk.WWN = randomWWN(10) } } - diskVolume, err := virConn.LookupStorageVolByKey(volumeKey) - if err != nil { - return fmt.Errorf("Can't retrieve volume %s", volumeKey) - } - diskVolumeFile, err := diskVolume.GetPath() - if err != nil { - return fmt.Errorf("Error retrieving volume file: %s", err) - } - disk.Source = &libvirtxml.DomainDiskSource{ - File: diskVolumeFile, + if _, ok := diskMap["volume_id"].(string); ok { + volumeKey := diskMap["volume_id"].(string) + + diskVolume, err := virConn.LookupStorageVolByKey(volumeKey) + if err != nil { + return fmt.Errorf("Can't retrieve volume %s", volumeKey) + } + diskVolumeFile, err := diskVolume.GetPath() + if err != nil { + return fmt.Errorf("Error retrieving volume file: %s", err) + } + + disk.Source = &libvirtxml.DomainDiskSource{ + File: diskVolumeFile, + } + } else if _, ok := diskMap["url"].(string); ok { + // Support for remote, read-only http disks + // useful for booting CDs + disk.Type = "network" + url, err := url.Parse(diskMap["url"].(string)) + if err != nil { + return err + } + + disk.Source = &libvirtxml.DomainDiskSource{ + Protocol: url.Scheme, + Name: url.Path, + Hosts: []libvirtxml.DomainDiskSourceHost{ + libvirtxml.DomainDiskSourceHost{ + Name: url.Hostname(), + Port: url.Port(), + }, + }, + } + if strings.HasSuffix(url.Path, ".iso") { + disk.Device = "cdrom" + } + if !strings.HasSuffix(url.Path, ".qcow2") { + disk.Driver.Type = "raw" + } } disks = append(disks, disk) } - log.Printf("[DEBUG] scsiDisk: %s", scsiDisk) + log.Printf("[DEBUG] scsiDisk: %t", scsiDisk) if scsiDisk { controller := libvirtxml.DomainController{Type: "scsi", Model: "virtio-scsi"} domainDef.Devices.Controllers = append(domainDef.Devices.Controllers, controller) @@ -870,33 +886,52 @@ func resourceLibvirtDomainRead(d *schema.ResourceData, meta interface{}) error { var disks []map[string]interface{} for _, diskDef := range domainDef.Devices.Disks { - var virVol *libvirt.StorageVol - if len(diskDef.Source.File) > 0 { - virVol, err = virConn.LookupStorageVolByPath(diskDef.Source.File) - } else { - virPool, err := virConn.LookupStoragePoolByName(diskDef.Source.Pool) + // network drives do not have a volume associated + if diskDef.Type == "network" { + if len(diskDef.Source.Hosts) < 1 { + return fmt.Errorf("Network disk does not contain any hosts") + } + url, err := url.Parse(fmt.Sprintf("%s://%s:%s%s", + diskDef.Source.Protocol, + diskDef.Source.Hosts[0].Name, + diskDef.Source.Hosts[0].Port, + diskDef.Source.Name)) if err != nil { - return fmt.Errorf("Error retrieving pool for disk: %s", err) + return err + } + disk := map[string]interface{}{ + "url": url.String(), } - defer virPool.Free() + disks = append(disks, disk) + } else { + var virVol *libvirt.StorageVol + if len(diskDef.Source.File) > 0 { + virVol, err = virConn.LookupStorageVolByPath(diskDef.Source.File) + } else { + virPool, err := virConn.LookupStoragePoolByName(diskDef.Source.Pool) + if err != nil { + return fmt.Errorf("Error retrieving pool for disk: %s", err) + } + defer virPool.Free() - virVol, err = virPool.LookupStorageVolByName(diskDef.Source.Volume) - } + virVol, err = virPool.LookupStorageVolByName(diskDef.Source.Volume) + } - if err != nil { - return fmt.Errorf("Error retrieving volume for disk: %s", err) - } - defer virVol.Free() + if err != nil { + return fmt.Errorf("Error retrieving volume for disk: %s", err) + } + defer virVol.Free() - virVolKey, err := virVol.GetKey() - if err != nil { - return fmt.Errorf("Error retrieving volume for disk: %s", err) - } + virVolKey, err := virVol.GetKey() + if err != nil { + return fmt.Errorf("Error retrieving volume for disk: %s", err) + } - disk := map[string]interface{}{ - "volume_id": virVolKey, + disk := map[string]interface{}{ + "volume_id": virVolKey, + } + disks = append(disks, disk) } - disks = append(disks, disk) } d.Set("disks", disks) var filesystems []map[string]interface{} diff --git a/libvirt/resource_libvirt_domain_test.go b/libvirt/resource_libvirt_domain_test.go index 5ea3af0b..2c9f1b52 100644 --- a/libvirt/resource_libvirt_domain_test.go +++ b/libvirt/resource_libvirt_domain_test.go @@ -5,6 +5,7 @@ import ( "fmt" "io/ioutil" "log" + "net/url" "os" "testing" @@ -202,6 +203,38 @@ func TestAccLibvirtDomain_ScsiDisk(t *testing.T) { } +func TestAccLibvirtDomainURLDisk(t *testing.T) { + var domain libvirt.Domain + u, err := url.Parse("http://download.opensuse.org/tumbleweed/iso/openSUSE-Tumbleweed-DVD-x86_64-Current.iso") + if err != nil { + t.Error(err) + } + + var configURL = fmt.Sprintf(` + resource "libvirt_domain" "acceptance-test-domain" { + name = "terraform-test-domain" + disk { + url = "%s" + } + }`, u.String()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckLibvirtDomainDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: configURL, + Check: resource.ComposeTestCheckFunc( + testAccCheckLibvirtDomainExists("libvirt_domain.acceptance-test-domain", &domain), + testAccCheckLibvirtURLDisk(u, &domain), + ), + }, + }, + }) + +} + func TestAccLibvirtDomain_NetworkInterface(t *testing.T) { var domain libvirt.Domain @@ -590,6 +623,41 @@ func testAccCheckLibvirtScsiDisk(n string, domain *libvirt.Domain) resource.Test } } +func testAccCheckLibvirtURLDisk(u *url.URL, domain *libvirt.Domain) resource.TestCheckFunc { + return func(s *terraform.State) error { + xmlDesc, err := domain.GetXMLDesc(0) + if err != nil { + return fmt.Errorf("Error retrieving libvirt domain XML description: %s", err) + } + + domainDef := newDomainDef() + err = xml.Unmarshal([]byte(xmlDesc), &domainDef) + if err != nil { + return fmt.Errorf("Error reading libvirt domain XML description: %s", err) + } + + disks := domainDef.Devices.Disks + for _, disk := range disks { + if disk.Type != "network" { + return fmt.Errorf("Disk type is not network") + } + if disk.Source.Protocol != u.Scheme { + return fmt.Errorf("Disk protocol is not %s", u.Scheme) + } + if disk.Source.Name != u.Path { + return fmt.Errorf("Disk name is not %s", u.Path) + } + if len(disk.Source.Hosts) < 1 { + return fmt.Errorf("Disk has no hosts defined") + } + if disk.Source.Hosts[0].Name != u.Hostname() { + return fmt.Errorf("Disk hostname is not %s", u.Hostname()) + } + } + return nil + } +} + func createNvramFile() (string, error) { // size of an accepted, valid, nvram backing store NVRAMDummyBuffer := make([]byte, 131072) diff --git a/libvirt/resource_libvirt_volume.go b/libvirt/resource_libvirt_volume.go index 8d75b744..7a7f6a40 100644 --- a/libvirt/resource_libvirt_volume.go +++ b/libvirt/resource_libvirt_volume.go @@ -90,8 +90,8 @@ func resourceLibvirtVolumeCreate(d *schema.ResourceData, meta interface{}) error poolName = d.Get("pool").(string) } - PoolSync.AcquireLock(poolName) - defer PoolSync.ReleaseLock(poolName) + poolMutexKV.Lock(poolName) + defer poolMutexKV.Unlock(poolName) pool, err := virConn.LookupStoragePoolByName(poolName) if err != nil { @@ -271,10 +271,10 @@ func resourceLibvirtVolumeRead(d *schema.ResourceData, meta interface{}) error { if virErr.Code != libvirt.ERR_NO_STORAGE_VOL { return fmt.Errorf("Can't retrieve volume %s", d.Id()) } + volID := d.Id() - log.Printf("[INFO] Volume %s not found, attempting to start its pool") + log.Printf("[INFO] Volume %s not found, attempting to start its pool", d.Id()) - volID := d.Id() volPoolName := d.Get("pool").(string) volPool, err := virConn.LookupStoragePoolByName(volPoolName) if err != nil { diff --git a/libvirt/utils.go b/libvirt/utils.go index a82417fb..13a34312 100644 --- a/libvirt/utils.go +++ b/libvirt/utils.go @@ -85,8 +85,8 @@ func RemoveVolume(virConn *libvirt.Connect, key string) error { return fmt.Errorf("Error retrieving name of volume: %s", err) } - PoolSync.AcquireLock(poolName) - defer PoolSync.ReleaseLock(poolName) + poolMutexKV.Lock(poolName) + defer poolMutexKV.Unlock(poolName) WaitForSuccess("Error refreshing pool for volume", func() error { return volPool.Refresh(0) diff --git a/libvirt/utils_volume.go b/libvirt/utils_volume.go index 457bacb9..03c99180 100644 --- a/libvirt/utils_volume.go +++ b/libvirt/utils_volume.go @@ -106,11 +106,12 @@ func (i *httpImage) Import(copier func(io.Reader) error, vol libvirtxml.StorageV req.Header.Set("If-Modified-Since", timeFromEpoch(vol.Target.Timestamps.Mtime).UTC().Format(http.TimeFormat)) } response, err := client.Do(req) - defer response.Body.Close() if err != nil { return fmt.Errorf("Error while downloading %s: %s", i.url.String(), err) } + + defer response.Body.Close() if response.StatusCode == http.StatusNotModified { return nil } diff --git a/travis/setup-guest b/travis/setup-guest index 71562655..42568e81 100755 --- a/travis/setup-guest +++ b/travis/setup-guest @@ -14,7 +14,7 @@ done add-apt-repository -y ppa:gophers/archive apt-get -qq update -apt-get install -y qemu libvirt-bin libvirt-dev golang-1.9 ovmf +apt-get install -y qemu libvirt-bin libvirt-dev golang-1.9 ovmf genisoimage echo -e "<pool type='dir'>\n<name>default</name>\n<target>\n<path>/pool-default</path>\n</target>\n</pool>" > pool.xml mkdir /pool-default chmod a+rwx /pool-default diff --git a/website/docs/r/domain.html.markdown b/website/docs/r/domain.html.markdown index c9bbfbaa..b047a6e7 100644 --- a/website/docs/r/domain.html.markdown +++ b/website/docs/r/domain.html.markdown @@ -143,7 +143,12 @@ resource "libvirt_domain" "my_machine" { The `disk` block supports: -* `volume_id` - (Required) The volume id to use for this disk. +* `volume_id` - (Optional) The volume id to use for this disk. +* `url` - (Optional) The http url to use as the block device for this disk (read-only) + +While both `volume_id` and `url` are optional, it is intended that you use either a volume or a +url. + * `scsi` - (Optional) Use a scsi controller for this disk. The controller model is set to `virtio-scsi` * `wwn` - (Optional) Specify a WWN to use for the disk if the disk is using @@ -167,6 +172,10 @@ resource "libvirt_domain" "domain1" { volume_id = "${libvirt_volume.mydisk.id}" scsi = "yes" } + + disk { + url = "http://foo.com/install.iso" + } } ``` |