@@ -117,6 +117,10 @@ type controllerManager struct {
117
117
// it must be deferred until after gracefulShutdown is done.
118
118
leaderElectionCancel context.CancelFunc
119
119
120
+ // leaderElectionStopped is an internal channel used to signal the stopping procedure that the
121
+ // LeaderElection.Run(...) function has returned and the shutdown can proceed.
122
+ leaderElectionStopped chan struct {}
123
+
120
124
// stop procedure engaged. In other words, we should not add anything else to the manager
121
125
stopProcedureEngaged bool
122
126
@@ -545,11 +549,16 @@ func (cm *controllerManager) engageStopProcedure(stopComplete <-chan struct{}) e
545
549
546
550
// waitForRunnableToEnd blocks until all runnables ended or the
547
551
// tearDownTimeout was reached. In the latter case, an error is returned.
548
- func (cm * controllerManager ) waitForRunnableToEnd (shutdownCancel context.CancelFunc ) error {
552
+ func (cm * controllerManager ) waitForRunnableToEnd (shutdownCancel context.CancelFunc ) ( retErr error ) {
549
553
// Cancel leader election only after we waited. It will os.Exit() the app for safety.
550
554
defer func () {
551
- if cm .leaderElectionCancel != nil {
555
+ if retErr == nil && cm .leaderElectionCancel != nil {
556
+ // After asking the context to be cancelled, make sure
557
+ // we wait for the leader stopped channel to be closed, otherwise
558
+ // we might encounter race conditions between this code
559
+ // and the event recorder, which is used within leader election code.
552
560
cm .leaderElectionCancel ()
561
+ <- cm .leaderElectionStopped
553
562
}
554
563
}()
555
564
@@ -652,7 +661,11 @@ func (cm *controllerManager) startLeaderElection() (err error) {
652
661
}
653
662
654
663
// Start the leader elector process
655
- go l .Run (ctx )
664
+ go func () {
665
+ l .Run (ctx )
666
+ <- ctx .Done ()
667
+ close (cm .leaderElectionStopped )
668
+ }()
656
669
return nil
657
670
}
658
671
0 commit comments