diff --git a/staging/src/k8s.io/apimachinery/pkg/api/meta/errors.go b/staging/src/k8s.io/apimachinery/pkg/api/meta/errors.go index cbf5d0263c6..f36aa4ec22a 100644 --- a/staging/src/k8s.io/apimachinery/pkg/api/meta/errors.go +++ b/staging/src/k8s.io/apimachinery/pkg/api/meta/errors.go @@ -17,6 +17,7 @@ limitations under the License. package meta import ( + "errors" "fmt" "k8s.io/apimachinery/pkg/runtime/schema" @@ -43,6 +44,11 @@ func (e *AmbiguousResourceError) Error() string { return fmt.Sprintf("%v matches multiple resources or kinds", e.PartialResource) } +func (*AmbiguousResourceError) Is(target error) bool { + _, ok := target.(*AmbiguousResourceError) + return ok +} + // AmbiguousKindError is returned if the RESTMapper finds multiple matches for a kind type AmbiguousKindError struct { PartialKind schema.GroupVersionKind @@ -63,16 +69,16 @@ func (e *AmbiguousKindError) Error() string { return fmt.Sprintf("%v matches multiple resources or kinds", e.PartialKind) } +func (*AmbiguousKindError) Is(target error) bool { + _, ok := target.(*AmbiguousKindError) + return ok +} + func IsAmbiguousError(err error) bool { if err == nil { return false } - switch err.(type) { - case *AmbiguousResourceError, *AmbiguousKindError: - return true - default: - return false - } + return errors.Is(err, &AmbiguousResourceError{}) || errors.Is(err, &AmbiguousKindError{}) } // NoResourceMatchError is returned if the RESTMapper can't find any match for a resource @@ -84,6 +90,11 @@ func (e *NoResourceMatchError) Error() string { return fmt.Sprintf("no matches for %v", e.PartialResource) } +func (*NoResourceMatchError) Is(target error) bool { + _, ok := target.(*NoResourceMatchError) + return ok +} + // NoKindMatchError is returned if the RESTMapper can't find any match for a kind type NoKindMatchError struct { // GroupKind is the API group and kind that was searched @@ -108,14 +119,14 @@ func (e *NoKindMatchError) Error() string { } } +func (*NoKindMatchError) Is(target error) bool { + _, ok := target.(*NoKindMatchError) + return ok +} + func IsNoMatchError(err error) bool { if err == nil { return false } - switch err.(type) { - case *NoResourceMatchError, *NoKindMatchError: - return true - default: - return false - } + return errors.Is(err, &NoResourceMatchError{}) || errors.Is(err, &NoKindMatchError{}) } diff --git a/staging/src/k8s.io/apimachinery/pkg/api/meta/errors_test.go b/staging/src/k8s.io/apimachinery/pkg/api/meta/errors_test.go new file mode 100644 index 00000000000..56e5d030b44 --- /dev/null +++ b/staging/src/k8s.io/apimachinery/pkg/api/meta/errors_test.go @@ -0,0 +1,79 @@ +/* +Copyright 2022 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package meta + +import ( + "errors" + "fmt" + "testing" + + "k8s.io/apimachinery/pkg/runtime/schema" +) + +func TestErrorMatching(t *testing.T) { + testCases := []struct { + name string + // input should contain an error that is _not_ empty, otherwise the naive reflectlite.DeepEqual matching of + // the errors lib will always succeed, but for all of these we want to verify that the matching is based on + // type. + input error + new func() error + matcherFunc func(error) bool + }{ + { + name: "AmbiguousResourceError", + input: &AmbiguousResourceError{MatchingResources: []schema.GroupVersionResource{{}}}, + new: func() error { return &AmbiguousResourceError{} }, + matcherFunc: IsAmbiguousError, + }, + { + name: "AmbiguousKindError", + input: &AmbiguousKindError{MatchingResources: []schema.GroupVersionResource{{}}}, + new: func() error { return &AmbiguousKindError{} }, + matcherFunc: IsAmbiguousError, + }, + { + name: "NoResourceMatchError", + input: &NoResourceMatchError{PartialResource: schema.GroupVersionResource{Group: "foo"}}, + new: func() error { return &NoResourceMatchError{} }, + matcherFunc: IsNoMatchError, + }, + { + name: "NoKindMatchError", + input: &NoKindMatchError{SearchedVersions: []string{"foo"}}, + new: func() error { return &NoKindMatchError{} }, + matcherFunc: IsNoMatchError, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + if !errors.Is(tc.input, tc.new()) { + t.Error("error doesn't match itself directly") + } + if !errors.Is(fmt.Errorf("wrapepd: %w", tc.input), tc.new()) { + t.Error("error doesn't match itself when wrapped") + } + if !tc.matcherFunc(tc.input) { + t.Errorf("error doesn't get matched by matcherfunc") + } + if errors.Is(tc.input, errors.New("foo")) { + t.Error("error incorrectly matches other error") + } + }) + } +}