github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/network/hostport_test.go (about) 1 // Copyright 2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package network_test 5 6 import ( 7 "fmt" 8 "net" 9 "strings" 10 11 "github.com/juju/errors" 12 jc "github.com/juju/testing/checkers" 13 gc "gopkg.in/check.v1" 14 15 "github.com/juju/juju/network" 16 "github.com/juju/juju/testing" 17 ) 18 19 type HostPortSuite struct { 20 testing.BaseSuite 21 } 22 23 var _ = gc.Suite(&HostPortSuite{}) 24 25 type hostPortTest struct { 26 about string 27 hostPorts []network.HostPort 28 expectedIndex int 29 } 30 31 // hostPortTest returns the HostPort equivalent test to the 32 // receiving selectTest. 33 func (t selectTest) hostPortTest() hostPortTest { 34 hps := network.AddressesWithPort(t.addresses, 9999) 35 for i := range hps { 36 hps[i].Port = i + 1 37 } 38 return hostPortTest{ 39 about: t.about, 40 hostPorts: hps, 41 expectedIndex: t.expectedIndex, 42 } 43 } 44 45 // expected returns the expected host:port result 46 // of the test. 47 func (t hostPortTest) expected() string { 48 if t.expectedIndex == -1 { 49 return "" 50 } 51 return t.hostPorts[t.expectedIndex].NetAddr() 52 } 53 54 func (*HostPortSuite) TestSelectPublicHostPort(c *gc.C) { 55 for i, t0 := range selectPublicTests { 56 t := t0.hostPortTest() 57 c.Logf("test %d: %s", i, t.about) 58 c.Check(network.SelectPublicHostPort(t.hostPorts), jc.DeepEquals, t.expected()) 59 } 60 } 61 62 func (*HostPortSuite) TestSelectInternalHostPort(c *gc.C) { 63 for i, t0 := range selectInternalTests { 64 t := t0.hostPortTest() 65 c.Logf("test %d: %s", i, t.about) 66 c.Check(network.SelectInternalHostPort(t.hostPorts, false), jc.DeepEquals, t.expected()) 67 } 68 } 69 70 func (*HostPortSuite) TestSelectInternalMachineHostPort(c *gc.C) { 71 for i, t0 := range selectInternalMachineTests { 72 t := t0.hostPortTest() 73 c.Logf("test %d: %s", i, t.about) 74 c.Check(network.SelectInternalHostPort(t.hostPorts, true), gc.DeepEquals, t.expected()) 75 } 76 } 77 78 func (s *HostPortSuite) TestResolveOrDropHostnames(c *gc.C) { 79 seq := 0 80 s.PatchValue(network.NetLookupIP, func(host string) ([]net.IP, error) { 81 if host == "invalid host" { 82 return nil, errors.New("lookup invalid host: no such host") 83 } 84 if host == "localhost" { 85 return []net.IP{net.ParseIP("127.0.0.1")}, nil 86 } 87 // Return 2 IPs for .net hosts, 1 IP otherwise. 88 var ips []net.IP 89 ips = append(ips, net.ParseIP(fmt.Sprintf("0.1.2.%d", seq))) 90 seq++ 91 if strings.Contains(host, ".net") { 92 ips = append(ips, net.ParseIP(fmt.Sprintf("0.1.2.%d", seq))) 93 seq++ 94 } 95 c.Logf("lookup host %q -> %v", host, ips) 96 return ips, nil 97 }) 98 resolved := network.ResolveOrDropHostnames(s.makeHostPorts()) 99 c.Assert( 100 c.GetTestLog(), 101 jc.Contains, 102 `DEBUG juju.network removing unresolvable address "invalid host"`, 103 ) 104 // Order should be preserved, duplicates dropped and hostnames, 105 // except localhost resolved or dropped. 106 c.Assert(resolved, jc.DeepEquals, network.NewHostPorts(1234, 107 "127.0.0.1", 108 "localhost", // localhost is not resolved intentionally. 109 "0.1.2.0", // from example.com 110 "127.0.1.1", 111 "0.1.2.1", // from example.org 112 "2001:db8::2", 113 "169.254.1.1", 114 "0.1.2.2", // from example.net 115 "0.1.2.3", // from example.net 116 "fd00::22", 117 "2001:db8::1", 118 "169.254.1.2", 119 "ff01::22", 120 "10.0.0.1", 121 "::1", 122 "fc00::1", 123 "fe80::2", 124 "172.16.0.1", 125 "8.8.8.8", 126 "7.8.8.8", 127 )) 128 } 129 130 func (s *HostPortSuite) TestFilterUnusableHostPorts(c *gc.C) { 131 // The order is preserved, but machine- and link-local addresses 132 // are dropped. 133 expected := append( 134 network.NewHostPorts(1234, 135 "localhost", 136 "example.com", 137 "example.org", 138 "2001:db8::2", 139 "example.net", 140 "invalid host", 141 "fd00::22", 142 "2001:db8::1", 143 "0.1.2.0", 144 "2001:db8::1", 145 "localhost", 146 "10.0.0.1", 147 "fc00::1", 148 "172.16.0.1", 149 "8.8.8.8", 150 "7.8.8.8", 151 ), 152 network.NewHostPorts(9999, 153 "10.0.0.1", 154 "2001:db8::1", // public 155 )..., 156 ) 157 158 result := network.FilterUnusableHostPorts(s.makeHostPorts()) 159 c.Assert(result, gc.HasLen, len(expected)) 160 c.Assert(result, jc.DeepEquals, expected) 161 } 162 163 func (*HostPortSuite) TestCollapseHostPorts(c *gc.C) { 164 servers := [][]network.HostPort{ 165 network.NewHostPorts(1234, 166 "0.1.2.3", "10.0.1.2", "fc00::1", "2001:db8::1", "::1", 167 "127.0.0.1", "localhost", "fe80::123", "example.com", 168 ), 169 network.NewHostPorts(4321, 170 "8.8.8.8", "1.2.3.4", "fc00::2", "127.0.0.1", "foo", 171 ), 172 network.NewHostPorts(9999, 173 "localhost", "127.0.0.1", 174 ), 175 } 176 expected := append(servers[0], append(servers[1], servers[2]...)...) 177 result := network.CollapseHostPorts(servers) 178 c.Assert(result, gc.HasLen, len(servers[0])+len(servers[1])+len(servers[2])) 179 c.Assert(result, jc.DeepEquals, expected) 180 } 181 182 func (s *HostPortSuite) TestEnsureFirstHostPort(c *gc.C) { 183 first := network.NewHostPorts(1234, "1.2.3.4")[0] 184 185 // Without any HostPorts, it still works. 186 hps := network.EnsureFirstHostPort(first, []network.HostPort{}) 187 c.Assert(hps, jc.DeepEquals, []network.HostPort{first}) 188 189 // If already there, no changes happen. 190 hps = s.makeHostPorts() 191 result := network.EnsureFirstHostPort(hps[0], hps) 192 c.Assert(result, jc.DeepEquals, hps) 193 194 // If not at the top, pop it up and put it on top. 195 firstLast := append(hps, first) 196 result = network.EnsureFirstHostPort(first, firstLast) 197 c.Assert(result, jc.DeepEquals, append([]network.HostPort{first}, hps...)) 198 } 199 200 func (*HostPortSuite) TestNewHostPorts(c *gc.C) { 201 addrs := []string{"0.1.2.3", "fc00::1", "::1", "example.com"} 202 expected := network.AddressesWithPort( 203 network.NewAddresses(addrs...), 42, 204 ) 205 result := network.NewHostPorts(42, addrs...) 206 c.Assert(result, gc.HasLen, len(addrs)) 207 c.Assert(result, jc.DeepEquals, expected) 208 } 209 210 func (*HostPortSuite) TestParseHostPortsErrors(c *gc.C) { 211 for i, test := range []struct { 212 input string 213 err string 214 }{{ 215 input: "", 216 err: `cannot parse "" as address:port: missing port in address`, 217 }, { 218 input: " ", 219 err: `cannot parse " " as address:port: missing port in address `, 220 }, { 221 input: ":", 222 err: `cannot parse ":" port: strconv.(ParseInt|Atoi): parsing "": invalid syntax`, 223 }, { 224 input: "host", 225 err: `cannot parse "host" as address:port: missing port in address host`, 226 }, { 227 input: "host:port", 228 err: `cannot parse "host:port" port: strconv.(ParseInt|Atoi): parsing "port": invalid syntax`, 229 }, { 230 input: "::1", 231 err: `cannot parse "::1" as address:port: too many colons in address ::1`, 232 }, { 233 input: "1.2.3.4", 234 err: `cannot parse "1.2.3.4" as address:port: missing port in address 1.2.3.4`, 235 }, { 236 input: "1.2.3.4:foo", 237 err: `cannot parse "1.2.3.4:foo" port: strconv.(ParseInt|Atoi): parsing "foo": invalid syntax`, 238 }} { 239 c.Logf("test %d: input %q", i, test.input) 240 // First test all error cases with a single argument. 241 hps, err := network.ParseHostPorts(test.input) 242 c.Check(err, gc.ErrorMatches, test.err) 243 c.Check(hps, gc.IsNil) 244 } 245 // Finally, test with mixed valid and invalid args. 246 hps, err := network.ParseHostPorts("1.2.3.4:42", "[fc00::1]:12", "foo") 247 c.Assert(err, gc.ErrorMatches, `cannot parse "foo" as address:port: missing port in address foo`) 248 c.Assert(hps, gc.IsNil) 249 } 250 251 func (*HostPortSuite) TestParseHostPortsSuccess(c *gc.C) { 252 for i, test := range []struct { 253 args []string 254 expect []network.HostPort 255 }{{ 256 args: nil, 257 expect: []network.HostPort{}, 258 }, { 259 args: []string{"1.2.3.4:42"}, 260 expect: network.NewHostPorts(42, "1.2.3.4"), 261 }, { 262 args: []string{"[fc00::1]:1234"}, 263 expect: network.NewHostPorts(1234, "fc00::1"), 264 }, { 265 args: []string{"[fc00::1]:1234", "127.0.0.1:4321", "example.com:42"}, 266 expect: []network.HostPort{ 267 {network.NewAddress("fc00::1"), 1234}, 268 {network.NewAddress("127.0.0.1"), 4321}, 269 {network.NewAddress("example.com"), 42}, 270 }, 271 }} { 272 c.Logf("test %d: args %v", i, test.args) 273 hps, err := network.ParseHostPorts(test.args...) 274 c.Check(err, jc.ErrorIsNil) 275 c.Check(hps, jc.DeepEquals, test.expect) 276 } 277 } 278 279 func (*HostPortSuite) TestAddressesWithPortAndHostsWithoutPort(c *gc.C) { 280 addrs := network.NewAddresses("0.1.2.3", "0.2.4.6") 281 hps := network.AddressesWithPort(addrs, 999) 282 c.Assert(hps, jc.DeepEquals, []network.HostPort{{ 283 Address: network.NewAddress("0.1.2.3"), 284 Port: 999, 285 }, { 286 Address: network.NewAddress("0.2.4.6"), 287 Port: 999, 288 }}) 289 c.Assert(network.HostsWithoutPort(hps), jc.DeepEquals, addrs) 290 } 291 292 func (s *HostPortSuite) assertHostPorts(c *gc.C, actual []network.HostPort, expected ...string) { 293 parsed, err := network.ParseHostPorts(expected...) 294 c.Assert(err, jc.ErrorIsNil) 295 c.Assert(actual, jc.DeepEquals, parsed) 296 } 297 298 func (s *HostPortSuite) TestSortHostPorts(c *gc.C) { 299 hps := s.makeHostPorts() 300 network.SortHostPorts(hps) 301 s.assertHostPorts(c, hps, 302 // Public IPv4 addresses on top. 303 "0.1.2.0:1234", 304 "7.8.8.8:1234", 305 "8.8.8.8:1234", 306 // After that public IPv6 addresses. 307 "[2001:db8::1]:1234", 308 "[2001:db8::1]:1234", 309 "[2001:db8::1]:9999", 310 "[2001:db8::2]:1234", 311 // Then hostnames. 312 "example.com:1234", 313 "example.net:1234", 314 "example.org:1234", 315 "invalid host:1234", 316 "localhost:1234", 317 "localhost:1234", 318 // Then IPv4 cloud-local addresses. 319 "10.0.0.1:1234", 320 "10.0.0.1:9999", 321 "172.16.0.1:1234", 322 // Then IPv6 cloud-local addresses. 323 "[fc00::1]:1234", 324 "[fd00::22]:1234", 325 // Then machine-local IPv4 addresses. 326 "127.0.0.1:1234", 327 "127.0.0.1:1234", 328 "127.0.0.1:9999", 329 "127.0.1.1:1234", 330 // Then machine-local IPv6 addresses. 331 "[::1]:1234", 332 "[::1]:1234", 333 // Then link-local IPv4 addresses. 334 "169.254.1.1:1234", 335 "169.254.1.2:1234", 336 // Finally, link-local IPv6 addresses. 337 "[fe80::2]:1234", 338 "[fe80::2]:9999", 339 "[ff01::22]:1234", 340 ) 341 } 342 343 var netAddrTests = []struct { 344 addr network.Address 345 port int 346 expect string 347 }{{ 348 addr: network.NewAddress("0.1.2.3"), 349 port: 99, 350 expect: "0.1.2.3:99", 351 }, { 352 addr: network.NewAddress("2001:DB8::1"), 353 port: 100, 354 expect: "[2001:DB8::1]:100", 355 }, { 356 addr: network.NewAddress("172.16.0.1"), 357 port: 52, 358 expect: "172.16.0.1:52", 359 }, { 360 addr: network.NewAddress("fc00::2"), 361 port: 1111, 362 expect: "[fc00::2]:1111", 363 }, { 364 addr: network.NewAddress("example.com"), 365 port: 9999, 366 expect: "example.com:9999", 367 }, { 368 addr: network.NewScopedAddress("example.com", network.ScopePublic), 369 port: 1234, 370 expect: "example.com:1234", 371 }, { 372 addr: network.NewAddress("169.254.1.2"), 373 port: 123, 374 expect: "169.254.1.2:123", 375 }, { 376 addr: network.NewAddress("fe80::222"), 377 port: 321, 378 expect: "[fe80::222]:321", 379 }, { 380 addr: network.NewAddress("127.0.0.2"), 381 port: 121, 382 expect: "127.0.0.2:121", 383 }, { 384 addr: network.NewAddress("::1"), 385 port: 111, 386 expect: "[::1]:111", 387 }} 388 389 func (*HostPortSuite) TestNetAddrAndString(c *gc.C) { 390 for i, test := range netAddrTests { 391 c.Logf("test %d: %q", i, test.addr) 392 hp := network.HostPort{ 393 Address: test.addr, 394 Port: test.port, 395 } 396 c.Check(hp.NetAddr(), gc.Equals, test.expect) 397 c.Check(hp.String(), gc.Equals, test.expect) 398 c.Check(hp.GoString(), gc.Equals, test.expect) 399 } 400 } 401 402 func (s *HostPortSuite) TestDropDuplicatedHostPorts(c *gc.C) { 403 hps := s.makeHostPorts() 404 noDups := network.DropDuplicatedHostPorts(hps) 405 c.Assert(noDups, gc.Not(gc.HasLen), len(hps)) 406 c.Assert(noDups, jc.DeepEquals, append( 407 network.NewHostPorts(1234, 408 "127.0.0.1", 409 "localhost", 410 "example.com", 411 "127.0.1.1", 412 "example.org", 413 "2001:db8::2", 414 "169.254.1.1", 415 "example.net", 416 "invalid host", 417 "fd00::22", 418 "2001:db8::1", 419 "169.254.1.2", 420 "ff01::22", 421 "0.1.2.0", 422 "10.0.0.1", 423 "::1", 424 "fc00::1", 425 "fe80::2", 426 "172.16.0.1", 427 "8.8.8.8", 428 "7.8.8.8", 429 ), 430 network.NewHostPorts(9999, 431 "127.0.0.1", // machine-local 432 "10.0.0.1", // cloud-local 433 "2001:db8::1", // public 434 "fe80::2", // link-local 435 )..., 436 )) 437 } 438 439 func (s *HostPortSuite) TestHostPortsToStrings(c *gc.C) { 440 hps := s.makeHostPorts() 441 strHPs := network.HostPortsToStrings(hps) 442 c.Assert(strHPs, gc.HasLen, len(hps)) 443 c.Assert(strHPs, jc.DeepEquals, []string{ 444 "127.0.0.1:1234", 445 "localhost:1234", 446 "example.com:1234", 447 "127.0.1.1:1234", 448 "example.org:1234", 449 "[2001:db8::2]:1234", 450 "169.254.1.1:1234", 451 "example.net:1234", 452 "invalid host:1234", 453 "[fd00::22]:1234", 454 "127.0.0.1:1234", 455 "[2001:db8::1]:1234", 456 "169.254.1.2:1234", 457 "[ff01::22]:1234", 458 "0.1.2.0:1234", 459 "[2001:db8::1]:1234", 460 "localhost:1234", 461 "10.0.0.1:1234", 462 "[::1]:1234", 463 "[fc00::1]:1234", 464 "[fe80::2]:1234", 465 "172.16.0.1:1234", 466 "[::1]:1234", 467 "8.8.8.8:1234", 468 "7.8.8.8:1234", 469 "127.0.0.1:9999", 470 "10.0.0.1:9999", 471 "[2001:db8::1]:9999", 472 "[fe80::2]:9999", 473 }) 474 } 475 476 func (*HostPortSuite) makeHostPorts() []network.HostPort { 477 return append( 478 network.NewHostPorts(1234, 479 "127.0.0.1", // machine-local 480 "localhost", // hostname 481 "example.com", // hostname 482 "127.0.1.1", // machine-local 483 "example.org", // hostname 484 "2001:db8::2", // public 485 "169.254.1.1", // link-local 486 "example.net", // hostname 487 "invalid host", // hostname 488 "fd00::22", // cloud-local 489 "127.0.0.1", // machine-local 490 "2001:db8::1", // public 491 "169.254.1.2", // link-local 492 "ff01::22", // link-local 493 "0.1.2.0", // public 494 "2001:db8::1", // public 495 "localhost", // hostname 496 "10.0.0.1", // cloud-local 497 "::1", // machine-local 498 "fc00::1", // cloud-local 499 "fe80::2", // link-local 500 "172.16.0.1", // cloud-local 501 "::1", // machine-local 502 "8.8.8.8", // public 503 "7.8.8.8", // public 504 ), 505 network.NewHostPorts(9999, 506 "127.0.0.1", // machine-local 507 "10.0.0.1", // cloud-local 508 "2001:db8::1", // public 509 "fe80::2", // link-local 510 )..., 511 ) 512 }