github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/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", network.ScopeUnknown), 1234},
   278  			{network.NewAddress("127.0.0.1", network.ScopeUnknown), 4321},
   279  			{network.NewAddress("example.com", network.ScopeUnknown), 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", network.ScopeUnknown),
   294  		Port:    999,
   295  	}, {
   296  		Address: network.NewAddress("0.2.4.6", network.ScopeUnknown),
   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", network.ScopeUnknown),
   389  	port:   99,
   390  	expect: "0.1.2.3:99",
   391  }, {
   392  	addr:   network.NewAddress("2001:DB8::1", network.ScopeUnknown),
   393  	port:   100,
   394  	expect: "[2001:DB8::1]:100",
   395  }, {
   396  	addr:   network.NewAddress("172.16.0.1", network.ScopeUnknown),
   397  	port:   52,
   398  	expect: "172.16.0.1:52",
   399  }, {
   400  	addr:   network.NewAddress("fc00::2", network.ScopeUnknown),
   401  	port:   1111,
   402  	expect: "[fc00::2]:1111",
   403  }, {
   404  	addr:   network.NewAddress("example.com", network.ScopeUnknown),
   405  	port:   9999,
   406  	expect: "example.com:9999",
   407  }, {
   408  	addr:   network.NewAddress("example.com", network.ScopePublic),
   409  	port:   1234,
   410  	expect: "example.com:1234",
   411  }, {
   412  	addr:   network.NewAddress("169.254.1.2", network.ScopeUnknown),
   413  	port:   123,
   414  	expect: "169.254.1.2:123",
   415  }, {
   416  	addr:   network.NewAddress("fe80::222", network.ScopeUnknown),
   417  	port:   321,
   418  	expect: "[fe80::222]:321",
   419  }, {
   420  	addr:   network.NewAddress("127.0.0.2", network.ScopeUnknown),
   421  	port:   121,
   422  	expect: "127.0.0.2:121",
   423  }, {
   424  	addr:   network.NewAddress("::1", network.ScopeUnknown),
   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  }