@@ -30,6 +30,7 @@ import (
3030 "k8s.io/apimachinery/pkg/util/json"
3131 utilruntime "k8s.io/apimachinery/pkg/util/runtime"
3232 "k8s.io/klog/v2"
33+ admissionmetrics "sigs.k8s.io/controller-runtime/pkg/webhook/admission/metrics"
3334
3435 logf "sigs.k8s.io/controller-runtime/pkg/log"
3536 "sigs.k8s.io/controller-runtime/pkg/webhook/internal/metrics"
@@ -123,7 +124,8 @@ type Webhook struct {
123124 Handler Handler
124125
125126 // RecoverPanic indicates whether the panic caused by webhook should be recovered.
126- RecoverPanic bool
127+ // Defaults to true.
128+ RecoverPanic * bool
127129
128130 // WithContextFunc will allow you to take the http.Request.Context() and
129131 // add any additional information such as passing the request path or
@@ -141,7 +143,8 @@ type Webhook struct {
141143}
142144
143145// WithRecoverPanic takes a bool flag which indicates whether the panic caused by webhook should be recovered.
144- func (wh * Webhook ) WithRecoverPanic (recoverPanic bool ) * Webhook {
146+ // Defaults to true.
147+ func (wh * Webhook ) WithRecoverPanic (recoverPanic * bool ) * Webhook {
145148 wh .RecoverPanic = recoverPanic
146149 return wh
147150}
@@ -151,25 +154,37 @@ func (wh *Webhook) WithRecoverPanic(recoverPanic bool) *Webhook {
151154// If the webhook is validating type, it delegates the AdmissionRequest to each handler and
152155// deny the request if anyone denies.
153156func (wh * Webhook ) Handle (ctx context.Context , req Request ) (response Response ) {
154- if wh .RecoverPanic {
155- defer func () {
156- if r := recover (); r != nil {
157+ defer func () {
158+ if r := recover (); r != nil {
159+ admissionmetrics .WebhookPanics .WithLabelValues ().Inc ()
160+
161+ if wh .RecoverPanic == nil || * wh .RecoverPanic {
157162 for _ , fn := range utilruntime .PanicHandlers {
158163 fn (ctx , r )
159164 }
160165 response = Errored (http .StatusInternalServerError , fmt .Errorf ("panic: %v [recovered]" , r ))
166+ // Note: We explicitly have to set the response UID. Usually that is done via resp.Complete below,
167+ // but if we encounter a panic in wh.Handler.Handle we are never going to reach resp.Complete.
168+ response .UID = req .UID
161169 return
162170 }
163- }()
164- }
171+
172+ log := logf .FromContext (ctx )
173+ log .Info (fmt .Sprintf ("Observed a panic in webhook: %v" , r ))
174+ panic (r )
175+ }
176+ }()
165177
166178 reqLog := wh .getLogger (& req )
167179 ctx = logf .IntoContext (ctx , reqLog )
168180
169181 resp := wh .Handler .Handle (ctx , req )
170182 if err := resp .Complete (req ); err != nil {
171183 reqLog .Error (err , "unable to encode response" )
172- return Errored (http .StatusInternalServerError , errUnableToEncodeResponse )
184+ resp := Errored (http .StatusInternalServerError , errUnableToEncodeResponse )
185+ // Note: We explicitly have to set the response UID. Usually that is done via resp.Complete.
186+ resp .UID = req .UID
187+ return resp
173188 }
174189
175190 return resp
0 commit comments