github.com/sijibomii/docker@v0.0.0-20231230191044-5cf6ca554647/pkg/discovery/backends.go (about) 1 package discovery 2 3 import ( 4 "fmt" 5 "net" 6 "strings" 7 "time" 8 9 log "github.com/Sirupsen/logrus" 10 ) 11 12 var ( 13 // Backends is a global map of discovery backends indexed by their 14 // associated scheme. 15 backends = make(map[string]Backend) 16 ) 17 18 // Register makes a discovery backend available by the provided scheme. 19 // If Register is called twice with the same scheme an error is returned. 20 func Register(scheme string, d Backend) error { 21 if _, exists := backends[scheme]; exists { 22 return fmt.Errorf("scheme already registered %s", scheme) 23 } 24 log.WithField("name", scheme).Debug("Registering discovery service") 25 backends[scheme] = d 26 return nil 27 } 28 29 func parse(rawurl string) (string, string) { 30 parts := strings.SplitN(rawurl, "://", 2) 31 32 // nodes:port,node2:port => nodes://node1:port,node2:port 33 if len(parts) == 1 { 34 return "nodes", parts[0] 35 } 36 return parts[0], parts[1] 37 } 38 39 // ParseAdvertise parses the --cluster-advertise daemon config which accepts 40 // <ip-address>:<port> or <interface-name>:<port> 41 func ParseAdvertise(advertise string) (string, error) { 42 var ( 43 iface *net.Interface 44 addrs []net.Addr 45 err error 46 ) 47 48 addr, port, err := net.SplitHostPort(advertise) 49 50 if err != nil { 51 return "", fmt.Errorf("invalid --cluster-advertise configuration: %s: %v", advertise, err) 52 } 53 54 ip := net.ParseIP(addr) 55 // If it is a valid ip-address, use it as is 56 if ip != nil { 57 return advertise, nil 58 } 59 60 // If advertise is a valid interface name, get the valid ipv4 address and use it to advertise 61 ifaceName := addr 62 iface, err = net.InterfaceByName(ifaceName) 63 if err != nil { 64 return "", fmt.Errorf("invalid cluster advertise IP address or interface name (%s) : %v", advertise, err) 65 } 66 67 addrs, err = iface.Addrs() 68 if err != nil { 69 return "", fmt.Errorf("unable to get advertise IP address from interface (%s) : %v", advertise, err) 70 } 71 72 if addrs == nil || len(addrs) == 0 { 73 return "", fmt.Errorf("no available advertise IP address in interface (%s)", advertise) 74 } 75 76 addr = "" 77 for _, a := range addrs { 78 ip, _, err := net.ParseCIDR(a.String()) 79 if err != nil { 80 return "", fmt.Errorf("error deriving advertise ip-address in interface (%s) : %v", advertise, err) 81 } 82 if ip.To4() == nil || ip.IsLoopback() { 83 continue 84 } 85 addr = ip.String() 86 break 87 } 88 if addr == "" { 89 return "", fmt.Errorf("couldnt find a valid ip-address in interface %s", advertise) 90 } 91 92 addr = net.JoinHostPort(addr, port) 93 return addr, nil 94 } 95 96 // New returns a new Discovery given a URL, heartbeat and ttl settings. 97 // Returns an error if the URL scheme is not supported. 98 func New(rawurl string, heartbeat time.Duration, ttl time.Duration, clusterOpts map[string]string) (Backend, error) { 99 scheme, uri := parse(rawurl) 100 if backend, exists := backends[scheme]; exists { 101 log.WithFields(log.Fields{"name": scheme, "uri": uri}).Debug("Initializing discovery service") 102 err := backend.Initialize(uri, heartbeat, ttl, clusterOpts) 103 return backend, err 104 } 105 106 return nil, ErrNotSupported 107 }