github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/network/utils_test.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  // +build !windows
     5  
     6  package network_test
     7  
     8  import (
     9  	"errors"
    10  	"fmt"
    11  	"io/ioutil"
    12  	"net"
    13  	"os"
    14  	"path/filepath"
    15  	"strings"
    16  
    17  	"github.com/juju/testing"
    18  	jc "github.com/juju/testing/checkers"
    19  	gc "gopkg.in/check.v1"
    20  
    21  	"github.com/juju/juju/network"
    22  )
    23  
    24  type UtilsSuite struct {
    25  	testing.IsolationSuite
    26  }
    27  
    28  var _ = gc.Suite(&UtilsSuite{})
    29  
    30  func (*UtilsSuite) TestParseResolvConfEmptyOrMissingPath(c *gc.C) {
    31  	emptyPath := ""
    32  	missingPath := filepath.Join(c.MkDir(), "missing")
    33  
    34  	for _, path := range []string{emptyPath, missingPath} {
    35  		result, err := network.ParseResolvConf(path)
    36  		c.Check(err, jc.ErrorIsNil)
    37  		c.Check(result, gc.IsNil)
    38  	}
    39  }
    40  
    41  func (*UtilsSuite) TestParseResolvConfNotReadablePath(c *gc.C) {
    42  	unreadableConf := makeResolvConf(c, "#empty", 0000)
    43  	result, err := network.ParseResolvConf(unreadableConf)
    44  	expected := fmt.Sprintf("open %s: permission denied", unreadableConf)
    45  	c.Check(err, gc.ErrorMatches, expected)
    46  	c.Check(result, gc.IsNil)
    47  }
    48  
    49  func makeResolvConf(c *gc.C, content string, perms os.FileMode) string {
    50  	fakeConfPath := filepath.Join(c.MkDir(), "fake")
    51  	err := ioutil.WriteFile(fakeConfPath, []byte(content), perms)
    52  	c.Check(err, jc.ErrorIsNil)
    53  	return fakeConfPath
    54  }
    55  
    56  func (*UtilsSuite) TestParseResolvConfEmptyFile(c *gc.C) {
    57  	emptyConf := makeResolvConf(c, "", 0644)
    58  	result, err := network.ParseResolvConf(emptyConf)
    59  	c.Check(err, jc.ErrorIsNil)
    60  	// Expected non-nil, but empty result.
    61  	c.Check(result, jc.DeepEquals, &network.DNSConfig{})
    62  }
    63  
    64  func (*UtilsSuite) TestParseResolvConfCommentsAndWhitespaceHandling(c *gc.C) {
    65  	const exampleConf = `
    66    ;; comment
    67  # also comment
    68  ;# ditto
    69    #nameserver ;still comment
    70  
    71    search    foo example.com       bar.     ;comment, leading/trailing ignored
    72  nameserver 8.8.8.8 #comment #still the same comment
    73  `
    74  	fakeConf := makeResolvConf(c, exampleConf, 0644)
    75  	result, err := network.ParseResolvConf(fakeConf)
    76  	c.Check(err, jc.ErrorIsNil)
    77  	c.Check(result, jc.DeepEquals, &network.DNSConfig{
    78  		Nameservers:   network.NewAddresses("8.8.8.8"),
    79  		SearchDomains: []string{"foo", "example.com", "bar."},
    80  	})
    81  }
    82  
    83  func (*UtilsSuite) TestParseResolvConfSearchWithoutValue(c *gc.C) {
    84  	badConf := makeResolvConf(c, "search # no value\n", 0644)
    85  	result, err := network.ParseResolvConf(badConf)
    86  	c.Check(err, gc.ErrorMatches, `parsing ".*", line 1: "search": required value\(s\) missing`)
    87  	c.Check(result, gc.IsNil)
    88  }
    89  
    90  func (*UtilsSuite) TestParseResolvConfNameserverWithoutValue(c *gc.C) {
    91  	badConf := makeResolvConf(c, "nameserver", 0644)
    92  	result, err := network.ParseResolvConf(badConf)
    93  	c.Check(err, gc.ErrorMatches, `parsing ".*", line 1: "nameserver": required value\(s\) missing`)
    94  	c.Check(result, gc.IsNil)
    95  }
    96  
    97  func (*UtilsSuite) TestParseResolvConfValueFollowedByCommentWithoutWhitespace(c *gc.C) {
    98  	badConf := makeResolvConf(c, "search foo bar#bad rest;is#ignored: still part of the comment", 0644)
    99  	result, err := network.ParseResolvConf(badConf)
   100  	c.Check(err, gc.ErrorMatches, `parsing ".*", line 1: "search": invalid value "bar#bad"`)
   101  	c.Check(result, gc.IsNil)
   102  }
   103  
   104  func (*UtilsSuite) TestParseResolvConfNameserverWithMultipleValues(c *gc.C) {
   105  	badConf := makeResolvConf(c, "nameserver one two 42 ;;; comment still-inside-comment\n", 0644)
   106  	result, err := network.ParseResolvConf(badConf)
   107  	c.Check(err, gc.ErrorMatches, `parsing ".*", line 1: one value expected for "nameserver", got 3`)
   108  	c.Check(result, gc.IsNil)
   109  }
   110  
   111  func (*UtilsSuite) TestParseResolvConfLastSearchWins(c *gc.C) {
   112  	const multiSearchConf = `
   113  search zero five
   114  search one
   115  # this below overrides all of the above
   116  search two three #comment ;also-comment still-comment
   117  `
   118  	fakeConf := makeResolvConf(c, multiSearchConf, 0644)
   119  	result, err := network.ParseResolvConf(fakeConf)
   120  	c.Check(err, jc.ErrorIsNil)
   121  	c.Check(result, jc.DeepEquals, &network.DNSConfig{
   122  		SearchDomains: []string{"two", "three"},
   123  	})
   124  }
   125  
   126  func (s *UtilsSuite) TestSupportsIPv6Error(c *gc.C) {
   127  	s.PatchValue(network.NetListen, func(netFamily, bindAddress string) (net.Listener, error) {
   128  		c.Check(netFamily, gc.Equals, "tcp6")
   129  		c.Check(bindAddress, gc.Equals, "[::1]:0")
   130  		return nil, errors.New("boom!")
   131  	})
   132  	c.Check(network.SupportsIPv6(), jc.IsFalse)
   133  }
   134  
   135  func (s *UtilsSuite) TestSupportsIPv6OK(c *gc.C) {
   136  	s.PatchValue(network.NetListen, func(_, _ string) (net.Listener, error) {
   137  		return &mockListener{}, nil
   138  	})
   139  	c.Check(network.SupportsIPv6(), jc.IsTrue)
   140  }
   141  
   142  func (*UtilsSuite) TestParseInterfaceType(c *gc.C) {
   143  	fakeSysPath := filepath.Join(c.MkDir(), network.SysClassNetPath)
   144  	err := os.MkdirAll(fakeSysPath, 0700)
   145  	c.Check(err, jc.ErrorIsNil)
   146  
   147  	writeFakeUEvent := func(interfaceName string, lines ...string) string {
   148  		fakeInterfacePath := filepath.Join(fakeSysPath, interfaceName)
   149  		err := os.MkdirAll(fakeInterfacePath, 0700)
   150  		c.Check(err, jc.ErrorIsNil)
   151  
   152  		fakeUEventPath := filepath.Join(fakeInterfacePath, "uevent")
   153  		contents := strings.Join(lines, "\n")
   154  		err = ioutil.WriteFile(fakeUEventPath, []byte(contents), 0644)
   155  		c.Check(err, jc.ErrorIsNil)
   156  		return fakeUEventPath
   157  	}
   158  
   159  	result := network.ParseInterfaceType(fakeSysPath, "missing")
   160  	c.Check(result, gc.Equals, network.UnknownInterface)
   161  
   162  	writeFakeUEvent("eth0", "IFINDEX=1", "INTERFACE=eth0")
   163  	result = network.ParseInterfaceType(fakeSysPath, "eth0")
   164  	c.Check(result, gc.Equals, network.UnknownInterface)
   165  
   166  	fakeUEventPath := writeFakeUEvent("eth0.42", "DEVTYPE=vlan")
   167  	result = network.ParseInterfaceType(fakeSysPath, "eth0.42")
   168  	c.Check(result, gc.Equals, network.VLAN_8021QInterface)
   169  
   170  	os.Chmod(fakeUEventPath, 0000) // permission denied error is OK
   171  	result = network.ParseInterfaceType(fakeSysPath, "eth0.42")
   172  	c.Check(result, gc.Equals, network.UnknownInterface)
   173  
   174  	writeFakeUEvent("bond0", "DEVTYPE=bond")
   175  	result = network.ParseInterfaceType(fakeSysPath, "bond0")
   176  	c.Check(result, gc.Equals, network.BondInterface)
   177  
   178  	writeFakeUEvent("br-ens4", "DEVTYPE=bridge")
   179  	result = network.ParseInterfaceType(fakeSysPath, "br-ens4")
   180  	c.Check(result, gc.Equals, network.BridgeInterface)
   181  
   182  	// First DEVTYPE found wins.
   183  	writeFakeUEvent("foo", "DEVTYPE=vlan", "DEVTYPE=bridge")
   184  	result = network.ParseInterfaceType(fakeSysPath, "foo")
   185  	c.Check(result, gc.Equals, network.VLAN_8021QInterface)
   186  
   187  	writeFakeUEvent("fake", "DEVTYPE=warp-drive")
   188  	result = network.ParseInterfaceType(fakeSysPath, "fake")
   189  	c.Check(result, gc.Equals, network.UnknownInterface)
   190  }
   191  
   192  func (*UtilsSuite) TestGetBridgePorts(c *gc.C) {
   193  	fakeSysPath := filepath.Join(c.MkDir(), network.SysClassNetPath)
   194  	err := os.MkdirAll(fakeSysPath, 0700)
   195  	c.Check(err, jc.ErrorIsNil)
   196  
   197  	writeFakePorts := func(bridgeName string, portNames ...string) {
   198  		fakePortsPath := filepath.Join(fakeSysPath, bridgeName, "brif")
   199  		err := os.MkdirAll(fakePortsPath, 0700)
   200  		c.Check(err, jc.ErrorIsNil)
   201  
   202  		for _, portName := range portNames {
   203  			portPath := filepath.Join(fakePortsPath, portName)
   204  			err = ioutil.WriteFile(portPath, []byte(""), 0644)
   205  			c.Check(err, jc.ErrorIsNil)
   206  		}
   207  	}
   208  
   209  	result := network.GetBridgePorts(fakeSysPath, "missing")
   210  	c.Check(result, gc.IsNil)
   211  
   212  	writeFakePorts("br-eth0")
   213  	result = network.GetBridgePorts(fakeSysPath, "br-eth0")
   214  	c.Check(result, gc.IsNil)
   215  
   216  	writeFakePorts("br-eth0", "eth0")
   217  	result = network.GetBridgePorts(fakeSysPath, "br-eth0")
   218  	c.Check(result, jc.DeepEquals, []string{"eth0"})
   219  
   220  	writeFakePorts("br-ovs", "eth0", "eth1", "eth2")
   221  	result = network.GetBridgePorts(fakeSysPath, "br-ovs")
   222  	c.Check(result, jc.DeepEquals, []string{"eth0", "eth1", "eth2"})
   223  }
   224  
   225  type mockListener struct {
   226  	net.Listener
   227  }
   228  
   229  func (*mockListener) Close() error {
   230  	return nil
   231  }