github.com/sijibomii/docker@v0.0.0-20231230191044-5cf6ca554647/daemon/discovery.go (about) 1 package daemon 2 3 import ( 4 "errors" 5 "fmt" 6 "reflect" 7 "strconv" 8 "time" 9 10 log "github.com/Sirupsen/logrus" 11 "github.com/docker/docker/pkg/discovery" 12 13 // Register the libkv backends for discovery. 14 _ "github.com/docker/docker/pkg/discovery/kv" 15 ) 16 17 const ( 18 // defaultDiscoveryHeartbeat is the default value for discovery heartbeat interval. 19 defaultDiscoveryHeartbeat = 20 * time.Second 20 // defaultDiscoveryTTLFactor is the default TTL factor for discovery 21 defaultDiscoveryTTLFactor = 3 22 ) 23 24 var errDiscoveryDisabled = errors.New("discovery is disabled") 25 26 type discoveryReloader interface { 27 discovery.Watcher 28 Stop() 29 Reload(backend, address string, clusterOpts map[string]string) error 30 } 31 32 type daemonDiscoveryReloader struct { 33 backend discovery.Backend 34 ticker *time.Ticker 35 term chan bool 36 } 37 38 func (d *daemonDiscoveryReloader) Watch(stopCh <-chan struct{}) (<-chan discovery.Entries, <-chan error) { 39 return d.backend.Watch(stopCh) 40 } 41 42 func discoveryOpts(clusterOpts map[string]string) (time.Duration, time.Duration, error) { 43 var ( 44 heartbeat = defaultDiscoveryHeartbeat 45 ttl = defaultDiscoveryTTLFactor * defaultDiscoveryHeartbeat 46 ) 47 48 if hb, ok := clusterOpts["discovery.heartbeat"]; ok { 49 h, err := strconv.Atoi(hb) 50 if err != nil { 51 return time.Duration(0), time.Duration(0), err 52 } 53 heartbeat = time.Duration(h) * time.Second 54 ttl = defaultDiscoveryTTLFactor * heartbeat 55 } 56 57 if tstr, ok := clusterOpts["discovery.ttl"]; ok { 58 t, err := strconv.Atoi(tstr) 59 if err != nil { 60 return time.Duration(0), time.Duration(0), err 61 } 62 ttl = time.Duration(t) * time.Second 63 64 if _, ok := clusterOpts["discovery.heartbeat"]; !ok { 65 h := int(t / defaultDiscoveryTTLFactor) 66 heartbeat = time.Duration(h) * time.Second 67 } 68 69 if ttl <= heartbeat { 70 return time.Duration(0), time.Duration(0), 71 fmt.Errorf("discovery.ttl timer must be greater than discovery.heartbeat") 72 } 73 } 74 75 return heartbeat, ttl, nil 76 } 77 78 // initDiscovery initializes the nodes discovery subsystem by connecting to the specified backend 79 // and starts a registration loop to advertise the current node under the specified address. 80 func initDiscovery(backendAddress, advertiseAddress string, clusterOpts map[string]string) (discoveryReloader, error) { 81 heartbeat, backend, err := parseDiscoveryOptions(backendAddress, clusterOpts) 82 if err != nil { 83 return nil, err 84 } 85 86 reloader := &daemonDiscoveryReloader{ 87 backend: backend, 88 ticker: time.NewTicker(heartbeat), 89 term: make(chan bool), 90 } 91 // We call Register() on the discovery backend in a loop for the whole lifetime of the daemon, 92 // but we never actually Watch() for nodes appearing and disappearing for the moment. 93 reloader.advertise(advertiseAddress) 94 return reloader, nil 95 } 96 97 func (d *daemonDiscoveryReloader) advertise(address string) { 98 d.registerAddr(address) 99 go d.advertiseHeartbeat(address) 100 } 101 102 func (d *daemonDiscoveryReloader) registerAddr(addr string) { 103 if err := d.backend.Register(addr); err != nil { 104 log.Warnf("Registering as %q in discovery failed: %v", addr, err) 105 } 106 } 107 108 // advertiseHeartbeat registers the current node against the discovery backend using the specified 109 // address. The function never returns, as registration against the backend comes with a TTL and 110 // requires regular heartbeats. 111 func (d *daemonDiscoveryReloader) advertiseHeartbeat(address string) { 112 for { 113 select { 114 case <-d.ticker.C: 115 d.registerAddr(address) 116 case <-d.term: 117 return 118 } 119 } 120 } 121 122 // Reload makes the watcher to stop advertising and reconfigures it to advertise in a new address. 123 func (d *daemonDiscoveryReloader) Reload(backendAddress, advertiseAddress string, clusterOpts map[string]string) error { 124 d.Stop() 125 126 heartbeat, backend, err := parseDiscoveryOptions(backendAddress, clusterOpts) 127 if err != nil { 128 return err 129 } 130 131 d.backend = backend 132 d.ticker = time.NewTicker(heartbeat) 133 134 d.advertise(advertiseAddress) 135 return nil 136 } 137 138 // Stop terminates the discovery advertising. 139 func (d *daemonDiscoveryReloader) Stop() { 140 d.ticker.Stop() 141 d.term <- true 142 } 143 144 func parseDiscoveryOptions(backendAddress string, clusterOpts map[string]string) (time.Duration, discovery.Backend, error) { 145 heartbeat, ttl, err := discoveryOpts(clusterOpts) 146 if err != nil { 147 return 0, nil, err 148 } 149 150 backend, err := discovery.New(backendAddress, heartbeat, ttl, clusterOpts) 151 if err != nil { 152 return 0, nil, err 153 } 154 return heartbeat, backend, nil 155 } 156 157 // modifiedDiscoverySettings returns whether the discovery configuration has been modified or not. 158 func modifiedDiscoverySettings(config *Config, backendType, advertise string, clusterOpts map[string]string) bool { 159 if config.ClusterStore != backendType || config.ClusterAdvertise != advertise { 160 return true 161 } 162 163 if (config.ClusterOpts == nil && clusterOpts == nil) || 164 (config.ClusterOpts == nil && len(clusterOpts) == 0) || 165 (len(config.ClusterOpts) == 0 && clusterOpts == nil) { 166 return false 167 } 168 169 return !reflect.DeepEqual(config.ClusterOpts, clusterOpts) 170 }