mirror of https://github.com/docker/docs.git
add code and tests to detect vboxnet collision with host interfaces
+ exclude existing host-only nets from list of host interfaces + interface and mocks for system calls to net.* Signed-off-by: Paul Callahan <paul.callahan@gmail.com>
This commit is contained in:
parent
5a270c9751
commit
0db41d0a92
|
@ -35,6 +35,27 @@ type hostOnlyNetwork struct {
|
||||||
NetworkName string // referenced in DHCP.NetworkName
|
NetworkName string // referenced in DHCP.NetworkName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HostInterfaces returns host network interface info. By default delegates to net.Interfaces()
|
||||||
|
type HostInterfaces interface {
|
||||||
|
Interfaces() ([]net.Interface, error)
|
||||||
|
Addrs(iface *net.Interface) ([]net.Addr, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHostInterfaces() HostInterfaces {
|
||||||
|
return &defaultHostInterfaces{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type defaultHostInterfaces struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ni *defaultHostInterfaces) Interfaces() ([]net.Interface, error) {
|
||||||
|
return net.Interfaces()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ni *defaultHostInterfaces) Addrs(iface *net.Interface) ([]net.Addr, error) {
|
||||||
|
return iface.Addrs()
|
||||||
|
}
|
||||||
|
|
||||||
// Save changes the configuration of the host-only network.
|
// Save changes the configuration of the host-only network.
|
||||||
func (n *hostOnlyNetwork) Save(vbox VBoxManager) error {
|
func (n *hostOnlyNetwork) Save(vbox VBoxManager) error {
|
||||||
if err := n.SaveIPv4(vbox); err != nil {
|
if err := n.SaveIPv4(vbox); err != nil {
|
||||||
|
@ -158,12 +179,7 @@ func getHostOnlyAdapter(nets map[string]*hostOnlyNetwork, hostIP net.IP, netmask
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getOrCreateHostOnlyNetwork(hostIP net.IP, netmask net.IPMask, vbox VBoxManager) (*hostOnlyNetwork, error) {
|
func getOrCreateHostOnlyNetwork(hostIP net.IP, netmask net.IPMask, nets map[string]*hostOnlyNetwork, vbox VBoxManager) (*hostOnlyNetwork, error) {
|
||||||
nets, err := listHostOnlyAdapters(vbox)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Search for an existing host-only adapter.
|
// Search for an existing host-only adapter.
|
||||||
hostOnlyAdapter := getHostOnlyAdapter(nets, hostIP, netmask)
|
hostOnlyAdapter := getHostOnlyAdapter(nets, hostIP, netmask)
|
||||||
if hostOnlyAdapter != nil {
|
if hostOnlyAdapter != nil {
|
||||||
|
@ -171,7 +187,7 @@ func getOrCreateHostOnlyNetwork(hostIP net.IP, netmask net.IPMask, vbox VBoxMana
|
||||||
}
|
}
|
||||||
|
|
||||||
// No existing host-only adapter found. Create a new one.
|
// No existing host-only adapter found. Create a new one.
|
||||||
_, err = createHostonlyAdapter(vbox)
|
_, err := createHostonlyAdapter(vbox)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Sometimes the host-only adapter fails to create. See https://www.virtualbox.org/ticket/14040
|
// Sometimes the host-only adapter fails to create. See https://www.virtualbox.org/ticket/14040
|
||||||
// BUT, it is created in fact! So let's wait until it appears last in the list
|
// BUT, it is created in fact! So let's wait until it appears last in the list
|
||||||
|
@ -333,6 +349,46 @@ func listDHCPServers(vbox VBoxManager) (map[string]*dhcpServer, error) {
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// listHostInterfaces returns a map of net.IPNet addresses of host interfaces that are "UP" and not loopback adapters
|
||||||
|
// and not virtualbox host-only networks (given by excludeNets), keyed by CIDR string.
|
||||||
|
func listHostInterfaces(hif HostInterfaces, excludeNets map[string]*hostOnlyNetwork) (map[string]*net.IPNet, error) {
|
||||||
|
ifaces, err := hif.Interfaces()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
m := map[string]*net.IPNet{}
|
||||||
|
|
||||||
|
for _, iface := range ifaces {
|
||||||
|
addrs, err := hif.Addrs(&iface)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, a := range addrs {
|
||||||
|
switch ipnet := a.(type) {
|
||||||
|
case *net.IPNet:
|
||||||
|
_, hostOnly := excludeNets[ipnet.String()]
|
||||||
|
if !hostOnly && iface.Flags&net.FlagUp != 0 && iface.Flags&net.FlagLoopback == 0 {
|
||||||
|
m[ipnet.String()] = ipnet
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkIPNetCollision returns true if any host interfaces conflict with the host-only network mask passed as a parameter.
|
||||||
|
// This works with IPv4 or IPv6 ip addresses.
|
||||||
|
func checkIPNetCollision(hostonly *net.IPNet, hostIfaces map[string]*net.IPNet) (bool, error) {
|
||||||
|
for _, ifaceNet := range hostIfaces {
|
||||||
|
if hostonly.IP.Equal(ifaceNet.IP.Mask(ifaceNet.Mask)) {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
// parseIPv4Mask parses IPv4 netmask written in IP form (e.g. 255.255.255.0).
|
// parseIPv4Mask parses IPv4 netmask written in IP form (e.g. 255.255.255.0).
|
||||||
// This function should really belong to the net package.
|
// This function should really belong to the net package.
|
||||||
func parseIPv4Mask(s string) net.IPMask {
|
func parseIPv4Mask(s string) net.IPMask {
|
||||||
|
|
|
@ -65,6 +65,37 @@ Enabled: No
|
||||||
`
|
`
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type mockHostInterfaces struct {
|
||||||
|
mockIfaces []net.Interface
|
||||||
|
mockAddrs map[string]net.Addr
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMockHostInterfaces() *mockHostInterfaces {
|
||||||
|
return &mockHostInterfaces{
|
||||||
|
mockAddrs: make(map[string]net.Addr),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mhi *mockHostInterfaces) Interfaces() ([]net.Interface, error) {
|
||||||
|
return mhi.mockIfaces, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mhi *mockHostInterfaces) Addrs(iface *net.Interface) ([]net.Addr, error) {
|
||||||
|
return []net.Addr{mhi.mockAddrs[iface.Name]}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mhi *mockHostInterfaces) addMockIface(ip string, mask int, iplen int, name string, flags net.Flags) (*net.IPNet, error) {
|
||||||
|
iface := &net.Interface{Name: name, Flags: flags}
|
||||||
|
mhi.mockIfaces = append(mhi.mockIfaces, *iface)
|
||||||
|
|
||||||
|
ipnet := &net.IPNet{IP: net.ParseIP(ip), Mask: net.CIDRMask(mask, 8*iplen)}
|
||||||
|
if ipnet.IP == nil {
|
||||||
|
return nil, &net.ParseError{Type: "IP address", Text: ip}
|
||||||
|
}
|
||||||
|
mhi.mockAddrs[name] = ipnet
|
||||||
|
return ipnet, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Tests that when we have a host only network which matches our expectations,
|
// Tests that when we have a host only network which matches our expectations,
|
||||||
// it gets returned correctly.
|
// it gets returned correctly.
|
||||||
func TestGetHostOnlyNetworkHappy(t *testing.T) {
|
func TestGetHostOnlyNetworkHappy(t *testing.T) {
|
||||||
|
@ -220,8 +251,10 @@ func TestGetHostOnlyNetwork(t *testing.T) {
|
||||||
args: "list hostonlyifs",
|
args: "list hostonlyifs",
|
||||||
stdOut: stdOutOneHostOnlyNetwork,
|
stdOut: stdOutOneHostOnlyNetwork,
|
||||||
}
|
}
|
||||||
|
nets, err := listHostOnlyAdapters(vbox)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
net, err := getOrCreateHostOnlyNetwork(net.ParseIP("192.168.99.1"), parseIPv4Mask("255.255.255.0"), vbox)
|
net, err := getOrCreateHostOnlyNetwork(net.ParseIP("192.168.99.1"), parseIPv4Mask("255.255.255.0"), nets, vbox)
|
||||||
|
|
||||||
assert.NotNil(t, net)
|
assert.NotNil(t, net)
|
||||||
assert.Equal(t, "HostInterfaceNetworking-vboxnet0", net.NetworkName)
|
assert.Equal(t, "HostInterfaceNetworking-vboxnet0", net.NetworkName)
|
||||||
|
@ -240,10 +273,8 @@ IPAddress: 192.168.99.1
|
||||||
NetworkMask: 255.255.255.0
|
NetworkMask: 255.255.255.0
|
||||||
VBoxNetworkName: HostInterfaceNetworking-vboxnet1`,
|
VBoxNetworkName: HostInterfaceNetworking-vboxnet1`,
|
||||||
}
|
}
|
||||||
|
nets, err := listHostOnlyAdapters(vbox)
|
||||||
net, err := getOrCreateHostOnlyNetwork(net.ParseIP("192.168.99.1"), parseIPv4Mask("255.255.255.0"), vbox)
|
assert.Nil(t, nets)
|
||||||
|
|
||||||
assert.Nil(t, net)
|
|
||||||
assert.EqualError(t, err, `VirtualBox is configured with multiple host-only adapters with the same IP "192.168.99.1". Please remove one.`)
|
assert.EqualError(t, err, `VirtualBox is configured with multiple host-only adapters with the same IP "192.168.99.1". Please remove one.`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -255,10 +286,8 @@ VBoxNetworkName: HostInterfaceNetworking-vboxnet0
|
||||||
Name: vboxnet0
|
Name: vboxnet0
|
||||||
VBoxNetworkName: HostInterfaceNetworking-vboxnet0`,
|
VBoxNetworkName: HostInterfaceNetworking-vboxnet0`,
|
||||||
}
|
}
|
||||||
|
nets, err := listHostOnlyAdapters(vbox)
|
||||||
net, err := getOrCreateHostOnlyNetwork(net.ParseIP("192.168.99.1"), parseIPv4Mask("255.255.255.0"), vbox)
|
assert.Nil(t, nets)
|
||||||
|
|
||||||
assert.Nil(t, net)
|
|
||||||
assert.EqualError(t, err, `VirtualBox is configured with multiple host-only adapters with the same name "HostInterfaceNetworking-vboxnet0". Please remove one.`)
|
assert.EqualError(t, err, `VirtualBox is configured with multiple host-only adapters with the same name "HostInterfaceNetworking-vboxnet0". Please remove one.`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -291,3 +320,87 @@ func TestGetDHCPServers(t *testing.T) {
|
||||||
assert.Equal(t, "ffffff00", server.IPv4.Mask.String())
|
assert.Equal(t, "ffffff00", server.IPv4.Mask.String())
|
||||||
assert.False(t, server.Enabled)
|
assert.False(t, server.Enabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tests detection of a conflict between prospective vbox host-only network and an IPV6 host interface
|
||||||
|
func TestCheckIPNetCollisionIPv6(t *testing.T) {
|
||||||
|
m := map[string]*net.IPNet{}
|
||||||
|
_, vboxHostOnly, err := net.ParseCIDR("2607:f8b0:400e:c04:ffff:ffff:ffff:ffff/64")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
hostIP, hostNet, err := net.ParseCIDR("2001:4998:c:a06::2:4008/64")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
m[hostIP.String()] = &net.IPNet{IP: hostIP, Mask: hostNet.Mask}
|
||||||
|
|
||||||
|
result, err := checkIPNetCollision(vboxHostOnly, m)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.False(t, result)
|
||||||
|
|
||||||
|
hostIP, hostNet, err = net.ParseCIDR("2607:f8b0:400e:c04::6a/64")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
m[hostIP.String()] = &net.IPNet{IP: hostIP, Mask: hostNet.Mask}
|
||||||
|
|
||||||
|
result, err = checkIPNetCollision(vboxHostOnly, m)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.True(t, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests detection of a conflict between prospective vbox host-only network and an IPV4 host interface
|
||||||
|
func TestCheckIPNetCollisionIPv4(t *testing.T) {
|
||||||
|
m := map[string]*net.IPNet{}
|
||||||
|
_, vboxHostOnly, err := net.ParseCIDR("192.168.99.1/24")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
hostIP, hostNet, err := net.ParseCIDR("10.10.10.42/24")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
m[hostIP.String()] = &net.IPNet{IP: hostIP, Mask: hostNet.Mask}
|
||||||
|
|
||||||
|
result, err := checkIPNetCollision(vboxHostOnly, m)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.False(t, result)
|
||||||
|
|
||||||
|
hostIP, hostNet, err = net.ParseCIDR("192.168.99.22/24")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
m[hostIP.String()] = &net.IPNet{IP: hostIP, Mask: hostNet.Mask}
|
||||||
|
|
||||||
|
result, err = checkIPNetCollision(vboxHostOnly, m)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.True(t, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests functionality of listHostInterfaces and verifies only non-loopback, active and non-excluded interfaces are returned
|
||||||
|
func TestListHostInterfaces(t *testing.T) {
|
||||||
|
mhi := newMockHostInterfaces()
|
||||||
|
excludes := map[string]*hostOnlyNetwork{}
|
||||||
|
|
||||||
|
en0, err := mhi.addMockIface("10.10.0.22", 24, net.IPv4len, "en0", net.FlagUp|net.FlagBroadcast)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
_, err = mhi.addMockIface("10.10.1.11", 24, net.IPv4len, "en1", net.FlagBroadcast /*not up*/)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
_, err = mhi.addMockIface("127.0.0.1", 24, net.IPv4len, "lo0", net.FlagUp|net.FlagLoopback)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
en0ipv6, err := mhi.addMockIface("2001:4998:c:a06::2:4008", 64, net.IPv6len, "en0ipv6", net.FlagUp|net.FlagBroadcast)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
vboxnet0, err := mhi.addMockIface("192.168.99.1", 24, net.IPv4len, "vboxnet0", net.FlagUp|net.FlagBroadcast)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
notvboxnet0, err := mhi.addMockIface("192.168.99.42", 24, net.IPv4len, "en2", net.FlagUp|net.FlagBroadcast)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
excludes["192.168.99.1/24"] = &hostOnlyNetwork{IPv4: *vboxnet0, Name: "HostInterfaceNetworking-vboxnet0"}
|
||||||
|
|
||||||
|
m, err := listHostInterfaces(mhi, excludes)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotEmpty(t, m)
|
||||||
|
|
||||||
|
assert.Contains(t, m, "10.10.0.22/24")
|
||||||
|
assert.Equal(t, en0, m["10.10.0.22/24"])
|
||||||
|
|
||||||
|
assert.Contains(t, m, "2001:4998:c:a06::2:4008/64")
|
||||||
|
assert.Equal(t, en0ipv6, m["2001:4998:c:a06::2:4008/64"])
|
||||||
|
|
||||||
|
assert.Contains(t, m, "192.168.99.42/24")
|
||||||
|
assert.Equal(t, notvboxnet0, m["192.168.99.42/24"])
|
||||||
|
|
||||||
|
assert.NotContains(t, m, "10.10.1.11/24")
|
||||||
|
assert.NotContains(t, m, "127.0.0.1/24")
|
||||||
|
assert.NotContains(t, m, "192.168.99.1/24")
|
||||||
|
}
|
||||||
|
|
|
@ -38,11 +38,13 @@ var (
|
||||||
ErrMustEnableVTX = errors.New("This computer doesn't have VT-X/AMD-v enabled. Enabling it in the BIOS is mandatory")
|
ErrMustEnableVTX = errors.New("This computer doesn't have VT-X/AMD-v enabled. Enabling it in the BIOS is mandatory")
|
||||||
ErrNotCompatibleWithHyperV = errors.New("Hyper-V is installed. VirtualBox won't boot a 64bits VM when Hyper-V is activated. If it's installed but deactivated, you can use --virtualbox-no-vtx-check to try anyways")
|
ErrNotCompatibleWithHyperV = errors.New("Hyper-V is installed. VirtualBox won't boot a 64bits VM when Hyper-V is activated. If it's installed but deactivated, you can use --virtualbox-no-vtx-check to try anyways")
|
||||||
ErrNetworkAddrCidr = errors.New("host-only cidr must be specified with a host address, not a network address")
|
ErrNetworkAddrCidr = errors.New("host-only cidr must be specified with a host address, not a network address")
|
||||||
|
ErrNetworkAddrCollision = errors.New("host-only cidr conflicts with the network address of a host interface")
|
||||||
)
|
)
|
||||||
|
|
||||||
type Driver struct {
|
type Driver struct {
|
||||||
*drivers.BaseDriver
|
*drivers.BaseDriver
|
||||||
VBoxManager
|
VBoxManager
|
||||||
|
HostInterfaces
|
||||||
b2dUpdater B2DUpdater
|
b2dUpdater B2DUpdater
|
||||||
sshKeyGenerator SSHKeyGenerator
|
sshKeyGenerator SSHKeyGenerator
|
||||||
diskCreator DiskCreator
|
diskCreator DiskCreator
|
||||||
|
@ -75,6 +77,7 @@ func NewDriver(hostName, storePath string) *Driver {
|
||||||
ipWaiter: NewIPWaiter(),
|
ipWaiter: NewIPWaiter(),
|
||||||
randomInter: NewRandomInter(),
|
randomInter: NewRandomInter(),
|
||||||
sleeper: NewSleeper(),
|
sleeper: NewSleeper(),
|
||||||
|
HostInterfaces: NewHostInterfaces(),
|
||||||
Memory: defaultMemory,
|
Memory: defaultMemory,
|
||||||
CPU: defaultCPU,
|
CPU: defaultCPU,
|
||||||
DiskSize: defaultDiskSize,
|
DiskSize: defaultDiskSize,
|
||||||
|
@ -529,6 +532,11 @@ func (d *Driver) Start() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = validateNoIPCollisions(d.HostInterfaces, network, nets)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
hostOnlyNet := getHostOnlyAdapter(nets, ip, network.Mask)
|
hostOnlyNet := getHostOnlyAdapter(nets, ip, network.Mask)
|
||||||
if hostOnlyNet != nil {
|
if hostOnlyNet != nil {
|
||||||
// OK, we found a valid host-only adapter
|
// OK, we found a valid host-only adapter
|
||||||
|
@ -768,8 +776,18 @@ func (d *Driver) setupHostOnlyNetwork(machineName string) (*hostOnlyNetwork, err
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nets, err := listHostOnlyAdapters(d.VBoxManager)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = validateNoIPCollisions(d.HostInterfaces, network, nets)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
log.Debugf("Searching for hostonly interface for IPv4: %s and Mask: %s", ip, network.Mask)
|
log.Debugf("Searching for hostonly interface for IPv4: %s and Mask: %s", ip, network.Mask)
|
||||||
hostOnlyAdapter, err := getOrCreateHostOnlyNetwork(ip, network.Mask, d.VBoxManager)
|
hostOnlyAdapter, err := getOrCreateHostOnlyNetwork(ip, network.Mask, nets, d.VBoxManager)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -822,6 +840,32 @@ func parseAndValidateCIDR(hostOnlyCIDR string) (net.IP, *net.IPNet, error) {
|
||||||
return ip, network, nil
|
return ip, network, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// validateNoIPCollisions ensures no conflicts between the host's network interfaces and the vbox host-only network that
|
||||||
|
// will be used for machine vm instances.
|
||||||
|
func validateNoIPCollisions(hif HostInterfaces, hostOnlyNet *net.IPNet, currHostOnlyNets map[string]*hostOnlyNetwork) error {
|
||||||
|
hostOnlyByCIDR := map[string]*hostOnlyNetwork{}
|
||||||
|
//listHostOnlyAdapters returns a map w/ virtualbox net names as key. Rekey to CIDRs
|
||||||
|
for _, n := range currHostOnlyNets {
|
||||||
|
ipnet := net.IPNet{IP: n.IPv4.IP, Mask: n.IPv4.Mask}
|
||||||
|
hostOnlyByCIDR[ipnet.String()] = n
|
||||||
|
}
|
||||||
|
|
||||||
|
m, err := listHostInterfaces(hif, hostOnlyByCIDR)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
collision, err := checkIPNetCollision(hostOnlyNet, m)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if collision {
|
||||||
|
return ErrNetworkAddrCollision
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Select an available port, trying the specified
|
// Select an available port, trying the specified
|
||||||
// port first, falling back on an OS selected port.
|
// port first, falling back on an OS selected port.
|
||||||
func getAvailableTCPPort(port int) (int, error) {
|
func getAvailableTCPPort(port int) (int, error) {
|
||||||
|
|
|
@ -241,6 +241,74 @@ func TestInvalidNetworkIpCIDR(t *testing.T) {
|
||||||
assert.Nil(t, network)
|
assert.Nil(t, network)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tests detection of a conflict between an existing vbox host-only network and a host network interface. This
|
||||||
|
// scenario would happen if the docker-machine was created with the host on one network, and then the host gets
|
||||||
|
// moved to another network (e.g. different wifi routers)
|
||||||
|
func TestCIDRHostIFaceCollisionExisting(t *testing.T) {
|
||||||
|
vbox := &VBoxManagerMock{
|
||||||
|
args: "list hostonlyifs",
|
||||||
|
stdOut: stdOutTwoHostOnlyNetwork,
|
||||||
|
}
|
||||||
|
mhi := newMockHostInterfaces()
|
||||||
|
_, err := mhi.addMockIface("192.168.99.42", 24, net.IPv4len, "en0", net.FlagUp|net.FlagBroadcast)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
nets, err := listHostOnlyAdapters(vbox)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
m, listErr := listHostInterfaces(mhi, nets)
|
||||||
|
assert.Nil(t, listErr)
|
||||||
|
assert.NotEmpty(t, m)
|
||||||
|
|
||||||
|
_, network, cidrErr := net.ParseCIDR("192.168.99.1/24")
|
||||||
|
assert.Nil(t, cidrErr)
|
||||||
|
err = validateNoIPCollisions(mhi, network, nets)
|
||||||
|
assert.Equal(t, ErrNetworkAddrCollision, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests operation of validateNoIPCollisions when no conflicts exist.
|
||||||
|
func TestCIDRHostIFaceNoCollision(t *testing.T) {
|
||||||
|
vbox := &VBoxManagerMock{
|
||||||
|
args: "list hostonlyifs",
|
||||||
|
stdOut: stdOutTwoHostOnlyNetwork,
|
||||||
|
}
|
||||||
|
mhi := newMockHostInterfaces()
|
||||||
|
_, err := mhi.addMockIface("10.10.0.22", 24, net.IPv4len, "en0", net.FlagUp|net.FlagBroadcast)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
nets, err := listHostOnlyAdapters(vbox)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
m, listErr := listHostInterfaces(mhi, nets)
|
||||||
|
assert.Nil(t, listErr)
|
||||||
|
assert.NotEmpty(t, m)
|
||||||
|
|
||||||
|
_, network, cidrErr := net.ParseCIDR("192.168.99.1/24")
|
||||||
|
assert.Nil(t, cidrErr)
|
||||||
|
err = validateNoIPCollisions(mhi, network, nets)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests detection of a conflict between a potential vbox host-only network and a host network interface.
|
||||||
|
func TestCIDRHostIFaceCollision(t *testing.T) {
|
||||||
|
vbox := &VBoxManagerMock{
|
||||||
|
args: "list hostonlyifs",
|
||||||
|
stdOut: "",
|
||||||
|
}
|
||||||
|
mhi := newMockHostInterfaces()
|
||||||
|
_, err := mhi.addMockIface("192.168.99.42", 24, net.IPv4len, "en0", net.FlagUp|net.FlagBroadcast)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
nets, err := listHostOnlyAdapters(vbox)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
m, listErr := listHostInterfaces(mhi, nets)
|
||||||
|
assert.Nil(t, listErr)
|
||||||
|
assert.NotEmpty(t, m)
|
||||||
|
|
||||||
|
_, network, cidrErr := net.ParseCIDR("192.168.99.1/24")
|
||||||
|
assert.Nil(t, cidrErr)
|
||||||
|
err = validateNoIPCollisions(mhi, network, nets)
|
||||||
|
assert.Equal(t, ErrNetworkAddrCollision, err)
|
||||||
|
}
|
||||||
|
|
||||||
func TestSetConfigFromFlags(t *testing.T) {
|
func TestSetConfigFromFlags(t *testing.T) {
|
||||||
driver := newTestDriver("default")
|
driver := newTestDriver("default")
|
||||||
|
|
||||||
|
@ -320,6 +388,16 @@ func (v *MockCreateOperations) Sleep(d time.Duration) {
|
||||||
v.doCall("Sleep " + fmt.Sprintf("%v", d))
|
v.doCall("Sleep " + fmt.Sprintf("%v", d))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *MockCreateOperations) Interfaces() ([]net.Interface, error) {
|
||||||
|
_, err := v.doCall("Interfaces")
|
||||||
|
return []net.Interface{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *MockCreateOperations) Addrs(iface *net.Interface) ([]net.Addr, error) {
|
||||||
|
_, err := v.doCall("Addrs " + fmt.Sprintf("%v", iface))
|
||||||
|
return []net.Addr{}, err
|
||||||
|
}
|
||||||
|
|
||||||
func (v *MockCreateOperations) expectCall(callSignature, output string, err error) {
|
func (v *MockCreateOperations) expectCall(callSignature, output string, err error) {
|
||||||
v.expectedCalls = append(v.expectedCalls, Call{
|
v.expectedCalls = append(v.expectedCalls, Call{
|
||||||
signature: callSignature,
|
signature: callSignature,
|
||||||
|
@ -359,6 +437,7 @@ func mockCalls(t *testing.T, driver *Driver, expectedCalls []Call) {
|
||||||
driver.ipWaiter = mockOperations
|
driver.ipWaiter = mockOperations
|
||||||
driver.randomInter = mockOperations
|
driver.randomInter = mockOperations
|
||||||
driver.sleeper = mockOperations
|
driver.sleeper = mockOperations
|
||||||
|
driver.HostInterfaces = mockOperations
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateVM(t *testing.T) {
|
func TestCreateVM(t *testing.T) {
|
||||||
|
@ -396,6 +475,7 @@ func TestStart(t *testing.T) {
|
||||||
mockCalls(t, driver, []Call{
|
mockCalls(t, driver, []Call{
|
||||||
{"vbm showvminfo default --machinereadable", `VMState="poweroff"`, nil},
|
{"vbm showvminfo default --machinereadable", `VMState="poweroff"`, nil},
|
||||||
{"vbm list hostonlyifs", "", nil},
|
{"vbm list hostonlyifs", "", nil},
|
||||||
|
{"Interfaces", "", nil},
|
||||||
{"vbm hostonlyif create", "Interface 'VirtualBox Host-Only Ethernet Adapter' was successfully created", nil},
|
{"vbm hostonlyif create", "Interface 'VirtualBox Host-Only Ethernet Adapter' was successfully created", nil},
|
||||||
{"vbm list hostonlyifs", `
|
{"vbm list hostonlyifs", `
|
||||||
Name: VirtualBox Host-Only Ethernet Adapter
|
Name: VirtualBox Host-Only Ethernet Adapter
|
||||||
|
@ -431,6 +511,7 @@ HardwareAddress: 0a:00:27:00:00:00
|
||||||
MediumType: Ethernet
|
MediumType: Ethernet
|
||||||
Status: Up
|
Status: Up
|
||||||
VBoxNetworkName: HostInterfaceNetworking-VirtualBox Host-Only Ethernet Adapter`, nil},
|
VBoxNetworkName: HostInterfaceNetworking-VirtualBox Host-Only Ethernet Adapter`, nil},
|
||||||
|
{"Interfaces", "", nil},
|
||||||
})
|
})
|
||||||
|
|
||||||
err := driver.Start()
|
err := driver.Start()
|
||||||
|
@ -443,6 +524,7 @@ func TestStartWithHostOnlyAdapterCreationBug(t *testing.T) {
|
||||||
mockCalls(t, driver, []Call{
|
mockCalls(t, driver, []Call{
|
||||||
{"vbm showvminfo default --machinereadable", `VMState="poweroff"`, nil},
|
{"vbm showvminfo default --machinereadable", `VMState="poweroff"`, nil},
|
||||||
{"vbm list hostonlyifs", "", nil},
|
{"vbm list hostonlyifs", "", nil},
|
||||||
|
{"Interfaces", "", nil},
|
||||||
{"vbm hostonlyif create", "", errors.New("error: Failed to create the host-only adapter")},
|
{"vbm hostonlyif create", "", errors.New("error: Failed to create the host-only adapter")},
|
||||||
{"vbm list hostonlyifs", "", nil},
|
{"vbm list hostonlyifs", "", nil},
|
||||||
{"vbm list hostonlyifs", `
|
{"vbm list hostonlyifs", `
|
||||||
|
@ -479,6 +561,7 @@ HardwareAddress: 0a:00:27:00:00:00
|
||||||
MediumType: Ethernet
|
MediumType: Ethernet
|
||||||
Status: Up
|
Status: Up
|
||||||
VBoxNetworkName: HostInterfaceNetworking-VirtualBox Host-Only Ethernet Adapter`, nil},
|
VBoxNetworkName: HostInterfaceNetworking-VirtualBox Host-Only Ethernet Adapter`, nil},
|
||||||
|
{"Interfaces", "", nil},
|
||||||
{"vbm showvminfo default --machinereadable", `VMState="running"`, nil},
|
{"vbm showvminfo default --machinereadable", `VMState="running"`, nil},
|
||||||
{"vbm controlvm default acpipowerbutton", "", nil},
|
{"vbm controlvm default acpipowerbutton", "", nil},
|
||||||
{"vbm showvminfo default --machinereadable", `VMState="stopped"`, nil},
|
{"vbm showvminfo default --machinereadable", `VMState="stopped"`, nil},
|
||||||
|
|
Loading…
Reference in New Issue