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  }