1package autoconf 2 3import ( 4 "fmt" 5 "net" 6 "strconv" 7 "strings" 8 9 "github.com/hashicorp/consul/lib" 10 "github.com/hashicorp/go-discover" 11 discoverk8s "github.com/hashicorp/go-discover/provider/k8s" 12 13 "github.com/hashicorp/go-hclog" 14) 15 16func (ac *AutoConfig) discoverServers(servers []string) ([]string, error) { 17 providers := make(map[string]discover.Provider) 18 for k, v := range discover.Providers { 19 providers[k] = v 20 } 21 providers["k8s"] = &discoverk8s.Provider{} 22 23 disco, err := discover.New( 24 discover.WithUserAgent(lib.UserAgent()), 25 discover.WithProviders(providers), 26 ) 27 28 if err != nil { 29 return nil, fmt.Errorf("Failed to create go-discover resolver: %w", err) 30 } 31 32 var addrs []string 33 for _, addr := range servers { 34 switch { 35 case strings.Contains(addr, "provider="): 36 resolved, err := disco.Addrs(addr, ac.logger.StandardLogger(&hclog.StandardLoggerOptions{InferLevels: true})) 37 if err != nil { 38 ac.logger.Error("failed to resolve go-discover auto-config servers", "configuration", addr, "err", err) 39 continue 40 } 41 42 addrs = append(addrs, resolved...) 43 ac.logger.Debug("discovered auto-config servers", "servers", resolved) 44 default: 45 addrs = append(addrs, addr) 46 } 47 } 48 49 return addrs, nil 50} 51 52// autoConfigHosts is responsible for taking the list of server addresses 53// and resolving any go-discover provider invocations. It will then return 54// a list of hosts. These might be hostnames and is expected that DNS resolution 55// may be performed after this function runs. Additionally these may contain 56// ports so SplitHostPort could also be necessary. 57func (ac *AutoConfig) autoConfigHosts() ([]string, error) { 58 // use servers known to gossip if there are any 59 if ac.acConfig.ServerProvider != nil { 60 if srv := ac.acConfig.ServerProvider.FindLANServer(); srv != nil { 61 return []string{srv.Addr.String()}, nil 62 } 63 } 64 65 addrs, err := ac.discoverServers(ac.config.AutoConfig.ServerAddresses) 66 if err != nil { 67 return nil, err 68 } 69 70 if len(addrs) == 0 { 71 return nil, fmt.Errorf("no auto-config server addresses available for use") 72 } 73 74 return addrs, nil 75} 76 77// resolveHost will take a single host string and convert it to a list of TCPAddrs 78// This will process any port in the input as well as looking up the hostname using 79// normal DNS resolution. 80func (ac *AutoConfig) resolveHost(hostPort string) []net.TCPAddr { 81 port := ac.config.ServerPort 82 host, portStr, err := net.SplitHostPort(hostPort) 83 if err != nil { 84 if strings.Contains(err.Error(), "missing port in address") { 85 host = hostPort 86 } else { 87 ac.logger.Warn("error splitting host address into IP and port", "address", hostPort, "error", err) 88 return nil 89 } 90 } else { 91 port, err = strconv.Atoi(portStr) 92 if err != nil { 93 ac.logger.Warn("Parsed port is not an integer", "port", portStr, "error", err) 94 return nil 95 } 96 } 97 98 // resolve the host to a list of IPs 99 ips, err := net.LookupIP(host) 100 if err != nil { 101 ac.logger.Warn("IP resolution failed", "host", host, "error", err) 102 return nil 103 } 104 105 var addrs []net.TCPAddr 106 for _, ip := range ips { 107 addrs = append(addrs, net.TCPAddr{IP: ip, Port: port}) 108 } 109 110 return addrs 111} 112