Merge pull request #88465 from alvaroaleman/utilerrors-implement-errors-is

Utilerrors.Aggregate: Allow using with errors.Is()

Kubernetes-commit: c812375ed605dafa3a7802fc9e21165031de9034
This commit is contained in:
Kubernetes Publisher 2020-03-05 20:03:53 -08:00
commit d511753639
5 changed files with 134 additions and 5 deletions

2
Godeps/Godeps.json generated
View File

@ -352,7 +352,7 @@
},
{
"ImportPath": "k8s.io/apimachinery",
"Rev": "6584f51ae935"
"Rev": "003f3e63f669"
},
{
"ImportPath": "k8s.io/gengo",

4
go.mod
View File

@ -29,7 +29,7 @@ require (
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4
google.golang.org/appengine v1.5.0 // indirect
k8s.io/api v0.0.0-20200306002249-bf5c8537bc90
k8s.io/apimachinery v0.0.0-20200303201514-6584f51ae935
k8s.io/apimachinery v0.0.0-20200306042133-003f3e63f669
k8s.io/klog v1.0.0
k8s.io/utils v0.0.0-20200229041039-0a110f9eb7ab
sigs.k8s.io/yaml v1.2.0
@ -39,5 +39,5 @@ replace (
golang.org/x/sys => golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a // pinned to release-branch.go1.13
golang.org/x/tools => golang.org/x/tools v0.0.0-20190821162956-65e3620a7ae7 // pinned to release-branch.go1.13
k8s.io/api => k8s.io/api v0.0.0-20200306002249-bf5c8537bc90
k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20200303201514-6584f51ae935
k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20200306042133-003f3e63f669
)

2
go.sum
View File

@ -183,7 +183,7 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
k8s.io/api v0.0.0-20200306002249-bf5c8537bc90/go.mod h1:EFuendCidCp9DUXAn3QXS0nWIaAgQYL8VaCqs8KTZBA=
k8s.io/apimachinery v0.0.0-20200303201514-6584f51ae935/go.mod h1:5X8oEhnd931nEg6/Nkumo00nT6ZsCLp2h7Xwd7Ym6P4=
k8s.io/apimachinery v0.0.0-20200306042133-003f3e63f669/go.mod h1:5X8oEhnd931nEg6/Nkumo00nT6ZsCLp2h7Xwd7Ym6P4=
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=

View File

@ -86,11 +86,41 @@ func (e errConfigurationInvalid) Error() string {
return fmt.Sprintf("invalid configuration: %v", utilerrors.NewAggregate(e).Error())
}
// Errors implements the AggregateError interface
// Errors implements the utilerrors.Aggregate interface
func (e errConfigurationInvalid) Errors() []error {
return e
}
// Is implements the utilerrors.Aggregate interface
func (e errConfigurationInvalid) Is(target error) bool {
return e.visit(func(err error) bool {
return errors.Is(err, target)
})
}
func (e errConfigurationInvalid) visit(f func(err error) bool) bool {
for _, err := range e {
switch err := err.(type) {
case errConfigurationInvalid:
if match := err.visit(f); match {
return match
}
case utilerrors.Aggregate:
for _, nestedErr := range err.Errors() {
if match := f(nestedErr); match {
return match
}
}
default:
if match := f(err); match {
return match
}
}
}
return false
}
// IsConfigurationInvalid returns true if the provided error indicates the configuration is invalid.
func IsConfigurationInvalid(err error) bool {
switch err.(type) {

View File

@ -17,6 +17,8 @@ limitations under the License.
package clientcmd
import (
"errors"
"fmt"
"io/ioutil"
"os"
"strings"
@ -569,3 +571,100 @@ func (c configValidationTest) testAuthInfo(authInfoName string, t *testing.T) {
}
}
}
type alwaysMatchingError struct{}
func (_ alwaysMatchingError) Error() string {
return "error"
}
func (_ alwaysMatchingError) Is(_ error) bool {
return true
}
type someError struct{ msg string }
func (se someError) Error() string {
if se.msg != "" {
return se.msg
}
return "err"
}
func TestErrConfigurationInvalidWithErrorsIs(t *testing.T) {
testCases := []struct {
name string
err error
matchAgainst error
expectMatch bool
}{
{
name: "no match",
err: errConfigurationInvalid{errors.New("my-error"), errors.New("my-other-error")},
matchAgainst: fmt.Errorf("no entry %s", "here"),
},
{
name: "match via .Is()",
err: errConfigurationInvalid{errors.New("forbidden"), alwaysMatchingError{}},
matchAgainst: errors.New("unauthorized"),
expectMatch: true,
},
{
name: "match via equality",
err: errConfigurationInvalid{errors.New("err"), someError{}},
matchAgainst: someError{},
expectMatch: true,
},
{
name: "match via nested aggregate",
err: errConfigurationInvalid{errors.New("closed today"), errConfigurationInvalid{errConfigurationInvalid{someError{}}}},
matchAgainst: someError{},
expectMatch: true,
},
{
name: "match via wrapped aggregate",
err: fmt.Errorf("wrap: %w", errConfigurationInvalid{errors.New("err"), someError{}}),
matchAgainst: someError{},
expectMatch: true,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
result := errors.Is(tc.err, tc.matchAgainst)
if result != tc.expectMatch {
t.Errorf("expected match: %t, got match: %t", tc.expectMatch, result)
}
})
}
}
type accessTrackingError struct {
wasAccessed bool
}
func (accessTrackingError) Error() string {
return "err"
}
func (ate *accessTrackingError) Is(_ error) bool {
ate.wasAccessed = true
return true
}
var _ error = &accessTrackingError{}
func TestErrConfigurationInvalidWithErrorsIsShortCircuitsOnFirstMatch(t *testing.T) {
errC := errConfigurationInvalid{&accessTrackingError{}, &accessTrackingError{}}
_ = errors.Is(errC, &accessTrackingError{})
var numAccessed int
for _, err := range errC {
if ate := err.(*accessTrackingError); ate.wasAccessed {
numAccessed++
}
}
if numAccessed != 1 {
t.Errorf("expected exactly one error to get accessed, got %d", numAccessed)
}
}