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  }