// Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: BUSL-1.1 package autoconf import ( "fmt" "net" "strconv" "strings" "github.com/hashicorp/consul/lib" "github.com/hashicorp/go-discover" discoverk8s "github.com/hashicorp/go-discover/provider/k8s" "github.com/hashicorp/go-hclog" ) func (ac *AutoConfig) discoverServers(servers []string) ([]string, error) { providers := make(map[string]discover.Provider) for k, v := range discover.Providers { providers[k] = v } providers["k8s"] = &discoverk8s.Provider{} disco, err := discover.New( discover.WithUserAgent(lib.UserAgent()), discover.WithProviders(providers), ) if err != nil { return nil, fmt.Errorf("Failed to create go-discover resolver: %w", err) } var addrs []string for _, addr := range servers { switch { case strings.Contains(addr, "provider="): resolved, err := disco.Addrs(addr, ac.logger.StandardLogger(&hclog.StandardLoggerOptions{InferLevels: true})) if err != nil { ac.logger.Error("failed to resolve go-discover auto-config servers", "configuration", addr, "err", err) continue } addrs = append(addrs, resolved...) ac.logger.Debug("discovered auto-config servers", "servers", resolved) default: addrs = append(addrs, addr) } } return addrs, nil } // autoConfigHosts is responsible for taking the list of server addresses // and resolving any go-discover provider invocations. It will then return // a list of hosts. These might be hostnames and is expected that DNS resolution // may be performed after this function runs. Additionally these may contain // ports so SplitHostPort could also be necessary. func (ac *AutoConfig) autoConfigHosts() ([]string, error) { // use servers known to gossip if there are any if ac.acConfig.ServerProvider != nil { if srv := ac.acConfig.ServerProvider.FindLANServer(); srv != nil { return []string{srv.Addr.String()}, nil } } addrs, err := ac.discoverServers(ac.config.AutoConfig.ServerAddresses) if err != nil { return nil, err } if len(addrs) == 0 { return nil, fmt.Errorf("no auto-config server addresses available for use") } return addrs, nil } // resolveHost will take a single host string and convert it to a list of TCPAddrs // This will process any port in the input as well as looking up the hostname using // normal DNS resolution. func (ac *AutoConfig) resolveHost(hostPort string) []net.TCPAddr { port := ac.config.ServerPort host, portStr, err := net.SplitHostPort(hostPort) if err != nil { if strings.Contains(err.Error(), "missing port in address") { host = hostPort } else { ac.logger.Warn("error splitting host address into IP and port", "address", hostPort, "error", err) return nil } } else { port, err = strconv.Atoi(portStr) if err != nil { ac.logger.Warn("Parsed port is not an integer", "port", portStr, "error", err) return nil } } // resolve the host to a list of IPs ips, err := net.LookupIP(host) if err != nil { ac.logger.Warn("IP resolution failed", "host", host, "error", err) return nil } var addrs []net.TCPAddr for _, ip := range ips { addrs = append(addrs, net.TCPAddr{IP: ip, Port: port}) } return addrs }