Skip to content

Commit 732bd60

Browse files
committed
Quiet context.Canceled errors during shutdown
Runnable implementations that return ctx.Err() cause a spurious "error received after stop" log message.
1 parent 2136860 commit 732bd60

File tree

2 files changed

+51
-2
lines changed

2 files changed

+51
-2
lines changed

pkg/manager/internal.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -485,8 +485,8 @@ func (cm *controllerManager) engageStopProcedure(stopComplete <-chan struct{}) e
485485
cm.internalCancel()
486486
})
487487
select {
488-
case err, ok := <-cm.errChan:
489-
if ok {
488+
case err := <-cm.errChan:
489+
if !errors.Is(err, context.Canceled) {
490490
cm.logger.Error(err, "error received after stop sequence was engaged")
491491
}
492492
case <-stopComplete:

pkg/manager/manager_test.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030
"time"
3131

3232
"github.com/go-logr/logr"
33+
"github.com/go-logr/logr/funcr"
3334
. "github.com/onsi/ginkgo/v2"
3435
. "github.com/onsi/gomega"
3536
"github.com/prometheus/client_golang/prometheus"
@@ -1044,6 +1045,54 @@ var _ = Describe("manger.Manager", func() {
10441045
}))).NotTo(Succeed())
10451046
})
10461047

1048+
It("should not return runnables context.Canceled errors", func() {
1049+
Expect(options.Logger).To(BeZero(), "this test overrides Logger")
1050+
1051+
var log struct {
1052+
sync.Mutex
1053+
messages []string
1054+
}
1055+
options.Logger = funcr.NewJSON(func(object string) {
1056+
log.Lock()
1057+
log.messages = append(log.messages, object)
1058+
log.Unlock()
1059+
}, funcr.Options{})
1060+
1061+
m, err := New(cfg, options)
1062+
Expect(err).NotTo(HaveOccurred())
1063+
for _, cb := range callbacks {
1064+
cb(m)
1065+
}
1066+
1067+
// Runnables may return ctx.Err() as shown in some [context.Context] examples.
1068+
started := make(chan struct{})
1069+
Expect(m.Add(RunnableFunc(func(ctx context.Context) error {
1070+
close(started)
1071+
<-ctx.Done()
1072+
return ctx.Err()
1073+
}))).To(Succeed())
1074+
1075+
stopped := make(chan error)
1076+
ctx, cancel := context.WithCancel(context.Background())
1077+
go func() {
1078+
stopped <- m.Start(ctx)
1079+
}()
1080+
1081+
// Wait for runnables to start, signal the manager, and wait for it.
1082+
<-started
1083+
cancel()
1084+
Expect(<-stopped).To(Succeed())
1085+
1086+
// TODO: Diagnose why LeaderElection races after Start() returns.
1087+
if options.LeaderElection {
1088+
time.Sleep(time.Second)
1089+
}
1090+
1091+
Expect(log.messages).To(Not(ContainElement(
1092+
ContainSubstring(context.Canceled.Error()),
1093+
)))
1094+
})
1095+
10471096
It("should return both runnables and stop errors when both error", func() {
10481097
m, err := New(cfg, options)
10491098
Expect(err).NotTo(HaveOccurred())

0 commit comments

Comments
 (0)