@@ -121,6 +121,10 @@ type controllerManager struct {
121121 // it must be deferred until after gracefulShutdown is done.
122122 leaderElectionCancel context.CancelFunc
123123
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+
124128 // stop procedure engaged. In other words, we should not add anything else to the manager
125129 stopProcedureEngaged bool
126130
@@ -553,11 +557,16 @@ func (cm *controllerManager) engageStopProcedure(stopComplete <-chan struct{}) e
553557
554558// waitForRunnableToEnd blocks until all runnables ended or the
555559// 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 ) {
557561 // Cancel leader election only after we waited. It will os.Exit() the app for safety.
558562 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.
560568 cm .leaderElectionCancel ()
569+ <- cm .leaderElectionStopped
561570 }
562571 }()
563572
@@ -660,7 +669,11 @@ func (cm *controllerManager) startLeaderElection() (err error) {
660669 }
661670
662671 // 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+ }()
664677 return nil
665678}
666679
0 commit comments