diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating/dispatcher.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating/dispatcher.go index 6f882a900ef..7d32e65d991 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating/dispatcher.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating/dispatcher.go @@ -136,10 +136,18 @@ func (a *mutatingDispatcher) Dispatch(ctx context.Context, attr admission.Attrib ignoreClientCallFailures := hook.FailurePolicy != nil && *hook.FailurePolicy == v1beta1.Ignore rejected := false if err != nil { - // ErrCallingWebhook is ignored if the webhook is configured to failopen. - // Otherwise the request is rejected. - if _, ok := err.(*webhookutil.ErrCallingWebhook); !ok || !ignoreClientCallFailures { + switch err := err.(type) { + case *webhookutil.ErrCallingWebhook: + if !ignoreClientCallFailures { + rejected = true + admissionmetrics.Metrics.ObserveWebhookRejection(hook.Name, "admit", string(versionedAttr.Attributes.GetOperation()), admissionmetrics.WebhookRejectionCallingWebhookError, 0) + } + case *webhookutil.ErrWebhookRejection: rejected = true + admissionmetrics.Metrics.ObserveWebhookRejection(hook.Name, "admit", string(versionedAttr.Attributes.GetOperation()), admissionmetrics.WebhookRejectionNoError, int(err.Status.ErrStatus.Code)) + default: + rejected = true + admissionmetrics.Metrics.ObserveWebhookRejection(hook.Name, "admit", string(versionedAttr.Attributes.GetOperation()), admissionmetrics.WebhookRejectionAPIServerInternalError, 0) } } admissionmetrics.Metrics.ObserveWebhook(time.Since(t), rejected, versionedAttr.Attributes, "admit", hook.Name) @@ -172,6 +180,9 @@ func (a *mutatingDispatcher) Dispatch(ctx context.Context, attr admission.Attrib klog.Warningf("Failed calling webhook, failing closed %v: %v", hook.Name, err) return apierrors.NewInternalError(err) } + if rejectionErr, ok := err.(*webhookutil.ErrWebhookRejection); ok { + return rejectionErr.Status + } return err } @@ -257,7 +268,7 @@ func (a *mutatingDispatcher) callAttrMutatingHook(ctx context.Context, h *v1beta } if !result.Allowed { - return false, webhookerrors.ToStatusErr(h.Name, result.Result) + return false, &webhookutil.ErrWebhookRejection{Status: webhookerrors.ToStatusErr(h.Name, result.Result)} } if len(result.Patch) == 0 { diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/validating/dispatcher.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/validating/dispatcher.go index 019b6e86c55..20a115acd77 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/validating/dispatcher.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/validating/dispatcher.go @@ -104,10 +104,18 @@ func (d *validatingDispatcher) Dispatch(ctx context.Context, attr admission.Attr ignoreClientCallFailures := hook.FailurePolicy != nil && *hook.FailurePolicy == v1beta1.Ignore rejected := false if err != nil { - // ErrCallingWebhook is ignored if the webhook is configured to failopen. - // Otherwise the request is rejected. - if _, ok := err.(*webhookutil.ErrCallingWebhook); !ok || !ignoreClientCallFailures { + switch err := err.(type) { + case *webhookutil.ErrCallingWebhook: + if !ignoreClientCallFailures { + rejected = true + admissionmetrics.Metrics.ObserveWebhookRejection(hook.Name, "validating", string(versionedAttr.Attributes.GetOperation()), admissionmetrics.WebhookRejectionCallingWebhookError, 0) + } + case *webhookutil.ErrWebhookRejection: rejected = true + admissionmetrics.Metrics.ObserveWebhookRejection(hook.Name, "validating", string(versionedAttr.Attributes.GetOperation()), admissionmetrics.WebhookRejectionNoError, int(err.Status.ErrStatus.Code)) + default: + rejected = true + admissionmetrics.Metrics.ObserveWebhookRejection(hook.Name, "validating", string(versionedAttr.Attributes.GetOperation()), admissionmetrics.WebhookRejectionAPIServerInternalError, 0) } } admissionmetrics.Metrics.ObserveWebhook(time.Since(t), rejected, versionedAttr.Attributes, "validating", hook.Name) @@ -127,6 +135,9 @@ func (d *validatingDispatcher) Dispatch(ctx context.Context, attr admission.Attr return } + if rejectionErr, ok := err.(*webhookutil.ErrWebhookRejection); ok { + err = rejectionErr.Status + } klog.Warningf("rejected by webhook %q: %#v", hook.Name, err) errCh <- err }(relevantHooks[i]) @@ -219,5 +230,5 @@ func (d *validatingDispatcher) callHook(ctx context.Context, h *v1beta1.Validati if result.Allowed { return nil } - return webhookerrors.ToStatusErr(h.Name, result.Result) + return &webhookutil.ErrWebhookRejection{Status: webhookerrors.ToStatusErr(h.Name, result.Result)} } diff --git a/staging/src/k8s.io/apiserver/pkg/util/webhook/error.go b/staging/src/k8s.io/apiserver/pkg/util/webhook/error.go index 4701530205d..9c6f780e2f8 100644 --- a/staging/src/k8s.io/apiserver/pkg/util/webhook/error.go +++ b/staging/src/k8s.io/apiserver/pkg/util/webhook/error.go @@ -16,7 +16,11 @@ limitations under the License. package webhook -import "fmt" +import ( + "fmt" + + apierrors "k8s.io/apimachinery/pkg/api/errors" +) // ErrCallingWebhook is returned for transport-layer errors calling webhooks. It // represents a failure to talk to the webhook, not the webhook rejecting a @@ -32,3 +36,12 @@ func (e *ErrCallingWebhook) Error() string { } return fmt.Sprintf("failed calling webhook %q; no further details available", e.WebhookName) } + +// ErrWebhookRejection represents a webhook properly rejecting a request. +type ErrWebhookRejection struct { + Status *apierrors.StatusError +} + +func (e *ErrWebhookRejection) Error() string { + return e.Status.Error() +}