github.com/chenbh/concourse/v6@v6.4.2/worker/beacon.go (about) 1 package worker 2 3 import ( 4 "context" 5 "os" 6 "sync" 7 "sync/atomic" 8 "time" 9 10 "code.cloudfoundry.org/lager" 11 "code.cloudfoundry.org/lager/lagerctx" 12 "github.com/chenbh/concourse/v6/tsa" 13 ) 14 15 type Beacon struct { 16 Logger lager.Logger 17 18 Client TSAClient 19 20 DrainSignals <-chan os.Signal 21 22 RebalanceInterval time.Duration 23 ConnectionDrainTimeout time.Duration 24 25 LocalGardenNetwork string 26 LocalGardenAddr string 27 28 LocalBaggageclaimNetwork string 29 LocalBaggageclaimAddr string 30 31 drained int32 32 } 33 34 // total number of active registrations; all but one are "live", the rest 35 // should all be draining 36 const maxActiveRegistrations = 5 37 38 func (beacon *Beacon) Run(signals <-chan os.Signal, ready chan<- struct{}) error { 39 beacon.Logger.Debug("start") 40 defer beacon.Logger.Debug("done") 41 42 var rebalanceCh <-chan time.Time 43 if beacon.RebalanceInterval != 0 { 44 ticker := time.NewTicker(beacon.RebalanceInterval) 45 defer ticker.Stop() 46 47 rebalanceCh = ticker.C 48 } 49 50 cwg := &countingWaitGroup{} 51 defer cwg.Wait() 52 53 rootCtx, cancelAll := context.WithCancel(lagerctx.NewContext(context.Background(), beacon.Logger)) 54 defer cancelAll() 55 56 latestErrChan := make(chan error, 1) 57 ctx, cancel := context.WithCancel(rootCtx) 58 59 cwg.Add(1) 60 beacon.registerWorker(ctx, cwg, latestErrChan) 61 62 close(ready) 63 64 var retiring bool 65 66 for { 67 select { 68 case <-rebalanceCh: 69 logger := beacon.Logger.Session("rebalance") 70 71 if cwg.Count() >= maxActiveRegistrations { 72 logger.Info("max-active-registrations-reached", lager.Data{ 73 "limit": maxActiveRegistrations, 74 }) 75 76 continue 77 } else { 78 logger.Debug("rebalancing") 79 } 80 81 cancelPrev := cancel 82 ctx, cancel = context.WithCancel(lagerctx.NewContext(rootCtx, logger)) 83 84 // make a new channel so prior registrations can write to their own 85 // buffered channel and exit 86 latestErrChan = make(chan error, 1) 87 88 cwg.Add(1) 89 beacon.registerWorker(ctx, cwg, latestErrChan) 90 91 cancelPrev() 92 93 case err := <-latestErrChan: 94 if err != nil { 95 beacon.Logger.Error("exited-with-error", err) 96 } else { 97 beacon.Logger.Info("exited") 98 } 99 100 // not actually necessary since we defer cancel the root ctx, but makes 101 // the linter happy 102 cancel() 103 104 return err 105 106 case sig := <-beacon.DrainSignals: 107 atomic.StoreInt32(&beacon.drained, 1) 108 109 logger := beacon.Logger.Session("drain") 110 111 logger.Debug("received-drain-signal", lager.Data{ 112 "signal": sig.String(), 113 }) 114 115 // prevent rebalancing from switching the worker back to 'running' 116 rebalanceCh = nil 117 118 if isLand(sig) { 119 logger.Info("landing-worker") 120 121 err := beacon.Client.Land(ctx) 122 if err != nil { 123 logger.Error("failed-to-land-worker", err) 124 125 // not actually necessary since we defer cancel the root ctx, but makes 126 // the linter happy 127 cancel() 128 129 return err 130 } 131 } else if isRetire(sig) { 132 retiring = true 133 134 logger.Info("retiring-worker") 135 136 err := beacon.Client.Retire(ctx) 137 if err != nil { 138 logger.Error("failed-to-retire-worker", err) 139 140 // not actually necessary since we defer cancel the root ctx, but makes 141 // the linter happy 142 cancel() 143 144 return err 145 } 146 } 147 148 case <-signals: 149 logger := beacon.Logger.Session("signal") 150 151 logger.Info("signalled") 152 153 // not actually necessary since we defer cancel the root ctx, but makes 154 // the linter happy 155 cancel() 156 157 if retiring { 158 logger.Info("deleting-worker") 159 160 err := beacon.Client.Delete(ctx) 161 if err != nil { 162 logger.Error("failed-to-delete-worker", err) 163 return err 164 } 165 } 166 167 return nil 168 } 169 } 170 } 171 172 func (beacon *Beacon) Drained() bool { 173 return atomic.LoadInt32(&beacon.drained) == 1 174 } 175 176 func (beacon *Beacon) registerWorker( 177 ctx context.Context, 178 cwg *countingWaitGroup, 179 errs chan<- error, 180 ) { 181 logger := lagerctx.FromContext(ctx) 182 183 once := &sync.Once{} 184 185 registeredOrFailed := make(chan struct{}) 186 go func() { 187 defer cwg.Done() 188 189 errs <- beacon.Client.Register(ctx, tsa.RegisterOptions{ 190 LocalGardenNetwork: beacon.LocalGardenNetwork, 191 LocalGardenAddr: beacon.LocalGardenAddr, 192 193 LocalBaggageclaimNetwork: beacon.LocalBaggageclaimNetwork, 194 LocalBaggageclaimAddr: beacon.LocalBaggageclaimAddr, 195 196 ConnectionDrainTimeout: beacon.ConnectionDrainTimeout, 197 198 RegisteredFunc: func() { 199 logger.Info("registered") 200 once.Do(func() { close(registeredOrFailed) }) 201 }, 202 203 HeartbeatedFunc: func() { 204 logger.Debug("heartbeated") 205 }, 206 }) 207 208 once.Do(func() { close(registeredOrFailed) }) 209 }() 210 211 <-registeredOrFailed 212 }