github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/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.GetPreferIPv6() 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.GetPreferIPv6() 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.GetPreferIPv6() 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 := network.NewHostPorts(1234, 151 "localhost", 152 "example.com", 153 "example.org", 154 "2001:db8::2", 155 "example.net", 156 "invalid host", 157 "fd00::22", 158 "2001:db8::1", 159 "0.1.2.0", 160 "2001:db8::1", 161 "localhost", 162 "10.0.0.1", 163 "fc00::1", 164 "172.16.0.1", 165 "8.8.8.8", 166 "7.8.8.8", 167 ) 168 result := network.FilterUnusableHostPorts(s.makeHostPorts()) 169 c.Assert(result, gc.HasLen, len(expected)) 170 c.Assert(result, jc.DeepEquals, expected) 171 } 172 173 func (*HostPortSuite) TestCollapseHostPorts(c *gc.C) { 174 servers := [][]network.HostPort{ 175 network.NewHostPorts(1234, 176 "0.1.2.3", "10.0.1.2", "fc00::1", "2001:db8::1", "::1", 177 "127.0.0.1", "localhost", "fe80::123", "example.com", 178 ), 179 network.NewHostPorts(4321, 180 "8.8.8.8", "1.2.3.4", "fc00::2", "127.0.0.1", "foo", 181 ), 182 network.NewHostPorts(9999, 183 "localhost", "127.0.0.1", 184 ), 185 } 186 expected := append(servers[0], append(servers[1], servers[2]...)...) 187 result := network.CollapseHostPorts(servers) 188 c.Assert(result, gc.HasLen, len(servers[0])+len(servers[1])+len(servers[2])) 189 c.Assert(result, jc.DeepEquals, expected) 190 } 191 192 func (s *HostPortSuite) TestEnsureFirstHostPort(c *gc.C) { 193 first := network.NewHostPorts(1234, "1.2.3.4")[0] 194 195 // Without any HostPorts, it still works. 196 hps := network.EnsureFirstHostPort(first, []network.HostPort{}) 197 c.Assert(hps, jc.DeepEquals, []network.HostPort{first}) 198 199 // If already there, no changes happen. 200 hps = s.makeHostPorts() 201 result := network.EnsureFirstHostPort(hps[0], hps) 202 c.Assert(result, jc.DeepEquals, hps) 203 204 // If not at the top, pop it up and put it on top. 205 firstLast := append(hps, first) 206 result = network.EnsureFirstHostPort(first, firstLast) 207 c.Assert(result, jc.DeepEquals, append([]network.HostPort{first}, hps...)) 208 } 209 210 func (*HostPortSuite) TestNewHostPorts(c *gc.C) { 211 addrs := []string{"0.1.2.3", "fc00::1", "::1", "example.com"} 212 expected := network.AddressesWithPort( 213 network.NewAddresses(addrs...), 42, 214 ) 215 result := network.NewHostPorts(42, addrs...) 216 c.Assert(result, gc.HasLen, len(addrs)) 217 c.Assert(result, jc.DeepEquals, expected) 218 } 219 220 func (*HostPortSuite) TestParseHostPortsErrors(c *gc.C) { 221 for i, test := range []struct { 222 input string 223 err string 224 }{{ 225 input: "", 226 err: `cannot parse "" as address:port: missing port in address`, 227 }, { 228 input: " ", 229 err: `cannot parse " " as address:port: missing port in address `, 230 }, { 231 input: ":", 232 err: `cannot parse ":" port: strconv.ParseInt: parsing "": invalid syntax`, 233 }, { 234 input: "host", 235 err: `cannot parse "host" as address:port: missing port in address host`, 236 }, { 237 input: "host:port", 238 err: `cannot parse "host:port" port: strconv.ParseInt: parsing "port": invalid syntax`, 239 }, { 240 input: "::1", 241 err: `cannot parse "::1" as address:port: too many colons in address ::1`, 242 }, { 243 input: "1.2.3.4", 244 err: `cannot parse "1.2.3.4" as address:port: missing port in address 1.2.3.4`, 245 }, { 246 input: "1.2.3.4:foo", 247 err: `cannot parse "1.2.3.4:foo" port: strconv.ParseInt: parsing "foo": invalid syntax`, 248 }} { 249 c.Logf("test %d: input %q", i, test.input) 250 // First test all error cases with a single argument. 251 hps, err := network.ParseHostPorts(test.input) 252 c.Check(err, gc.ErrorMatches, test.err) 253 c.Check(hps, gc.IsNil) 254 } 255 // Finally, test with mixed valid and invalid args. 256 hps, err := network.ParseHostPorts("1.2.3.4:42", "[fc00::1]:12", "foo") 257 c.Assert(err, gc.ErrorMatches, `cannot parse "foo" as address:port: missing port in address foo`) 258 c.Assert(hps, gc.IsNil) 259 } 260 261 func (*HostPortSuite) TestParseHostPortsSuccess(c *gc.C) { 262 for i, test := range []struct { 263 args []string 264 expect []network.HostPort 265 }{{ 266 args: nil, 267 expect: []network.HostPort{}, 268 }, { 269 args: []string{"1.2.3.4:42"}, 270 expect: network.NewHostPorts(42, "1.2.3.4"), 271 }, { 272 args: []string{"[fc00::1]:1234"}, 273 expect: network.NewHostPorts(1234, "fc00::1"), 274 }, { 275 args: []string{"[fc00::1]:1234", "127.0.0.1:4321", "example.com:42"}, 276 expect: []network.HostPort{ 277 {network.NewAddress("fc00::1"), 1234}, 278 {network.NewAddress("127.0.0.1"), 4321}, 279 {network.NewAddress("example.com"), 42}, 280 }, 281 }} { 282 c.Logf("test %d: args %v", i, test.args) 283 hps, err := network.ParseHostPorts(test.args...) 284 c.Check(err, jc.ErrorIsNil) 285 c.Check(hps, jc.DeepEquals, test.expect) 286 } 287 } 288 289 func (*HostPortSuite) TestAddressesWithPortAndHostsWithoutPort(c *gc.C) { 290 addrs := network.NewAddresses("0.1.2.3", "0.2.4.6") 291 hps := network.AddressesWithPort(addrs, 999) 292 c.Assert(hps, jc.DeepEquals, []network.HostPort{{ 293 Address: network.NewAddress("0.1.2.3"), 294 Port: 999, 295 }, { 296 Address: network.NewAddress("0.2.4.6"), 297 Port: 999, 298 }}) 299 c.Assert(network.HostsWithoutPort(hps), jc.DeepEquals, addrs) 300 } 301 302 func (s *HostPortSuite) TestSortHostPorts(c *gc.C) { 303 hps := s.makeHostPorts() 304 // Simulate prefer-ipv6: false first. 305 network.SortHostPorts(hps, false) 306 c.Assert(hps, jc.DeepEquals, network.NewHostPorts(1234, 307 // Public IPv4 addresses on top. 308 "0.1.2.0", 309 "7.8.8.8", 310 "8.8.8.8", 311 // After that public IPv6 addresses. 312 "2001:db8::1", 313 "2001:db8::1", 314 "2001:db8::2", 315 // Then hostnames. 316 "example.com", 317 "example.net", 318 "example.org", 319 "invalid host", 320 "localhost", 321 "localhost", 322 // Then IPv4 cloud-local addresses. 323 "10.0.0.1", 324 "172.16.0.1", 325 // Then IPv6 cloud-local addresses. 326 "fc00::1", 327 "fd00::22", 328 // Then machine-local IPv4 addresses. 329 "127.0.0.1", 330 "127.0.0.1", 331 "127.0.1.1", 332 // Then machine-local IPv6 addresses. 333 "::1", 334 "::1", 335 // Then link-local IPv4 addresses. 336 "169.254.1.1", 337 "169.254.1.2", 338 // Finally, link-local IPv6 addresses. 339 "fe80::2", 340 "ff01::22", 341 )) 342 343 // Now, simulate prefer-ipv6: true. 344 network.SortHostPorts(hps, true) 345 c.Assert(hps, jc.DeepEquals, network.NewHostPorts(1234, 346 // Public IPv6 addresses on top. 347 "2001:db8::1", 348 "2001:db8::1", 349 "2001:db8::2", 350 // After that public IPv4 addresses. 351 "0.1.2.0", 352 "7.8.8.8", 353 "8.8.8.8", 354 // Then hostnames. 355 "example.com", 356 "example.net", 357 "example.org", 358 "invalid host", 359 "localhost", 360 "localhost", 361 // Then IPv6 cloud-local addresses. 362 "fc00::1", 363 "fd00::22", 364 // Then IPv4 cloud-local addresses. 365 "10.0.0.1", 366 "172.16.0.1", 367 // Then machine-local IPv6 addresses. 368 "::1", 369 "::1", 370 // Then machine-local IPv4 addresses. 371 "127.0.0.1", 372 "127.0.0.1", 373 "127.0.1.1", 374 // Then link-local IPv6 addresses. 375 "fe80::2", 376 "ff01::22", 377 // Finally, link-local IPv4 addresses. 378 "169.254.1.1", 379 "169.254.1.2", 380 )) 381 } 382 383 var netAddrTests = []struct { 384 addr network.Address 385 port int 386 expect string 387 }{{ 388 addr: network.NewAddress("0.1.2.3"), 389 port: 99, 390 expect: "0.1.2.3:99", 391 }, { 392 addr: network.NewAddress("2001:DB8::1"), 393 port: 100, 394 expect: "[2001:DB8::1]:100", 395 }, { 396 addr: network.NewAddress("172.16.0.1"), 397 port: 52, 398 expect: "172.16.0.1:52", 399 }, { 400 addr: network.NewAddress("fc00::2"), 401 port: 1111, 402 expect: "[fc00::2]:1111", 403 }, { 404 addr: network.NewAddress("example.com"), 405 port: 9999, 406 expect: "example.com:9999", 407 }, { 408 addr: network.NewScopedAddress("example.com", network.ScopePublic), 409 port: 1234, 410 expect: "example.com:1234", 411 }, { 412 addr: network.NewAddress("169.254.1.2"), 413 port: 123, 414 expect: "169.254.1.2:123", 415 }, { 416 addr: network.NewAddress("fe80::222"), 417 port: 321, 418 expect: "[fe80::222]:321", 419 }, { 420 addr: network.NewAddress("127.0.0.2"), 421 port: 121, 422 expect: "127.0.0.2:121", 423 }, { 424 addr: network.NewAddress("::1"), 425 port: 111, 426 expect: "[::1]:111", 427 }} 428 429 func (*HostPortSuite) TestNetAddrAndString(c *gc.C) { 430 for i, test := range netAddrTests { 431 c.Logf("test %d: %q", i, test.addr) 432 hp := network.HostPort{ 433 Address: test.addr, 434 Port: test.port, 435 } 436 c.Check(hp.NetAddr(), gc.Equals, test.expect) 437 c.Check(hp.String(), gc.Equals, test.expect) 438 c.Check(hp.GoString(), gc.Equals, test.expect) 439 } 440 } 441 442 func (s *HostPortSuite) TestDropDuplicatedHostPorts(c *gc.C) { 443 hps := s.makeHostPorts() 444 noDups := network.DropDuplicatedHostPorts(hps) 445 c.Assert(noDups, gc.Not(gc.HasLen), len(hps)) 446 c.Assert(noDups, jc.DeepEquals, network.NewHostPorts(1234, 447 "127.0.0.1", 448 "localhost", 449 "example.com", 450 "127.0.1.1", 451 "example.org", 452 "2001:db8::2", 453 "169.254.1.1", 454 "example.net", 455 "invalid host", 456 "fd00::22", 457 "2001:db8::1", 458 "169.254.1.2", 459 "ff01::22", 460 "0.1.2.0", 461 "10.0.0.1", 462 "::1", 463 "fc00::1", 464 "fe80::2", 465 "172.16.0.1", 466 "8.8.8.8", 467 "7.8.8.8", 468 )) 469 } 470 471 func (s *HostPortSuite) TestHostPortsToStrings(c *gc.C) { 472 hps := s.makeHostPorts() 473 strHPs := network.HostPortsToStrings(hps) 474 c.Assert(strHPs, gc.HasLen, len(hps)) 475 c.Assert(strHPs, jc.DeepEquals, []string{ 476 "127.0.0.1:1234", 477 "localhost:1234", 478 "example.com:1234", 479 "127.0.1.1:1234", 480 "example.org:1234", 481 "[2001:db8::2]:1234", 482 "169.254.1.1:1234", 483 "example.net:1234", 484 "invalid host:1234", 485 "[fd00::22]:1234", 486 "127.0.0.1:1234", 487 "[2001:db8::1]:1234", 488 "169.254.1.2:1234", 489 "[ff01::22]:1234", 490 "0.1.2.0:1234", 491 "[2001:db8::1]:1234", 492 "localhost:1234", 493 "10.0.0.1:1234", 494 "[::1]:1234", 495 "[fc00::1]:1234", 496 "[fe80::2]:1234", 497 "172.16.0.1:1234", 498 "[::1]:1234", 499 "8.8.8.8:1234", 500 "7.8.8.8:1234", 501 }) 502 } 503 504 func (*HostPortSuite) makeHostPorts() []network.HostPort { 505 return network.NewHostPorts(1234, 506 "127.0.0.1", // machine-local 507 "localhost", // hostname 508 "example.com", // hostname 509 "127.0.1.1", // machine-local 510 "example.org", // hostname 511 "2001:db8::2", // public 512 "169.254.1.1", // link-local 513 "example.net", // hostname 514 "invalid host", // hostname 515 "fd00::22", // cloud-local 516 "127.0.0.1", // machine-local 517 "2001:db8::1", // public 518 "169.254.1.2", // link-local 519 "ff01::22", // link-local 520 "0.1.2.0", // public 521 "2001:db8::1", // public 522 "localhost", // hostname 523 "10.0.0.1", // cloud-local 524 "::1", // machine-local 525 "fc00::1", // cloud-local 526 "fe80::2", // link-local 527 "172.16.0.1", // cloud-local 528 "::1", // machine-local 529 "8.8.8.8", // public 530 "7.8.8.8", // public 531 ) 532 }