@@ -121,6 +121,10 @@ type controllerManager struct {
121
121
// it must be deferred until after gracefulShutdown is done.
122
122
leaderElectionCancel context.CancelFunc
123
123
124
+ // leaderElectionStopped is an internal channel used to signal the stopping procedure that the
125
+ // LeaderElection.Run(...) function has returned and the shutdown can proceed.
126
+ leaderElectionStopped chan struct {}
127
+
124
128
// stop procedure engaged. In other words, we should not add anything else to the manager
125
129
stopProcedureEngaged bool
126
130
@@ -553,11 +557,16 @@ func (cm *controllerManager) engageStopProcedure(stopComplete <-chan struct{}) e
553
557
554
558
// waitForRunnableToEnd blocks until all runnables ended or the
555
559
// tearDownTimeout was reached. In the latter case, an error is returned.
556
- func (cm * controllerManager ) waitForRunnableToEnd (shutdownCancel context.CancelFunc ) error {
560
+ func (cm * controllerManager ) waitForRunnableToEnd (shutdownCancel context.CancelFunc ) ( retErr error ) {
557
561
// Cancel leader election only after we waited. It will os.Exit() the app for safety.
558
562
defer func () {
559
- if cm .leaderElectionCancel != nil {
563
+ if retErr == nil && cm .leaderElectionCancel != nil {
564
+ // After asking the context to be cancelled, make sure
565
+ // we wait for the leader stopped channel to be closed, otherwise
566
+ // we might encounter race conditions between this code
567
+ // and the event recorder, which is used within leader election code.
560
568
cm .leaderElectionCancel ()
569
+ <- cm .leaderElectionStopped
561
570
}
562
571
}()
563
572
@@ -660,7 +669,11 @@ func (cm *controllerManager) startLeaderElection() (err error) {
660
669
}
661
670
662
671
// Start the leader elector process
663
- go l .Run (ctx )
672
+ go func () {
673
+ l .Run (ctx )
674
+ <- ctx .Done ()
675
+ close (cm .leaderElectionStopped )
676
+ }()
664
677
return nil
665
678
}
666
679
0 commit comments