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  }