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 }