github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/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  }
    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 host`,
   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 ::1`,
   232  	}, {
   233  		input: "1.2.3.4",
   234  		err:   `cannot parse "1.2.3.4" as address:port: missing port in address 1.2.3.4`,
   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 foo`)
   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) TestDropDuplicatedHostPorts(c *gc.C) {
   403  	hps := s.makeHostPorts()
   404  	noDups := network.DropDuplicatedHostPorts(hps)
   405  	c.Assert(noDups, gc.Not(gc.HasLen), len(hps))
   406  	c.Assert(noDups, jc.DeepEquals, append(
   407  		network.NewHostPorts(1234,
   408  			"127.0.0.1",
   409  			"localhost",
   410  			"example.com",
   411  			"127.0.1.1",
   412  			"example.org",
   413  			"2001:db8::2",
   414  			"169.254.1.1",
   415  			"example.net",
   416  			"invalid host",
   417  			"fd00::22",
   418  			"2001:db8::1",
   419  			"169.254.1.2",
   420  			"ff01::22",
   421  			"0.1.2.0",
   422  			"10.0.0.1",
   423  			"::1",
   424  			"fc00::1",
   425  			"fe80::2",
   426  			"172.16.0.1",
   427  			"8.8.8.8",
   428  			"7.8.8.8",
   429  		),
   430  		network.NewHostPorts(9999,
   431  			"127.0.0.1",   // machine-local
   432  			"10.0.0.1",    // cloud-local
   433  			"2001:db8::1", // public
   434  			"fe80::2",     // link-local
   435  		)...,
   436  	))
   437  }
   438  
   439  func (s *HostPortSuite) TestHostPortsToStrings(c *gc.C) {
   440  	hps := s.makeHostPorts()
   441  	strHPs := network.HostPortsToStrings(hps)
   442  	c.Assert(strHPs, gc.HasLen, len(hps))
   443  	c.Assert(strHPs, jc.DeepEquals, []string{
   444  		"127.0.0.1:1234",
   445  		"localhost:1234",
   446  		"example.com:1234",
   447  		"127.0.1.1:1234",
   448  		"example.org:1234",
   449  		"[2001:db8::2]:1234",
   450  		"169.254.1.1:1234",
   451  		"example.net:1234",
   452  		"invalid host:1234",
   453  		"[fd00::22]:1234",
   454  		"127.0.0.1:1234",
   455  		"[2001:db8::1]:1234",
   456  		"169.254.1.2:1234",
   457  		"[ff01::22]:1234",
   458  		"0.1.2.0:1234",
   459  		"[2001:db8::1]:1234",
   460  		"localhost:1234",
   461  		"10.0.0.1:1234",
   462  		"[::1]:1234",
   463  		"[fc00::1]:1234",
   464  		"[fe80::2]:1234",
   465  		"172.16.0.1:1234",
   466  		"[::1]:1234",
   467  		"8.8.8.8:1234",
   468  		"7.8.8.8:1234",
   469  		"127.0.0.1:9999",
   470  		"10.0.0.1:9999",
   471  		"[2001:db8::1]:9999",
   472  		"[fe80::2]:9999",
   473  	})
   474  }
   475  
   476  func (*HostPortSuite) makeHostPorts() []network.HostPort {
   477  	return append(
   478  		network.NewHostPorts(1234,
   479  			"127.0.0.1",    // machine-local
   480  			"localhost",    // hostname
   481  			"example.com",  // hostname
   482  			"127.0.1.1",    // machine-local
   483  			"example.org",  // hostname
   484  			"2001:db8::2",  // public
   485  			"169.254.1.1",  // link-local
   486  			"example.net",  // hostname
   487  			"invalid host", // hostname
   488  			"fd00::22",     // cloud-local
   489  			"127.0.0.1",    // machine-local
   490  			"2001:db8::1",  // public
   491  			"169.254.1.2",  // link-local
   492  			"ff01::22",     // link-local
   493  			"0.1.2.0",      // public
   494  			"2001:db8::1",  // public
   495  			"localhost",    // hostname
   496  			"10.0.0.1",     // cloud-local
   497  			"::1",          // machine-local
   498  			"fc00::1",      // cloud-local
   499  			"fe80::2",      // link-local
   500  			"172.16.0.1",   // cloud-local
   501  			"::1",          // machine-local
   502  			"8.8.8.8",      // public
   503  			"7.8.8.8",      // public
   504  		),
   505  		network.NewHostPorts(9999,
   506  			"127.0.0.1",   // machine-local
   507  			"10.0.0.1",    // cloud-local
   508  			"2001:db8::1", // public
   509  			"fe80::2",     // link-local
   510  		)...,
   511  	)
   512  }