diff --git a/api/v3rpc/rpctypes/error.go b/api/v3rpc/rpctypes/error.go index 28f8d7e435f..c630e454cbb 100644 --- a/api/v3rpc/rpctypes/error.go +++ b/api/v3rpc/rpctypes/error.go @@ -239,8 +239,9 @@ var ( // EtcdError defines gRPC server errors. // (https://github.com/grpc/grpc-go/blob/master/rpc_util.go#L319-L323) type EtcdError struct { - code codes.Code - desc string + code codes.Code + desc string + wrappedError error } // Code returns grpc/codes.Code. @@ -253,6 +254,10 @@ func (e EtcdError) Error() string { return e.desc } +func (e EtcdError) Unwrap() error { + return e.wrappedError +} + func Error(err error) error { if err == nil { return nil @@ -268,7 +273,7 @@ func Error(err error) error { } else { desc = verr.Error() } - return EtcdError{code: ev.Code(), desc: desc} + return EtcdError{code: ev.Code(), desc: desc, wrappedError: err} } func ErrorDesc(err error) string { diff --git a/api/v3rpc/rpctypes/error_test.go b/api/v3rpc/rpctypes/error_test.go index bf3e0c68027..3f95265ddfd 100644 --- a/api/v3rpc/rpctypes/error_test.go +++ b/api/v3rpc/rpctypes/error_test.go @@ -15,6 +15,7 @@ package rpctypes import ( + "errors" "testing" "google.golang.org/grpc/codes" @@ -40,3 +41,20 @@ func TestConvert(t *testing.T) { t.Fatalf("expected them to be equal, got %v / %v", ev2.Code(), e3.(EtcdError).Code()) } } + +func TestComparingWrappedError(t *testing.T) { + errTest := errors.New("test error") + e1 := Error(ErrGRPCEmptyKey) + e2 := Error(status.Error(codes.InvalidArgument, "etcdserver: key is not provided")) + e3 := Error(errTest) + + if !errors.Is(e1, ErrGRPCEmptyKey) { + t.Fatalf("expected %v to be an ErrGRPCEmptyKey wrapped error", e1) + } + if !errors.Is(e2, ErrGRPCEmptyKey) { + t.Fatalf("expected %v to be an ErrGRPCEmptyKey wrapped error", e1) + } + if !errors.Is(e3, errTest) { + t.Fatalf("expected %v to be an errTest wrapped error", e3) + } +}