Skip to content

Commit 5af1f3e

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 5b943b9 commit 5af1f3e

File tree

2 files changed

+54
-2
lines changed

2 files changed

+54
-2
lines changed

pkg/manager/internal.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -507,8 +507,8 @@ func (cm *controllerManager) engageStopProcedure(stopComplete <-chan struct{}) e
507507
cm.internalCancel()
508508
})
509509
select {
510-
case err, ok := <-cm.errChan:
511-
if ok {
510+
case err := <-cm.errChan:
511+
if !errors.Is(err, context.Canceled) {
512512
cm.logger.Error(err, "error received after stop sequence was engaged")
513513
}
514514
case <-stopComplete:

pkg/manager/manager_test.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
"time"
3030

3131
"github.com/go-logr/logr"
32+
"github.com/go-logr/logr/funcr"
3233
. "github.com/onsi/ginkgo/v2"
3334
. "github.com/onsi/gomega"
3435
"github.com/prometheus/client_golang/prometheus"
@@ -982,6 +983,57 @@ var _ = Describe("manger.Manager", func() {
982983
}))).NotTo(Succeed())
983984
})
984985

986+
It("should not return runnables context.Canceled errors", func() {
987+
Expect(options.Logger).To(BeZero(), "this test overrides Logger")
988+
989+
var log struct {
990+
sync.Mutex
991+
messages []string
992+
}
993+
options.Logger = funcr.NewJSON(func(object string) {
994+
log.Lock()
995+
log.messages = append(log.messages, object)
996+
log.Unlock()
997+
}, funcr.Options{})
998+
999+
m, err := New(cfg, options)
1000+
Expect(err).NotTo(HaveOccurred())
1001+
for _, cb := range callbacks {
1002+
cb(m)
1003+
}
1004+
1005+
// Runnables may return ctx.Err() as shown in some [context.Context] examples.
1006+
started := make(chan struct{})
1007+
Expect(m.Add(RunnableFunc(func(ctx context.Context) error {
1008+
close(started)
1009+
<-ctx.Done()
1010+
return ctx.Err()
1011+
}))).To(Succeed())
1012+
1013+
stopped := make(chan error)
1014+
ctx, cancel := context.WithCancel(context.Background())
1015+
go func() {
1016+
stopped <- m.Start(ctx)
1017+
}()
1018+
1019+
// Wait for runnables to start, signal the manager, and wait for it to return.
1020+
<-started
1021+
cancel()
1022+
Expect(<-stopped).To(Succeed())
1023+
1024+
// The leader election goroutine emits one more log message after Start() returns.
1025+
// Take the lock here to avoid a race between it writing to log.messages and the
1026+
// following read from log.messages.
1027+
if options.LeaderElection {
1028+
log.Lock()
1029+
defer log.Unlock()
1030+
}
1031+
1032+
Expect(log.messages).To(Not(ContainElement(
1033+
ContainSubstring(context.Canceled.Error()),
1034+
)))
1035+
})
1036+
9851037
It("should return both runnables and stop errors when both error", func() {
9861038
m, err := New(cfg, options)
9871039
Expect(err).NotTo(HaveOccurred())

0 commit comments

Comments
 (0)