// 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 }
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 }
// 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 }
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, )) }
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", ) }
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", )) }
// 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 }