| package discovery |
| |
| import ( |
| "fmt" |
| "net" |
| "strings" |
| "time" |
| |
| "github.com/Sirupsen/logrus" |
| ) |
| |
| var ( |
| // Backends is a global map of discovery backends indexed by their |
| // associated scheme. |
| backends = make(map[string]Backend) |
| ) |
| |
| // Register makes a discovery backend available by the provided scheme. |
| // If Register is called twice with the same scheme an error is returned. |
| func Register(scheme string, d Backend) error { |
| if _, exists := backends[scheme]; exists { |
| return fmt.Errorf("scheme already registered %s", scheme) |
| } |
| logrus.WithField("name", scheme).Debugf("Registering discovery service") |
| backends[scheme] = d |
| return nil |
| } |
| |
| func parse(rawurl string) (string, string) { |
| parts := strings.SplitN(rawurl, "://", 2) |
| |
| // nodes:port,node2:port => nodes://node1:port,node2:port |
| if len(parts) == 1 { |
| return "nodes", parts[0] |
| } |
| return parts[0], parts[1] |
| } |
| |
| // ParseAdvertise parses the --cluster-advertise daemon config which accepts |
| // <ip-address>:<port> or <interface-name>:<port> |
| func ParseAdvertise(advertise string) (string, error) { |
| var ( |
| iface *net.Interface |
| addrs []net.Addr |
| err error |
| ) |
| |
| addr, port, err := net.SplitHostPort(advertise) |
| |
| if err != nil { |
| return "", fmt.Errorf("invalid --cluster-advertise configuration: %s: %v", advertise, err) |
| } |
| |
| ip := net.ParseIP(addr) |
| // If it is a valid ip-address, use it as is |
| if ip != nil { |
| return advertise, nil |
| } |
| |
| // If advertise is a valid interface name, get the valid IPv4 address and use it to advertise |
| ifaceName := addr |
| iface, err = net.InterfaceByName(ifaceName) |
| if err != nil { |
| return "", fmt.Errorf("invalid cluster advertise IP address or interface name (%s) : %v", advertise, err) |
| } |
| |
| addrs, err = iface.Addrs() |
| if err != nil { |
| return "", fmt.Errorf("unable to get advertise IP address from interface (%s) : %v", advertise, err) |
| } |
| |
| if len(addrs) == 0 { |
| return "", fmt.Errorf("no available advertise IP address in interface (%s)", advertise) |
| } |
| |
| addr = "" |
| for _, a := range addrs { |
| ip, _, err := net.ParseCIDR(a.String()) |
| if err != nil { |
| return "", fmt.Errorf("error deriving advertise ip-address in interface (%s) : %v", advertise, err) |
| } |
| if ip.To4() == nil || ip.IsLoopback() { |
| continue |
| } |
| addr = ip.String() |
| break |
| } |
| if addr == "" { |
| return "", fmt.Errorf("could not find a valid ip-address in interface %s", advertise) |
| } |
| |
| addr = net.JoinHostPort(addr, port) |
| return addr, nil |
| } |
| |
| // New returns a new Discovery given a URL, heartbeat and ttl settings. |
| // Returns an error if the URL scheme is not supported. |
| func New(rawurl string, heartbeat time.Duration, ttl time.Duration, clusterOpts map[string]string) (Backend, error) { |
| scheme, uri := parse(rawurl) |
| if backend, exists := backends[scheme]; exists { |
| logrus.WithFields(logrus.Fields{"name": scheme, "uri": uri}).Debugf("Initializing discovery service") |
| err := backend.Initialize(uri, heartbeat, ttl, clusterOpts) |
| return backend, err |
| } |
| |
| return nil, ErrNotSupported |
| } |