mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 03:41:45 +00:00
Merge pull request #108532 from cbandy/status-cause
Use errors.As to detect wrapping in StatusCause
This commit is contained in:
commit
e85dcc5337
@ -87,21 +87,21 @@ func (e *StatusError) DebugError() (string, []interface{}) {
|
|||||||
|
|
||||||
// HasStatusCause returns true if the provided error has a details cause
|
// HasStatusCause returns true if the provided error has a details cause
|
||||||
// with the provided type name.
|
// 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 {
|
func HasStatusCause(err error, name metav1.CauseType) bool {
|
||||||
_, ok := StatusCause(err, name)
|
_, ok := StatusCause(err, name)
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
// StatusCause returns the named cause from the provided error if it exists and
|
// 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) {
|
func StatusCause(err error, name metav1.CauseType) (metav1.StatusCause, bool) {
|
||||||
apierr, ok := err.(APIStatus)
|
status, ok := err.(APIStatus)
|
||||||
if !ok || apierr == nil || apierr.Status().Details == nil {
|
if (ok || errors.As(err, &status)) && status.Status().Details != nil {
|
||||||
return metav1.StatusCause{}, false
|
for _, cause := range status.Status().Details.Causes {
|
||||||
}
|
if cause.Type == name {
|
||||||
for _, cause := range apierr.Status().Details.Causes {
|
return cause, true
|
||||||
if cause.Type == name {
|
}
|
||||||
return cause, true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return metav1.StatusCause{}, false
|
return metav1.StatusCause{}, false
|
||||||
@ -757,7 +757,8 @@ func IsRequestEntityTooLargeError(err error) bool {
|
|||||||
// and may be the result of another HTTP actor.
|
// and may be the result of another HTTP actor.
|
||||||
// It supports wrapped errors and returns false when the error is nil.
|
// It supports wrapped errors and returns false when the error is nil.
|
||||||
func IsUnexpectedServerError(err error) bool {
|
func IsUnexpectedServerError(err error) bool {
|
||||||
if status := APIStatus(nil); errors.As(err, &status) && status.Status().Details != nil {
|
status, ok := err.(APIStatus)
|
||||||
|
if (ok || errors.As(err, &status)) && status.Status().Details != nil {
|
||||||
for _, cause := range status.Status().Details.Causes {
|
for _, cause := range status.Status().Details.Causes {
|
||||||
if cause.Type == metav1.CauseTypeUnexpectedServerResponse {
|
if cause.Type == metav1.CauseTypeUnexpectedServerResponse {
|
||||||
return true
|
return true
|
||||||
@ -770,8 +771,8 @@ func IsUnexpectedServerError(err error) bool {
|
|||||||
// IsUnexpectedObjectError determines if err is due to an unexpected object from the master.
|
// IsUnexpectedObjectError determines if err is due to an unexpected object from the master.
|
||||||
// It supports wrapped errors and returns false when the error is nil.
|
// It supports wrapped errors and returns false when the error is nil.
|
||||||
func IsUnexpectedObjectError(err error) bool {
|
func IsUnexpectedObjectError(err error) bool {
|
||||||
uoe := &UnexpectedObjectError{}
|
uoe, ok := err.(*UnexpectedObjectError)
|
||||||
return err != nil && errors.As(err, &uoe)
|
return err != nil && (ok || errors.As(err, &uoe))
|
||||||
}
|
}
|
||||||
|
|
||||||
// SuggestsClientDelay returns true if this error suggests a client delay as well as the
|
// SuggestsClientDelay returns true if this error suggests a client delay as well as the
|
||||||
@ -780,7 +781,8 @@ func IsUnexpectedObjectError(err error) bool {
|
|||||||
// request delay without retry.
|
// request delay without retry.
|
||||||
// It supports wrapped errors and returns false when the error is nil.
|
// It supports wrapped errors and returns false when the error is nil.
|
||||||
func SuggestsClientDelay(err error) (int, bool) {
|
func SuggestsClientDelay(err error) (int, bool) {
|
||||||
if t := APIStatus(nil); errors.As(err, &t) && t.Status().Details != nil {
|
t, ok := err.(APIStatus)
|
||||||
|
if (ok || errors.As(err, &t)) && t.Status().Details != nil {
|
||||||
switch t.Status().Reason {
|
switch t.Status().Reason {
|
||||||
// this StatusReason explicitly requests the caller to delay the action
|
// this StatusReason explicitly requests the caller to delay the action
|
||||||
case metav1.StatusReasonServerTimeout:
|
case metav1.StatusReasonServerTimeout:
|
||||||
@ -798,14 +800,14 @@ func SuggestsClientDelay(err error) (int, bool) {
|
|||||||
// It supports wrapped errors and returns StatusReasonUnknown when
|
// It supports wrapped errors and returns StatusReasonUnknown when
|
||||||
// the error is nil or doesn't have a status.
|
// the error is nil or doesn't have a status.
|
||||||
func ReasonForError(err error) metav1.StatusReason {
|
func ReasonForError(err error) metav1.StatusReason {
|
||||||
if status := APIStatus(nil); errors.As(err, &status) {
|
if status, ok := err.(APIStatus); ok || errors.As(err, &status) {
|
||||||
return status.Status().Reason
|
return status.Status().Reason
|
||||||
}
|
}
|
||||||
return metav1.StatusReasonUnknown
|
return metav1.StatusReasonUnknown
|
||||||
}
|
}
|
||||||
|
|
||||||
func reasonAndCodeForError(err error) (metav1.StatusReason, int32) {
|
func reasonAndCodeForError(err error) (metav1.StatusReason, int32) {
|
||||||
if status := APIStatus(nil); errors.As(err, &status) {
|
if status, ok := err.(APIStatus); ok || errors.As(err, &status) {
|
||||||
return status.Status().Reason, status.Status().Code
|
return status.Status().Reason, status.Status().Code
|
||||||
}
|
}
|
||||||
return metav1.StatusReasonUnknown, 0
|
return metav1.StatusReasonUnknown, 0
|
||||||
|
@ -625,3 +625,81 @@ 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkIsAlreadyExistsWrappedErrors(b *testing.B) {
|
||||||
|
err := NewAlreadyExists(schema.GroupResource{}, "")
|
||||||
|
wrapped := fmt.Errorf("once: %w", err)
|
||||||
|
|
||||||
|
b.Run("Nil", func(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
IsAlreadyExists(nil)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
b.Run("Bare", func(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
IsAlreadyExists(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
b.Run("Wrapped", func(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
IsAlreadyExists(wrapped)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkIsNotFoundWrappedErrors(b *testing.B) {
|
||||||
|
err := NewNotFound(schema.GroupResource{}, "")
|
||||||
|
wrapped := fmt.Errorf("once: %w", err)
|
||||||
|
|
||||||
|
b.Run("Nil", func(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
IsNotFound(nil)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
b.Run("Bare", func(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
IsNotFound(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
b.Run("Wrapped", func(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
IsNotFound(wrapped)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user