diff options
-rw-r--r-- | .travis.yml | 29 | ||||
-rw-r--r-- | libvirt/domain_def.go | 7 | ||||
-rw-r--r-- | libvirt/libvirt_domain_mock_test.go | 9 | ||||
-rw-r--r-- | libvirt/libvirt_interfaces.go | 11 | ||||
-rw-r--r-- | libvirt/libvirt_network_mock_test.go | 10 | ||||
-rw-r--r-- | libvirt/network_def.go | 4 | ||||
-rw-r--r-- | libvirt/network_def_test.go | 73 | ||||
-rw-r--r-- | libvirt/pool_sync_test.go | 46 | ||||
-rw-r--r-- | libvirt/qemu_agent.go | 25 | ||||
-rw-r--r-- | libvirt/qemu_agent_test.go | 196 | ||||
-rw-r--r-- | libvirt/resource_libvirt_domain_test.go | 2 | ||||
-rw-r--r-- | libvirt/utils.go | 7 | ||||
-rw-r--r-- | libvirt/utils_test.go | 46 |
13 files changed, 434 insertions, 31 deletions
diff --git a/.travis.yml b/.travis.yml index 687158cf..eed2d261 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,18 +5,15 @@ go: - 1.7 before_install: + - sudo add-apt-repository -y cloud-archive:mitaka - sudo apt-get -qq update - - sudo apt-get install -y wget - - sudo apt-get build-dep -y libvirt - - wget https://libvirt.org/sources/libvirt-1.2.14.tar.gz - - sudo mkdir -p /usr/src - - sudo tar xvzf libvirt-1.2.14.tar.gz -C /usr/src - - pushd /usr/src/libvirt-1.2.14 - - sudo ./configure --prefix=/usr/ - - sudo make - - sudo make install - - sudo libvirtd -d - - popd + - sudo apt-get install -y qemu libvirt-bin libvirt-dev + - sudo usermod -a -G libvirtd $USER + - echo -e "<pool type='dir'>\n<name>default</name>\n<target>\n<path>/pool-default</path>\n</target>\n</pool>" > pool.xml + - sudo mkdir /pool-default + - sudo chmod a+rwx /pool-default + - sudo virsh pool-define pool.xml + - sudo virsh pool-start default - go get github.com/mattn/goveralls - go get -u github.com/govend/govend - govend -v @@ -27,6 +24,12 @@ install: # override the custom test script, this would trigger # also the execution of the unit tests of the vendored # code +env: + global: + - TERRAFORM_LIBVIRT_TEST_DOMAIN_TYPE=qemu + - TF_ACC=true + - LIBVIRT_DEFAULT_URI="qemu:///system" + script: - - go test ./libvirt - - $HOME/gopath/bin/goveralls -service=travis-ci + - sg libvirtd -c "go test -v ./libvirt" + - sg libvirtd -c "$HOME/gopath/bin/goveralls -v -service=travis-ci" diff --git a/libvirt/domain_def.go b/libvirt/domain_def.go index f41a88bd..306286ed 100644 --- a/libvirt/domain_def.go +++ b/libvirt/domain_def.go @@ -2,6 +2,7 @@ package libvirt import ( "encoding/xml" + "os" ) type defDomain struct { @@ -117,7 +118,11 @@ type defConsole struct { func newDomainDef() defDomain { // libvirt domain definition domainDef := defDomain{} - domainDef.Type = "kvm" + if v := os.Getenv("TERRAFORM_LIBVIRT_TEST_DOMAIN_TYPE"); v != "" { + domainDef.Type = v + } else { + domainDef.Type = "kvm" + } domainDef.Xmlns = "" domainDef.Os = defOs{} diff --git a/libvirt/libvirt_domain_mock_test.go b/libvirt/libvirt_domain_mock_test.go new file mode 100644 index 00000000..9a4c0290 --- /dev/null +++ b/libvirt/libvirt_domain_mock_test.go @@ -0,0 +1,9 @@ +package libvirt + +type LibVirtDomainMock struct { + QemuAgentCommandResponse string +} + +func (d LibVirtDomainMock) QemuAgentCommand(cmd string, timeout int, flags uint32) string { + return d.QemuAgentCommandResponse +} diff --git a/libvirt/libvirt_interfaces.go b/libvirt/libvirt_interfaces.go new file mode 100644 index 00000000..13bdbe35 --- /dev/null +++ b/libvirt/libvirt_interfaces.go @@ -0,0 +1,11 @@ +package libvirt + +// Interface used to expose a libvirt.VirDomain +// Used to allow testing +type LibVirtDomain interface { + QemuAgentCommand(cmd string, timeout int, flags uint32) string +} + +type LibVirtNetwork interface { + GetXMLDesc(flags uint32) (string, error) +} diff --git a/libvirt/libvirt_network_mock_test.go b/libvirt/libvirt_network_mock_test.go new file mode 100644 index 00000000..b219cb91 --- /dev/null +++ b/libvirt/libvirt_network_mock_test.go @@ -0,0 +1,10 @@ +package libvirt + +type LibVirtNetworkMock struct { + GetXMLDescReply string + GetXMLDescError error +} + +func (n LibVirtNetworkMock) GetXMLDesc(flags uint32) (string, error) { + return n.GetXMLDescReply, n.GetXMLDescError +} diff --git a/libvirt/network_def.go b/libvirt/network_def.go index 86f6dc6c..bd758747 100644 --- a/libvirt/network_def.go +++ b/libvirt/network_def.go @@ -3,8 +3,6 @@ package libvirt import ( "encoding/xml" "fmt" - - libvirt "github.com/dmacvicar/libvirt-go" ) type defNetworkIpDhcpRange struct { @@ -118,7 +116,7 @@ func newDefNetworkFromXML(s string) (defNetwork, error) { return networkDef, nil } -func newDefNetworkfromLibvirt(network *libvirt.VirNetwork) (defNetwork, error) { +func newDefNetworkfromLibvirt(network LibVirtNetwork) (defNetwork, error) { networkXmlDesc, err := network.GetXMLDesc(0) if err != nil { return defNetwork{}, fmt.Errorf("Error retrieving libvirt domain XML description: %s", err) diff --git a/libvirt/network_def_test.go b/libvirt/network_def_test.go index d84c3ce8..14901f2f 100644 --- a/libvirt/network_def_test.go +++ b/libvirt/network_def_test.go @@ -3,6 +3,7 @@ package libvirt import ( "bytes" "encoding/xml" + "errors" "testing" "github.com/davecgh/go-spew/spew" @@ -103,3 +104,75 @@ func TestBrokenNetworkDefUnmarshall(t *testing.T) { t.Error("Unmarshal was supposed to fail") } } + +func TestHasDHCPNoForwardSet(t *testing.T) { + net := defNetwork{} + + if net.HasDHCP() { + t.Error("Expected to not have forward enabled") + } +} + +func TestHasDHCPForwardSet(t *testing.T) { + createNet := func(mode string) defNetwork { + return defNetwork{ + Forward: &defNetworkForward{ + Mode: mode, + }, + } + } + + for _, mode := range []string{"nat", "route", ""} { + net := createNet(mode) + if !net.HasDHCP() { + t.Errorf( + "Expected to have forward enabled with forward set to be '%s'", + mode) + } + } +} + +func TestNetworkFromLibvirtError(t *testing.T) { + net := LibVirtNetworkMock{ + GetXMLDescError: errors.New("boom"), + } + + _, err := newDefNetworkfromLibvirt(net) + if err == nil { + t.Error("Expected error") + } +} + +func TestNetworkFromLibvirtWrongResponse(t *testing.T) { + net := LibVirtNetworkMock{ + GetXMLDescReply: "wrong xml", + } + + _, err := newDefNetworkfromLibvirt(net) + if err == nil { + t.Error("Expected error") + } +} + +func TestNetworkFromLibvirt(t *testing.T) { + net := LibVirtNetworkMock{ + GetXMLDescReply: ` + <network> + <name>default</name> + <forward mode='nat'> + <nat> + <port start='1024' end='65535'/> + </nat> + </forward> + </network>`, + } + + dn, err := newDefNetworkfromLibvirt(net) + if err != nil { + t.Errorf("Unexpected error %v", err) + } + + if dn.Forward.Mode != "nat" { + t.Errorf("Wrong forward mode: %s", dn.Forward.Mode) + } +} diff --git a/libvirt/pool_sync_test.go b/libvirt/pool_sync_test.go new file mode 100644 index 00000000..630ae2a4 --- /dev/null +++ b/libvirt/pool_sync_test.go @@ -0,0 +1,46 @@ +package libvirt + +import ( + "testing" +) + +func TestAcquireLock(t *testing.T) { + ps := NewLibVirtPoolSync() + + ps.AcquireLock("test") + + _, found := ps.PoolLocks["test"] + + if !found { + t.Errorf("lock not found") + } +} + +func TestReleaseLock(t *testing.T) { + ps := NewLibVirtPoolSync() + + 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 := NewLibVirtPoolSync() + + 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/qemu_agent.go b/libvirt/qemu_agent.go index 5b87dbd5..946aa39f 100644 --- a/libvirt/qemu_agent.go +++ b/libvirt/qemu_agent.go @@ -13,13 +13,15 @@ type QemuAgentInterfacesResponse struct { } type QemuAgentInterface struct { - Name string `json:"name"` - Hwaddr string `json:"hardware-address"` - IpAddresses []struct { - Type string `json:"ip-address-type"` - Address string `json:"ip-address"` - Prefix uint `json:"prefix"` - } `json:"ip-addresses"` + Name string `json:"name"` + Hwaddr string `json:"hardware-address"` + IpAddresses []QemuAgentInterfaceIpAddress `json:"ip-addresses"` +} + +type QemuAgentInterfaceIpAddress struct { + Type string `json:"ip-address-type"` + Address string `json:"ip-address"` + Prefix uint `json:"prefix"` } // Retrieve all the interfaces attached to a domain and their addresses. Only @@ -27,7 +29,7 @@ type QemuAgentInterface struct { // When wait4ipv4 is turned on the code will not report interfaces that don't // have a ipv4 address set. This is useful when a domain gets the ipv6 address // before the ipv4 one. -func getDomainInterfacesViaQemuAgent(domain *libvirt.VirDomain, wait4ipv4 bool) []libvirt.VirDomainInterface { +func getDomainInterfacesViaQemuAgent(domain LibVirtDomain, wait4ipv4 bool) []libvirt.VirDomainInterface { log.Print("[DEBUG] get network interfaces using qemu agent") var interfaces []libvirt.VirDomainInterface @@ -54,9 +56,9 @@ func getDomainInterfacesViaQemuAgent(domain *libvirt.VirDomain, wait4ipv4 bool) continue } - libVirtIface := libvirt.VirDomainInterface{} - libVirtIface.Name = iface.Name - libVirtIface.Hwaddr = iface.Hwaddr + libVirtIface := libvirt.VirDomainInterface{ + Name: iface.Name, + Hwaddr: iface.Hwaddr} ipv4Assigned := false for _, addr := range iface.IpAddresses { @@ -78,6 +80,7 @@ func getDomainInterfacesViaQemuAgent(domain *libvirt.VirDomain, wait4ipv4 bool) libVirtAddr.Type = libvirt.VIR_IP_ADDR_TYPE_IPV6 default: log.Printf("[ERROR] Cannot handle unknown address type %s", addr.Type) + continue } libVirtIface.Addrs = append(libVirtIface.Addrs, libVirtAddr) } diff --git a/libvirt/qemu_agent_test.go b/libvirt/qemu_agent_test.go new file mode 100644 index 00000000..095af723 --- /dev/null +++ b/libvirt/qemu_agent_test.go @@ -0,0 +1,196 @@ +package libvirt + +import ( + "encoding/json" + "testing" + + libvirt "github.com/dmacvicar/libvirt-go" +) + +func TestGetDomainInterfacesViaQemuAgentInvalidResponse(t *testing.T) { + domain := LibVirtDomainMock{} + + interfaces := getDomainInterfacesViaQemuAgent(domain, false) + + if len(interfaces) != 0 { + t.Errorf("wrong number of interfaces: %d instead of 0", len(interfaces)) + } +} + +func TestGetDomainInterfacesViaQemuAgentNoInterfaces(t *testing.T) { + + response := QemuAgentInterfacesResponse{ + Interfaces: []QemuAgentInterface{}} + data, err := json.Marshal(response) + if err != nil { + t.Errorf("error: %v", err) + } + domain := LibVirtDomainMock{ + QemuAgentCommandResponse: string(data), + } + + interfaces := getDomainInterfacesViaQemuAgent(domain, false) + if len(interfaces) != 0 { + t.Errorf("wrong number of interfaces: %d instead of 0", len(interfaces)) + } +} + +func TestGetDomainInterfacesViaQemuAgentIgnoreLoopbackDevice(t *testing.T) { + response := QemuAgentInterfacesResponse{ + Interfaces: []QemuAgentInterface{ + QemuAgentInterface{ + Name: "lo", + Hwaddr: "ho:me", + IpAddresses: []QemuAgentInterfaceIpAddress{ + QemuAgentInterfaceIpAddress{ + Type: "ipv4", + Address: "127.0.0.1", + Prefix: 1, + }, + }, + }, + }, + } + data, err := json.Marshal(response) + if err != nil { + t.Errorf("error: %v", err) + } + domain := LibVirtDomainMock{ + QemuAgentCommandResponse: string(data), + } + + interfaces := getDomainInterfacesViaQemuAgent(domain, false) + + if len(interfaces) != 0 { + t.Errorf("wrong number of interfaces)") + } +} + +func TestGetDomainInterfacesViaQemuAgentIgnoreDevicesWithoutAddress(t *testing.T) { + response := QemuAgentInterfacesResponse{ + Interfaces: []QemuAgentInterface{ + QemuAgentInterface{ + Name: "eth1", + Hwaddr: "xy:yy:zz", + IpAddresses: []QemuAgentInterfaceIpAddress{ + QemuAgentInterfaceIpAddress{ + Type: "ipv4", + Address: "", + Prefix: 1, + }, + }, + }, + }, + } + data, err := json.Marshal(response) + if err != nil { + t.Errorf("error: %v", err) + } + domain := LibVirtDomainMock{ + QemuAgentCommandResponse: string(data), + } + + interfaces := getDomainInterfacesViaQemuAgent(domain, false) + + if len(interfaces) != 0 { + t.Errorf("wrong number of interfaces") + } +} + +func TestGetDomainInterfacesViaQemuAgentUnknownIpAddressType(t *testing.T) { + response := QemuAgentInterfacesResponse{ + Interfaces: []QemuAgentInterface{ + QemuAgentInterface{ + Name: "eth2", + Hwaddr: "zy:yy:zz", + IpAddresses: []QemuAgentInterfaceIpAddress{ + QemuAgentInterfaceIpAddress{ + Type: "ipv8", + Address: "i don't exist", + Prefix: 1, + }, + }, + }, + }, + } + data, err := json.Marshal(response) + if err != nil { + t.Errorf("error: %v", err) + } + domain := LibVirtDomainMock{ + QemuAgentCommandResponse: string(data), + } + + interfaces := getDomainInterfacesViaQemuAgent(domain, false) + + if len(interfaces) != 0 { + t.Errorf("wrong number of interfaces: %d instead of 1", len(interfaces)) + } +} + +func TestGetDomainInterfacesViaQemuAgent(t *testing.T) { + device := "eth0" + mac := "xx:yy:zz" + ipv4Addr := "192.168.1.1" + ipv6Addr := "2001:0db8:0000:0000:0000:ff00:0042:8329" + + response := QemuAgentInterfacesResponse{ + Interfaces: []QemuAgentInterface{ + QemuAgentInterface{ + Name: device, + Hwaddr: mac, + IpAddresses: []QemuAgentInterfaceIpAddress{ + QemuAgentInterfaceIpAddress{ + Type: "ipv4", + Address: ipv4Addr, + Prefix: 1, + }, + QemuAgentInterfaceIpAddress{ + Type: "ipv6", + Address: ipv6Addr, + Prefix: 1, + }, + }, + }, + }, + } + data, err := json.Marshal(response) + if err != nil { + t.Errorf("error: %v", err) + } + domain := LibVirtDomainMock{ + QemuAgentCommandResponse: string(data), + } + + interfaces := getDomainInterfacesViaQemuAgent(domain, false) + + if len(interfaces) != 1 { + t.Errorf("wrong number of interfaces: %d instead of 1", len(interfaces)) + } + + if interfaces[0].Name != device { + t.Errorf("wrong interface name: %s", interfaces[0].Name) + } + + if interfaces[0].Hwaddr != mac { + t.Errorf("wrong interface name: %s", interfaces[0].Hwaddr) + } + + if len(interfaces[0].Addrs) != 2 { + t.Errorf("wrong number of addresses: %d", len(interfaces[0].Addrs)) + } + + for _, addr := range interfaces[0].Addrs { + var expected string + + if addr.Type == libvirt.VIR_IP_ADDR_TYPE_IPV4 { + expected = ipv4Addr + } else { + expected = ipv6Addr + } + + if expected != addr.Addr { + t.Errorf("Expected %s, got %s", expected, addr.Addr) + } + } +} diff --git a/libvirt/resource_libvirt_domain_test.go b/libvirt/resource_libvirt_domain_test.go index dbb68863..b27df0e4 100644 --- a/libvirt/resource_libvirt_domain_test.go +++ b/libvirt/resource_libvirt_domain_test.go @@ -181,7 +181,7 @@ func TestAccLibvirtDomain_NetworkInterface(t *testing.T) { network_name = "default" } network_interface = { - bridge = "br0" + network_name = "default" mac = "52:54:00:A9:F5:17" } disk { diff --git a/libvirt/utils.go b/libvirt/utils.go index 0700b756..1f8bbfcd 100644 --- a/libvirt/utils.go +++ b/libvirt/utils.go @@ -26,6 +26,9 @@ func DiskLetterForIndex(i int) string { return fmt.Sprintf("%s%c", DiskLetterForIndex(q-1), letter) } +var WAIT_SLEEP_INTERVAL time.Duration = 1 * time.Second +var WAIT_TIMEOUT time.Duration = 5 * time.Minute + // wait for success and timeout after 5 minutes. func WaitForSuccess(errorMessage string, f func() error) error { start := time.Now() @@ -36,8 +39,8 @@ func WaitForSuccess(errorMessage string, f func() error) error { } log.Printf("[DEBUG] %s. Re-trying.\n", err) - time.Sleep(1 * time.Second) - if time.Since(start) > 5*time.Minute { + time.Sleep(WAIT_SLEEP_INTERVAL) + if time.Since(start) > WAIT_TIMEOUT { return fmt.Errorf("%s: %s", errorMessage, err) } } diff --git a/libvirt/utils_test.go b/libvirt/utils_test.go index 62000f34..cdcf05b9 100644 --- a/libvirt/utils_test.go +++ b/libvirt/utils_test.go @@ -1,8 +1,10 @@ package libvirt import ( + "errors" "net" "testing" + "time" ) func TestDiskLetterForIndex(t *testing.T) { @@ -32,3 +34,47 @@ func TestIPsRange(t *testing.T) { t.Errorf("unexpected range start for '%s': %s", net, start) } } + +func TestWaitForSuccessEverythingFine(t *testing.T) { + wait_sleep := WAIT_SLEEP_INTERVAL + wait_timeout := WAIT_TIMEOUT + defer func() { + WAIT_SLEEP_INTERVAL = wait_sleep + WAIT_TIMEOUT = wait_timeout + }() + + WAIT_TIMEOUT = 1 * time.Second + WAIT_SLEEP_INTERVAL = 1 * time.Nanosecond + + err := WaitForSuccess( + "boom", + func() error { + return nil + }) + + if err != nil { + t.Errorf("unexpected error %v", err) + } +} + +func TestWaitForSuccessBrokenFunction(t *testing.T) { + wait_sleep := WAIT_SLEEP_INTERVAL + wait_timeout := WAIT_TIMEOUT + defer func() { + WAIT_SLEEP_INTERVAL = wait_sleep + WAIT_TIMEOUT = wait_timeout + }() + + WAIT_TIMEOUT = 1 * time.Second + WAIT_SLEEP_INTERVAL = 1 * time.Nanosecond + + err := WaitForSuccess( + "boom", + func() error { + return errors.New("something went wrong") + }) + + if err == nil { + t.Error("expected error") + } +} |