2015-08-31 16:23:17 -04:00
|
|
|
package discovery
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2015-10-25 20:12:22 -04:00
|
|
|
"net"
|
2015-08-31 16:23:17 -04:00
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
log "github.com/Sirupsen/logrus"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
// Backends is a global map of discovery backends indexed by their
|
|
|
|
// associated scheme.
|
|
|
|
backends = make(map[string]Backend)
|
2015-12-10 18:35:10 -05:00
|
|
|
)
|
2015-08-31 16:23:17 -04:00
|
|
|
|
|
|
|
// 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)
|
|
|
|
}
|
|
|
|
log.WithField("name", scheme).Debug("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]
|
|
|
|
}
|
|
|
|
|
2015-10-25 20:12:22 -04:00
|
|
|
// ParseAdvertise parses the --cluster-advertise daemon config which accepts
|
|
|
|
// <ip-address>:<port> or <interface-name>:<port>
|
2015-12-10 18:35:10 -05:00
|
|
|
func ParseAdvertise(advertise string) (string, error) {
|
2015-10-25 20:12:22 -04:00
|
|
|
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 addrs == nil || 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("couldnt find a valid ip-address in interface %s", advertise)
|
|
|
|
}
|
|
|
|
|
|
|
|
addr = fmt.Sprintf("%s:%s", addr, port)
|
|
|
|
return addr, nil
|
|
|
|
}
|
|
|
|
|
2015-08-31 16:23:17 -04:00
|
|
|
// New returns a new Discovery given a URL, heartbeat and ttl settings.
|
|
|
|
// Returns an error if the URL scheme is not supported.
|
2015-09-28 19:22:57 -04:00
|
|
|
func New(rawurl string, heartbeat time.Duration, ttl time.Duration, clusterOpts map[string]string) (Backend, error) {
|
2015-08-31 16:23:17 -04:00
|
|
|
scheme, uri := parse(rawurl)
|
|
|
|
if backend, exists := backends[scheme]; exists {
|
|
|
|
log.WithFields(log.Fields{"name": scheme, "uri": uri}).Debug("Initializing discovery service")
|
2015-09-28 19:22:57 -04:00
|
|
|
err := backend.Initialize(uri, heartbeat, ttl, clusterOpts)
|
2015-08-31 16:23:17 -04:00
|
|
|
return backend, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, ErrNotSupported
|
|
|
|
}
|