github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/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 coretesting "github.com/juju/juju/testing" 17 ) 18 19 type HostPortSuite struct { 20 coretesting.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.*`, 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.*`, 232 }, { 233 input: "1.2.3.4", 234 err: `cannot parse "1.2.3.4" as address:port: .*missing port in address.*`, 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.*`) 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) TestHostPortsToStrings(c *gc.C) { 403 hps := s.makeHostPorts() 404 strHPs := network.HostPortsToStrings(hps) 405 c.Assert(strHPs, gc.HasLen, len(hps)) 406 c.Assert(strHPs, jc.DeepEquals, []string{ 407 "127.0.0.1:1234", 408 "localhost:1234", 409 "example.com:1234", 410 "127.0.1.1:1234", 411 "example.org:1234", 412 "[2001:db8::2]:1234", 413 "169.254.1.1:1234", 414 "example.net:1234", 415 "invalid host:1234", 416 "[fd00::22]:1234", 417 "127.0.0.1:1234", 418 "[2001:db8::1]:1234", 419 "169.254.1.2:1234", 420 "[ff01::22]:1234", 421 "0.1.2.0:1234", 422 "[2001:db8::1]:1234", 423 "localhost:1234", 424 "10.0.0.1:1234", 425 "[::1]:1234", 426 "[fc00::1]:1234", 427 "[fe80::2]:1234", 428 "172.16.0.1:1234", 429 "[::1]:1234", 430 "8.8.8.8:1234", 431 "7.8.8.8:1234", 432 "127.0.0.1:9999", 433 "10.0.0.1:9999", 434 "[2001:db8::1]:9999", 435 "[fe80::2]:9999", 436 }) 437 } 438 439 func (*HostPortSuite) makeHostPorts() []network.HostPort { 440 return append( 441 network.NewHostPorts(1234, 442 "127.0.0.1", // machine-local 443 "localhost", // hostname 444 "example.com", // hostname 445 "127.0.1.1", // machine-local 446 "example.org", // hostname 447 "2001:db8::2", // public 448 "169.254.1.1", // link-local 449 "example.net", // hostname 450 "invalid host", // hostname 451 "fd00::22", // cloud-local 452 "127.0.0.1", // machine-local 453 "2001:db8::1", // public 454 "169.254.1.2", // link-local 455 "ff01::22", // link-local 456 "0.1.2.0", // public 457 "2001:db8::1", // public 458 "localhost", // hostname 459 "10.0.0.1", // cloud-local 460 "::1", // machine-local 461 "fc00::1", // cloud-local 462 "fe80::2", // link-local 463 "172.16.0.1", // cloud-local 464 "::1", // machine-local 465 "8.8.8.8", // public 466 "7.8.8.8", // public 467 ), 468 network.NewHostPorts(9999, 469 "127.0.0.1", // machine-local 470 "10.0.0.1", // cloud-local 471 "2001:db8::1", // public 472 "fe80::2", // link-local 473 )..., 474 ) 475 } 476 477 func (s *HostPortSuite) TestUniqueHostPortsSimpleInput(c *gc.C) { 478 input := network.NewHostPorts(1234, "127.0.0.1", "::1") 479 expected := input 480 481 results := network.UniqueHostPorts(input) 482 c.Assert(results, jc.DeepEquals, expected) 483 } 484 485 func (s *HostPortSuite) TestUniqueHostPortsOnlyDuplicates(c *gc.C) { 486 input := s.manyHostPorts(c, 10000, nil) // use IANA reserved port 487 expected := input[0:1] 488 489 results := network.UniqueHostPorts(input) 490 c.Assert(results, jc.DeepEquals, expected) 491 } 492 493 func (s *HostPortSuite) TestUniqueHostPortsHugeUniqueInput(c *gc.C) { 494 input := s.manyHostPorts(c, maxTCPPort, func(port int) string { 495 return fmt.Sprintf("127.1.0.1:%d", port) 496 }) 497 expected := input 498 499 results := network.UniqueHostPorts(input) 500 c.Assert(results, jc.DeepEquals, expected) 501 } 502 503 const maxTCPPort = 65535 504 505 func (s *HostPortSuite) manyHostPorts(c *gc.C, count int, addressFunc func(index int) string) []network.HostPort { 506 if addressFunc == nil { 507 addressFunc = func(_ int) string { 508 return "127.0.0.1:49151" // all use the same IANA reserved port. 509 } 510 } 511 512 results := make([]network.HostPort, count) 513 for i := range results { 514 hostPort, err := network.ParseHostPort(addressFunc(i)) 515 c.Assert(err, jc.ErrorIsNil) 516 results[i] = *hostPort 517 } 518 return results 519 }