55 "errors"
66 "sync"
77
8+ "github.com/go-logr/logr"
89 "sigs.k8s.io/controller-runtime/pkg/webhook"
910)
1011
@@ -46,6 +47,16 @@ func newRunnables(baseContext BaseContextFunc, errChan chan error) *runnables {
4647 }
4748}
4849
50+ // withLogger returns the runnables with the logger set for all runnable groups.
51+ func (r * runnables ) withLogger (logger logr.Logger ) * runnables {
52+ r .HTTPServers .withLogger (logger )
53+ r .Webhooks .withLogger (logger )
54+ r .Caches .withLogger (logger )
55+ r .LeaderElection .withLogger (logger )
56+ r .Others .withLogger (logger )
57+ return r
58+ }
59+
4960// Add adds a runnable to closest group of runnable that they belong to.
5061//
5162// Add should be able to be called before and after Start, but not after StopAndWait.
@@ -105,6 +116,9 @@ type runnableGroup struct {
105116 // wg is an internal sync.WaitGroup that allows us to properly stop
106117 // and wait for all the runnables to finish before returning.
107118 wg * sync.WaitGroup
119+
120+ // logger is used for logging when errors are dropped during shutdown
121+ logger logr.Logger
108122}
109123
110124func newRunnableGroup (baseContext BaseContextFunc , errChan chan error ) * runnableGroup {
@@ -113,12 +127,18 @@ func newRunnableGroup(baseContext BaseContextFunc, errChan chan error) *runnable
113127 errChan : errChan ,
114128 ch : make (chan * readyRunnable ),
115129 wg : new (sync.WaitGroup ),
130+ logger : logr .Discard (), // Default to no-op logger
116131 }
117132
118133 r .ctx , r .cancel = context .WithCancel (baseContext ())
119134 return r
120135}
121136
137+ // withLogger sets the logger for this runnable group.
138+ func (r * runnableGroup ) withLogger (logger logr.Logger ) {
139+ r .logger = logger
140+ }
141+
122142// Started returns true if the group has started.
123143func (r * runnableGroup ) Started () bool {
124144 r .start .Lock ()
@@ -224,7 +244,17 @@ func (r *runnableGroup) reconcile() {
224244
225245 // Start the runnable.
226246 if err := rn .Start (r .ctx ); err != nil {
227- r .errChan <- err
247+ // Send error with context awareness to prevent blocking during shutdown
248+ select {
249+ case r .errChan <- err :
250+ // Error sent successfully
251+ case <- r .ctx .Done ():
252+ // Context cancelled (shutdown), drop error to prevent blocking forever
253+ // This prevents goroutine leaks when error drain go routine has exited after timeout
254+ if ! errors .Is (err , context .Canceled ) { // don't log context.Canceled errors as they are expected during shutdown
255+ r .logger .Info ("error dropped during shutdown to prevent goroutine leak" , "error" , err )
256+ }
257+ }
228258 }
229259 }(runnable )
230260 }
0 commit comments