2015-06-04 07:51:16 -04:00
|
|
|
// +build libnetwork_discovery
|
|
|
|
|
2015-05-15 18:23:59 -04:00
|
|
|
package hostdiscovery
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"net"
|
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
log "github.com/Sirupsen/logrus"
|
|
|
|
|
|
|
|
mapset "github.com/deckarep/golang-set"
|
|
|
|
"github.com/docker/libnetwork/config"
|
|
|
|
"github.com/docker/swarm/discovery"
|
|
|
|
// Anonymous import will be removed after we upgrade to latest swarm
|
|
|
|
_ "github.com/docker/swarm/discovery/file"
|
|
|
|
// Anonymous import will be removed after we upgrade to latest swarm
|
|
|
|
_ "github.com/docker/swarm/discovery/kv"
|
|
|
|
// Anonymous import will be removed after we upgrade to latest swarm
|
|
|
|
_ "github.com/docker/swarm/discovery/nodes"
|
|
|
|
// Anonymous import will be removed after we upgrade to latest swarm
|
|
|
|
_ "github.com/docker/swarm/discovery/token"
|
|
|
|
)
|
|
|
|
|
2015-05-29 23:42:23 -04:00
|
|
|
const defaultHeartbeat = time.Duration(10) * time.Second
|
2015-06-05 16:31:12 -04:00
|
|
|
const TTLFactor = 3
|
2015-05-15 18:23:59 -04:00
|
|
|
|
|
|
|
type hostDiscovery struct {
|
|
|
|
discovery discovery.Discovery
|
|
|
|
nodes mapset.Set
|
|
|
|
stopChan chan struct{}
|
|
|
|
sync.Mutex
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewHostDiscovery function creates a host discovery object
|
|
|
|
func NewHostDiscovery() HostDiscovery {
|
|
|
|
return &hostDiscovery{nodes: mapset.NewSet(), stopChan: make(chan struct{})}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *hostDiscovery) StartDiscovery(cfg *config.ClusterCfg, joinCallback JoinCallback, leaveCallback LeaveCallback) error {
|
|
|
|
if cfg == nil {
|
|
|
|
return fmt.Errorf("discovery requires a valid configuration")
|
|
|
|
}
|
|
|
|
|
2015-05-29 23:42:23 -04:00
|
|
|
hb := time.Duration(cfg.Heartbeat) * time.Second
|
2015-05-15 18:23:59 -04:00
|
|
|
if hb == 0 {
|
|
|
|
hb = defaultHeartbeat
|
|
|
|
}
|
2015-06-05 16:31:12 -04:00
|
|
|
d, err := discovery.New(cfg.Discovery, hb, TTLFactor*hb)
|
2015-05-15 18:23:59 -04:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if ip := net.ParseIP(cfg.Address); ip == nil {
|
2015-05-29 23:42:23 -04:00
|
|
|
return errors.New("address config should be either ipv4 or ipv6 address")
|
2015-05-15 18:23:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if err := d.Register(cfg.Address + ":0"); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
h.Lock()
|
|
|
|
h.discovery = d
|
|
|
|
h.Unlock()
|
|
|
|
|
2015-05-29 23:42:23 -04:00
|
|
|
discoveryCh, errCh := d.Watch(h.stopChan)
|
|
|
|
go h.monitorDiscovery(discoveryCh, errCh, joinCallback, leaveCallback)
|
|
|
|
go h.sustainHeartbeat(d, hb, cfg)
|
2015-05-15 18:23:59 -04:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-05-29 23:42:23 -04:00
|
|
|
func (h *hostDiscovery) monitorDiscovery(ch <-chan discovery.Entries, errCh <-chan error, joinCallback JoinCallback, leaveCallback LeaveCallback) {
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case entries := <-ch:
|
|
|
|
h.processCallback(entries, joinCallback, leaveCallback)
|
|
|
|
case err := <-errCh:
|
|
|
|
log.Errorf("discovery error: %v", err)
|
|
|
|
case <-h.stopChan:
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-15 18:23:59 -04:00
|
|
|
func (h *hostDiscovery) StopDiscovery() error {
|
|
|
|
h.Lock()
|
|
|
|
stopChan := h.stopChan
|
|
|
|
h.discovery = nil
|
|
|
|
h.Unlock()
|
|
|
|
|
|
|
|
close(stopChan)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-05-29 23:42:23 -04:00
|
|
|
func (h *hostDiscovery) sustainHeartbeat(d discovery.Discovery, hb time.Duration, config *config.ClusterCfg) {
|
2015-05-15 18:23:59 -04:00
|
|
|
for {
|
|
|
|
select {
|
2015-05-29 23:42:23 -04:00
|
|
|
case <-h.stopChan:
|
2015-05-15 18:23:59 -04:00
|
|
|
return
|
2015-05-29 23:42:23 -04:00
|
|
|
case <-time.After(hb):
|
2015-05-15 18:23:59 -04:00
|
|
|
if err := d.Register(config.Address + ":0"); err != nil {
|
|
|
|
log.Warn(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-29 23:42:23 -04:00
|
|
|
func (h *hostDiscovery) processCallback(entries discovery.Entries, joinCallback JoinCallback, leaveCallback LeaveCallback) {
|
2015-05-15 18:23:59 -04:00
|
|
|
updated := hosts(entries)
|
|
|
|
h.Lock()
|
|
|
|
existing := h.nodes
|
|
|
|
added, removed := diff(existing, updated)
|
|
|
|
h.nodes = updated
|
|
|
|
h.Unlock()
|
|
|
|
|
|
|
|
if len(added) > 0 {
|
|
|
|
joinCallback(added)
|
|
|
|
}
|
|
|
|
if len(removed) > 0 {
|
|
|
|
leaveCallback(removed)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func diff(existing mapset.Set, updated mapset.Set) (added []net.IP, removed []net.IP) {
|
|
|
|
addSlice := updated.Difference(existing).ToSlice()
|
|
|
|
removeSlice := existing.Difference(updated).ToSlice()
|
|
|
|
for _, ip := range addSlice {
|
|
|
|
added = append(added, net.ParseIP(ip.(string)))
|
|
|
|
}
|
|
|
|
for _, ip := range removeSlice {
|
|
|
|
removed = append(removed, net.ParseIP(ip.(string)))
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *hostDiscovery) Fetch() ([]net.IP, error) {
|
|
|
|
h.Lock()
|
2015-05-29 23:42:23 -04:00
|
|
|
defer h.Unlock()
|
2015-05-15 18:23:59 -04:00
|
|
|
ips := []net.IP{}
|
2015-05-29 23:42:23 -04:00
|
|
|
for _, ipstr := range h.nodes.ToSlice() {
|
|
|
|
ips = append(ips, net.ParseIP(ipstr.(string)))
|
2015-05-15 18:23:59 -04:00
|
|
|
}
|
|
|
|
return ips, nil
|
|
|
|
}
|
|
|
|
|
2015-05-29 23:42:23 -04:00
|
|
|
func hosts(entries discovery.Entries) mapset.Set {
|
2015-05-15 18:23:59 -04:00
|
|
|
hosts := mapset.NewSet()
|
|
|
|
for _, entry := range entries {
|
|
|
|
hosts.Add(entry.Host)
|
|
|
|
}
|
|
|
|
return hosts
|
|
|
|
}
|