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