diff --git a/staging/src/k8s.io/apimachinery/pkg/api/errors/errors.go b/staging/src/k8s.io/apimachinery/pkg/api/errors/errors.go index fab187a6a09..89a3ceaaac7 100644 --- a/staging/src/k8s.io/apimachinery/pkg/api/errors/errors.go +++ b/staging/src/k8s.io/apimachinery/pkg/api/errors/errors.go @@ -87,21 +87,21 @@ func (e *StatusError) DebugError() (string, []interface{}) { // HasStatusCause returns true if the provided error has a details cause // with the provided type name. +// It supports wrapped errors and returns false when the error is nil. func HasStatusCause(err error, name metav1.CauseType) bool { _, ok := StatusCause(err, name) return ok } // StatusCause returns the named cause from the provided error if it exists and -// the error is of the type APIStatus. Otherwise it returns false. +// the error unwraps to the type APIStatus. Otherwise it returns false. func StatusCause(err error, name metav1.CauseType) (metav1.StatusCause, bool) { - apierr, ok := err.(APIStatus) - if !ok || apierr == nil || apierr.Status().Details == nil { - return metav1.StatusCause{}, false - } - for _, cause := range apierr.Status().Details.Causes { - if cause.Type == name { - return cause, true + var status APIStatus + if errors.As(err, &status) && status.Status().Details != nil { + for _, cause := range status.Status().Details.Causes { + if cause.Type == name { + return cause, true + } } } return metav1.StatusCause{}, false diff --git a/staging/src/k8s.io/apimachinery/pkg/api/errors/errors_test.go b/staging/src/k8s.io/apimachinery/pkg/api/errors/errors_test.go index d41d0349347..5feb287d846 100644 --- a/staging/src/k8s.io/apimachinery/pkg/api/errors/errors_test.go +++ b/staging/src/k8s.io/apimachinery/pkg/api/errors/errors_test.go @@ -625,3 +625,35 @@ func TestIsErrorTypesByReasonAndCode(t *testing.T) { } } + +func TestStatusCauseSupportsWrappedErrors(t *testing.T) { + err := &StatusError{ErrStatus: metav1.Status{ + Details: &metav1.StatusDetails{ + Causes: []metav1.StatusCause{{Type: "SomeCause"}}, + }, + }} + + if cause, ok := StatusCause(nil, "SomeCause"); ok { + t.Errorf("expected no cause for nil, got %v: %#v", ok, cause) + } + if cause, ok := StatusCause(errors.New("boom"), "SomeCause"); ok { + t.Errorf("expected no cause for wrong type, got %v: %#v", ok, cause) + } + + if cause, ok := StatusCause(err, "Other"); ok { + t.Errorf("expected no cause for wrong name, got %v: %#v", ok, cause) + } + if cause, ok := StatusCause(err, "SomeCause"); !ok || cause != err.ErrStatus.Details.Causes[0] { + t.Errorf("expected cause, got %v: %#v", ok, cause) + } + + wrapped := fmt.Errorf("once: %w", err) + if cause, ok := StatusCause(wrapped, "SomeCause"); !ok || cause != err.ErrStatus.Details.Causes[0] { + t.Errorf("expected cause when wrapped, got %v: %#v", ok, cause) + } + + nested := fmt.Errorf("twice: %w", wrapped) + if cause, ok := StatusCause(nested, "SomeCause"); !ok || cause != err.ErrStatus.Details.Causes[0] { + t.Errorf("expected cause when nested, got %v: %#v", ok, cause) + } +}