Skip to content

Commit 37da785

Browse files
committed
⚠️ Allow configuring RecoverPanic for controllers globally
This change allows configuring the RecoverPanic setting for controllers globally. It is a breaking change as it requires changing the field type of the setting to *bool from bool in order to be able to check if it has been set already.
1 parent 222fb66 commit 37da785

File tree

5 files changed

+56
-4
lines changed

5 files changed

+56
-4
lines changed

pkg/config/v1alpha1/types.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,10 @@ type ControllerConfigurationSpec struct {
9494
// Defaults to 2 minutes if not set.
9595
// +optional
9696
CacheSyncTimeout *time.Duration `json:"cacheSyncTimeout,omitempty"`
97+
98+
// RecoverPanic indicates if panics should be recovered.
99+
// +optional
100+
RecoverPanic *bool `json:"recoverPanic,omitempty"`
97101
}
98102

99103
// ControllerMetrics defines the metrics configs.

pkg/controller/controller.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ type Options struct {
5656
CacheSyncTimeout time.Duration
5757

5858
// RecoverPanic indicates whether the panic caused by reconcile should be recovered.
59-
RecoverPanic bool
59+
// Defaults to the Controller.RecoverPanic setting from the Manager if unset.
60+
RecoverPanic *bool
6061
}
6162

6263
// Controller implements a Kubernetes API. A Controller manages a work queue fed reconcile.Requests
@@ -139,6 +140,10 @@ func NewUnmanaged(name string, mgr manager.Manager, options Options) (Controller
139140
return nil, err
140141
}
141142

143+
if options.RecoverPanic == nil {
144+
options.RecoverPanic = mgr.GetControllerOptions().RecoverPanic
145+
}
146+
142147
// Create controller with dependencies set
143148
return &controller.Controller{
144149
Do: options.Reconciler,

pkg/controller/controller_test.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,11 @@ import (
2727
corev1 "k8s.io/api/core/v1"
2828

2929
"sigs.k8s.io/controller-runtime/pkg/client"
30+
"sigs.k8s.io/controller-runtime/pkg/config/v1alpha1"
3031
"sigs.k8s.io/controller-runtime/pkg/controller"
3132
"sigs.k8s.io/controller-runtime/pkg/event"
3233
"sigs.k8s.io/controller-runtime/pkg/handler"
34+
internalcontroller "sigs.k8s.io/controller-runtime/pkg/internal/controller"
3335
"sigs.k8s.io/controller-runtime/pkg/manager"
3436
"sigs.k8s.io/controller-runtime/pkg/reconcile"
3537
"sigs.k8s.io/controller-runtime/pkg/runtime/inject"
@@ -142,6 +144,39 @@ var _ = Describe("controller.Controller", func() {
142144
clientTransport.CloseIdleConnections()
143145
Eventually(func() error { return goleak.Find(currentGRs) }).Should(Succeed())
144146
})
147+
148+
It("should default RecoverPanic from the manager", func() {
149+
m, err := manager.New(cfg, manager.Options{Controller: v1alpha1.ControllerConfigurationSpec{RecoverPanic: ptr(true)}})
150+
Expect(err).NotTo(HaveOccurred())
151+
152+
c, err := controller.New("new-controller", m, controller.Options{
153+
Reconciler: reconcile.Func(nil),
154+
})
155+
Expect(err).NotTo(HaveOccurred())
156+
157+
ctrl, ok := c.(*internalcontroller.Controller)
158+
Expect(ok).To(BeTrue())
159+
160+
Expect(ctrl.RecoverPanic).NotTo(BeNil())
161+
Expect(*ctrl.RecoverPanic).To(BeTrue())
162+
})
163+
164+
It("should not override RecoverPanic on the controller", func() {
165+
m, err := manager.New(cfg, manager.Options{Controller: v1alpha1.ControllerConfigurationSpec{RecoverPanic: ptr(true)}})
166+
Expect(err).NotTo(HaveOccurred())
167+
168+
c, err := controller.New("new-controller", m, controller.Options{
169+
Reconciler: reconcile.Func(nil),
170+
RecoverPanic: ptr(false),
171+
})
172+
Expect(err).NotTo(HaveOccurred())
173+
174+
ctrl, ok := c.(*internalcontroller.Controller)
175+
Expect(ok).To(BeTrue())
176+
177+
Expect(ctrl.RecoverPanic).NotTo(BeNil())
178+
Expect(*ctrl.RecoverPanic).To(BeFalse())
179+
})
145180
})
146181
})
147182

@@ -157,3 +192,7 @@ func (*failRec) Reconcile(context.Context, reconcile.Request) (reconcile.Result,
157192
func (*failRec) InjectClient(client.Client) error {
158193
return fmt.Errorf("expected error")
159194
}
195+
196+
func ptr[T any](to T) *T {
197+
return &to
198+
}

pkg/internal/controller/controller.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ type Controller struct {
9292
LogConstructor func(request *reconcile.Request) logr.Logger
9393

9494
// RecoverPanic indicates whether the panic caused by reconcile should be recovered.
95-
RecoverPanic bool
95+
RecoverPanic *bool
9696
}
9797

9898
// watchDescription contains all the information necessary to start a watch.
@@ -106,7 +106,7 @@ type watchDescription struct {
106106
func (c *Controller) Reconcile(ctx context.Context, req reconcile.Request) (_ reconcile.Result, err error) {
107107
defer func() {
108108
if r := recover(); r != nil {
109-
if c.RecoverPanic {
109+
if c.RecoverPanic != nil && *c.RecoverPanic {
110110
for _, fn := range utilruntime.PanicHandlers {
111111
fn(r)
112112
}

pkg/internal/controller/controller_test.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ var _ = Describe("controller", func() {
114114
defer func() {
115115
Expect(recover()).To(BeNil())
116116
}()
117-
ctrl.RecoverPanic = true
117+
ctrl.RecoverPanic = ptr(true)
118118
ctrl.Do = reconcile.Func(func(context.Context, reconcile.Request) (reconcile.Result, error) {
119119
var res *reconcile.Result
120120
return *res, nil
@@ -996,3 +996,7 @@ func (c *cacheWithIndefinitelyBlockingGetInformer) GetInformer(ctx context.Conte
996996
<-ctx.Done()
997997
return nil, errors.New("GetInformer timed out")
998998
}
999+
1000+
func ptr[T any](in T) *T {
1001+
return &in
1002+
}

0 commit comments

Comments
 (0)