Example #1
0
// SetAPIHostPorts sets the addresses of the API server instances.
// Each server is represented by one element in the top level slice.
// If prefer-ipv6 environment setting is true, the addresses will be
// sorted before setting them to bring IPv6 addresses on top (if
// available).
func (st *State) SetAPIHostPorts(hps [][]network.HostPort) error {
	envConfig, err := st.EnvironConfig()
	if err != nil {
		return err
	}
	for i, _ := range hps {
		network.SortHostPorts(hps[i], envConfig.PreferIPv6())
	}
	doc := apiHostPortsDoc{
		APIHostPorts: instanceHostPortsToHostPorts(hps),
	}
	buildTxn := func(attempt int) ([]txn.Op, error) {
		existing, err := st.APIHostPorts()
		if err != nil {
			return nil, err
		}
		op := txn.Op{
			C:  st.stateServers.Name,
			Id: apiHostPortsKey,
			Assert: bson.D{{
				"apihostports", instanceHostPortsToHostPorts(existing),
			}},
		}
		if !hostPortsEqual(hps, existing) {
			op.Update = bson.D{{
				"$set", bson.D{{"apihostports", doc.APIHostPorts}},
			}}
		}
		return []txn.Op{op}, nil
	}
	if err := st.run(buildTxn); err != nil {
		return errors.Annotate(err, "cannot set API addresses")
	}
	return nil
}
Example #2
0
func (pub *publisher) publishAPIServers(apiServers [][]network.HostPort, instanceIds []instance.Id) error {
	if len(apiServers) == 0 {
		return errors.Errorf("no api servers specified")
	}
	pub.mu.Lock()
	defer pub.mu.Unlock()

	sortedAPIServers := make([][]network.HostPort, len(apiServers))
	for i, hostPorts := range apiServers {
		sortedAPIServers[i] = append([]network.HostPort{}, hostPorts...)
		network.SortHostPorts(sortedAPIServers[i], pub.preferIPv6)
	}
	if apiServersEqual(sortedAPIServers, pub.lastAPIServers) {
		logger.Debugf("API host ports have not changed")
		return nil
	}

	// TODO(rog) publish instanceIds in environment storage.
	err := pub.st.SetAPIHostPorts(sortedAPIServers)
	if err != nil {
		return err
	}
	pub.lastAPIServers = sortedAPIServers
	return nil
}
Example #3
0
File: api.go Project: kat-co/juju
// PrepareEndpointsForCaching performs the necessary operations on the
// given API hostPorts so they are suitable for saving into the
// controller.yaml file, taking into account the addrConnectedTo
// and the existing config store info:
//
// 1. Collapses hostPorts into a single slice.
// 2. Filters out machine-local and link-local addresses.
// 3. Removes any duplicates
// 4. Call network.SortHostPorts() on the list.
// 5. Puts the addrConnectedTo on top.
// 6. Compares the result against info.APIEndpoint.Hostnames.
// 7. If the addresses differ, call network.ResolveOrDropHostnames()
// on the list and perform all steps again from step 1.
// 8. Compare the list of resolved addresses against the cached info
// APIEndpoint.Addresses, and if changed return both addresses and
// hostnames as strings (so they can be cached on APIEndpoint) and
// set haveChanged to true.
// 9. If the hostnames haven't changed, return two empty slices and set
// haveChanged to false. No DNS resolution is performed to save time.
//
// This is used right after bootstrap to saved the initial API
// endpoints, as well as on each CLI connection to verify if the
// saved endpoints need updating.
//
// TODO(rogpeppe) this function mixes too many concerns - the
// logic is difficult to follow and has non-obvious properties.
func PrepareEndpointsForCaching(
	controllerDetails jujuclient.ControllerDetails,
	hostPorts [][]network.HostPort,
	addrConnectedTo ...network.HostPort,
) (addrs, unresolvedAddrs []string, haveChanged bool) {
	processHostPorts := func(allHostPorts [][]network.HostPort) []network.HostPort {
		uniqueHPs := usableHostPorts(allHostPorts)
		network.SortHostPorts(uniqueHPs)
		for _, addr := range addrConnectedTo {
			uniqueHPs = network.EnsureFirstHostPort(addr, uniqueHPs)
		}
		return uniqueHPs
	}

	apiHosts := processHostPorts(hostPorts)
	hostsStrings := network.HostPortsToStrings(apiHosts)
	needResolving := false

	// Verify if the unresolved addresses have changed.
	if len(apiHosts) > 0 && len(controllerDetails.UnresolvedAPIEndpoints) > 0 {
		if addrsChanged(hostsStrings, controllerDetails.UnresolvedAPIEndpoints) {
			logger.Debugf(
				"API hostnames changed from %v to %v - resolving hostnames",
				controllerDetails.UnresolvedAPIEndpoints, hostsStrings,
			)
			needResolving = true
		}
	} else if len(apiHosts) > 0 {
		// No cached hostnames, most likely right after bootstrap.
		logger.Debugf("API hostnames %v - resolving hostnames", hostsStrings)
		needResolving = true
	}
	if !needResolving {
		// We're done - nothing changed.
		logger.Debugf("API hostnames unchanged - not resolving")
		return nil, nil, false
	}
	// Perform DNS resolution and check against APIEndpoints.Addresses.
	resolved := resolveOrDropHostnames(apiHosts)
	apiAddrs := processHostPorts([][]network.HostPort{resolved})
	addrsStrings := network.HostPortsToStrings(apiAddrs)
	if len(apiAddrs) > 0 && len(controllerDetails.APIEndpoints) > 0 {
		if addrsChanged(addrsStrings, controllerDetails.APIEndpoints) {
			logger.Infof(
				"API addresses changed from %v to %v",
				controllerDetails.APIEndpoints, addrsStrings,
			)
			return addrsStrings, hostsStrings, true
		}
	} else if len(apiAddrs) > 0 {
		// No cached addresses, most likely right after bootstrap.
		logger.Infof("new API addresses to cache %v", addrsStrings)
		return addrsStrings, hostsStrings, true
	}
	// No changes.
	logger.Debugf("API addresses unchanged")
	return nil, nil, false
}
Example #4
0
func (*PortSuite) TestSortHostPorts(c *gc.C) {
	hps := network.AddressesWithPort(
		network.NewAddresses(
			"127.0.0.1",
			"localhost",
			"example.com",
			"::1",
			"fc00::1",
			"fe80::2",
			"172.16.0.1",
			"8.8.8.8",
		),
		1234,
	)
	network.SortHostPorts(hps, false)
	c.Assert(hps, jc.DeepEquals, network.AddressesWithPort(
		network.NewAddresses(
			"localhost",
			"example.com",
			"127.0.0.1",
			"172.16.0.1",
			"8.8.8.8",
			"::1",
			"fc00::1",
			"fe80::2",
		),
		1234,
	))

	network.SortHostPorts(hps, true)
	c.Assert(hps, jc.DeepEquals, network.AddressesWithPort(
		network.NewAddresses(
			"localhost",
			"example.com",
			"::1",
			"fc00::1",
			"fe80::2",
			"127.0.0.1",
			"172.16.0.1",
			"8.8.8.8",
		),
		1234,
	))
}
Example #5
0
func (s *HostPortSuite) TestSortHostPorts(c *gc.C) {
	hps := s.makeHostPorts()
	network.SortHostPorts(hps)
	s.assertHostPorts(c, hps,
		// Public IPv4 addresses on top.
		"0.1.2.0:1234",
		"7.8.8.8:1234",
		"8.8.8.8:1234",
		// After that public IPv6 addresses.
		"[2001:db8::1]:1234",
		"[2001:db8::1]:1234",
		"[2001:db8::1]:9999",
		"[2001:db8::2]:1234",
		// Then hostnames.
		"example.com:1234",
		"example.net:1234",
		"example.org:1234",
		"invalid host:1234",
		"localhost:1234",
		"localhost:1234",
		// Then IPv4 cloud-local addresses.
		"10.0.0.1:1234",
		"10.0.0.1:9999",
		"172.16.0.1:1234",
		// Then IPv6 cloud-local addresses.
		"[fc00::1]:1234",
		"[fd00::22]:1234",
		// Then machine-local IPv4 addresses.
		"127.0.0.1:1234",
		"127.0.0.1:1234",
		"127.0.0.1:9999",
		"127.0.1.1:1234",
		// Then machine-local IPv6 addresses.
		"[::1]:1234",
		"[::1]:1234",
		// Then link-local IPv4 addresses.
		"169.254.1.1:1234",
		"169.254.1.2:1234",
		// Finally, link-local IPv6 addresses.
		"[fe80::2]:1234",
		"[fe80::2]:9999",
		"[ff01::22]:1234",
	)
}
Example #6
0
func (s *HostPortSuite) TestSortHostPorts(c *gc.C) {
	hps := s.makeHostPorts()
	// Simulate prefer-ipv6: false first.
	network.SortHostPorts(hps, false)
	c.Assert(hps, jc.DeepEquals, network.NewHostPorts(1234,
		// Public IPv4 addresses on top.
		"0.1.2.0",
		"7.8.8.8",
		"8.8.8.8",
		// After that public IPv6 addresses.
		"2001:db8::1",
		"2001:db8::1",
		"2001:db8::2",
		// Then hostnames.
		"example.com",
		"example.net",
		"example.org",
		"invalid host",
		"localhost",
		"localhost",
		// Then IPv4 cloud-local addresses.
		"10.0.0.1",
		"172.16.0.1",
		// Then IPv6 cloud-local addresses.
		"fc00::1",
		"fd00::22",
		// Then machine-local IPv4 addresses.
		"127.0.0.1",
		"127.0.0.1",
		"127.0.1.1",
		// Then machine-local IPv6 addresses.
		"::1",
		"::1",
		// Then link-local IPv4 addresses.
		"169.254.1.1",
		"169.254.1.2",
		// Finally, link-local IPv6 addresses.
		"fe80::2",
		"ff01::22",
	))

	// Now, simulate prefer-ipv6: true.
	network.SortHostPorts(hps, true)
	c.Assert(hps, jc.DeepEquals, network.NewHostPorts(1234,
		// Public IPv6 addresses on top.
		"2001:db8::1",
		"2001:db8::1",
		"2001:db8::2",
		// After that public IPv4 addresses.
		"0.1.2.0",
		"7.8.8.8",
		"8.8.8.8",
		// Then hostnames.
		"example.com",
		"example.net",
		"example.org",
		"invalid host",
		"localhost",
		"localhost",
		// Then IPv6 cloud-local addresses.
		"fc00::1",
		"fd00::22",
		// Then IPv4 cloud-local addresses.
		"10.0.0.1",
		"172.16.0.1",
		// Then machine-local IPv6 addresses.
		"::1",
		"::1",
		// Then machine-local IPv4 addresses.
		"127.0.0.1",
		"127.0.0.1",
		"127.0.1.1",
		// Then link-local IPv6 addresses.
		"fe80::2",
		"ff01::22",
		// Finally, link-local IPv4 addresses.
		"169.254.1.1",
		"169.254.1.2",
	))
}
Example #7
0
// PrepareEndpointsForCaching performs the necessary operations on the
// given API hostPorts so they are suitable for caching into the
// environment's .jenv file, taking into account the addrConnectedTo
// and the existing config store info:
//
// 1. Collapses hostPorts into a single slice.
// 2. Filters out machine-local and link-local addresses.
// 3. Removes any duplicates
// 4. Call network.SortHostPorts() on the list, respecing prefer-ipv6
// flag.
// 5. Puts the addrConnectedTo on top.
// 6. Compares the result against info.APIEndpoint.Hostnames.
// 7. If the addresses differ, call network.ResolveOrDropHostnames()
// on the list and perform all steps again from step 1.
// 8. Compare the list of resolved addresses against the cached info
// APIEndpoint.Addresses, and if changed return both addresses and
// hostnames as strings (so they can be cached on APIEndpoint) and
// set haveChanged to true.
// 9. If the hostnames haven't changed, return two empty slices and set
// haveChanged to false. No DNS resolution is performed to save time.
//
// This is used right after bootstrap to cache the initial API
// endpoints, as well as on each CLI connection to verify if the
// cached endpoints need updating.
func PrepareEndpointsForCaching(info configstore.EnvironInfo, hostPorts [][]network.HostPort, addrConnectedTo network.HostPort) (addresses, hostnames []string, haveChanged bool) {
	processHostPorts := func(allHostPorts [][]network.HostPort) []network.HostPort {
		collapsedHPs := network.CollapseHostPorts(allHostPorts)
		filteredHPs := network.FilterUnusableHostPorts(collapsedHPs)
		uniqueHPs := network.DropDuplicatedHostPorts(filteredHPs)
		// Sort the result to prefer public IPs on top (when prefer-ipv6
		// is true, IPv6 addresses of the same scope will come before IPv4
		// ones).
		preferIPv6 := maybePreferIPv6(info)
		network.SortHostPorts(uniqueHPs, preferIPv6)

		if addrConnectedTo.Value != "" {
			return network.EnsureFirstHostPort(addrConnectedTo, uniqueHPs)
		}
		// addrConnectedTo can be empty only right after bootstrap.
		return uniqueHPs
	}

	apiHosts := processHostPorts(hostPorts)
	hostsStrings := network.HostPortsToStrings(apiHosts)
	endpoint := info.APIEndpoint()
	needResolving := false

	// Verify if the unresolved addresses have changed.
	if len(apiHosts) > 0 && len(endpoint.Hostnames) > 0 {
		if addrsChanged(hostsStrings, endpoint.Hostnames) {
			logger.Debugf(
				"API hostnames changed from %v to %v - resolving hostnames",
				endpoint.Hostnames, hostsStrings,
			)
			needResolving = true
		}
	} else if len(apiHosts) > 0 {
		// No cached hostnames, most likely right after bootstrap.
		logger.Debugf("API hostnames %v - resolving hostnames", hostsStrings)
		needResolving = true
	}
	if !needResolving {
		// We're done - nothing changed.
		logger.Debugf("API hostnames unchanged - not resolving")
		return nil, nil, false
	}
	// Perform DNS resolution and check against APIEndpoints.Addresses.
	resolved := resolveOrDropHostnames(apiHosts)
	apiAddrs := processHostPorts([][]network.HostPort{resolved})
	addrsStrings := network.HostPortsToStrings(apiAddrs)
	if len(apiAddrs) > 0 && len(endpoint.Addresses) > 0 {
		if addrsChanged(addrsStrings, endpoint.Addresses) {
			logger.Infof(
				"API addresses changed from %v to %v",
				endpoint.Addresses, addrsStrings,
			)
			return addrsStrings, hostsStrings, true
		}
	} else if len(apiAddrs) > 0 {
		// No cached addresses, most likely right after bootstrap.
		logger.Infof("new API addresses to cache %v", addrsStrings)
		return addrsStrings, hostsStrings, true
	}
	// No changes.
	logger.Debugf("API addresses unchanged")
	return nil, nil, false
}