Skip to content

Commit fd0e482

Browse files
committed
Split gRPC and HTTP error utility into seperate packages
By having seperate packages, users can consume base package without pulling gRPC or HTTP as a dependency if not required. Signed-off-by: Austin Vazquez <[email protected]>
1 parent 98ae5ec commit fd0e482

6 files changed

Lines changed: 320 additions & 121 deletions

File tree

grpc.go renamed to errgrpc/grpc.go

Lines changed: 49 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,12 @@
1414
limitations under the License.
1515
*/
1616

17-
package errdefs
17+
// Package errgrpc provides utility functions for translating errors to
18+
// and from a gRPC context.
19+
//
20+
// The functions ToGRPC and ToNative can be used to map server-side and
21+
// client-side errors to the correct types.
22+
package errgrpc
1823

1924
import (
2025
"context"
@@ -24,6 +29,9 @@ import (
2429

2530
"google.golang.org/grpc/codes"
2631
"google.golang.org/grpc/status"
32+
33+
"github.com/containerd/errdefs"
34+
"github.com/containerd/errdefs/internal/cause"
2735
)
2836

2937
// ToGRPC will attempt to map the backend containerd error into a grpc error,
@@ -45,37 +53,37 @@ func ToGRPC(err error) error {
4553
}
4654

4755
switch {
48-
case IsInvalidArgument(err):
56+
case errdefs.IsInvalidArgument(err):
4957
return status.Error(codes.InvalidArgument, err.Error())
50-
case IsNotFound(err):
58+
case errdefs.IsNotFound(err):
5159
return status.Error(codes.NotFound, err.Error())
52-
case IsAlreadyExists(err):
60+
case errdefs.IsAlreadyExists(err):
5361
return status.Error(codes.AlreadyExists, err.Error())
54-
case IsFailedPrecondition(err) || IsConflict(err) || IsNotModified(err):
62+
case errdefs.IsFailedPrecondition(err) || errdefs.IsConflict(err) || errdefs.IsNotModified(err):
5563
return status.Error(codes.FailedPrecondition, err.Error())
56-
case IsUnavailable(err):
64+
case errdefs.IsUnavailable(err):
5765
return status.Error(codes.Unavailable, err.Error())
58-
case IsNotImplemented(err):
66+
case errdefs.IsNotImplemented(err):
5967
return status.Error(codes.Unimplemented, err.Error())
60-
case IsCanceled(err):
68+
case errdefs.IsCanceled(err):
6169
return status.Error(codes.Canceled, err.Error())
62-
case IsDeadlineExceeded(err):
70+
case errdefs.IsDeadlineExceeded(err):
6371
return status.Error(codes.DeadlineExceeded, err.Error())
64-
case IsUnauthorized(err):
72+
case errdefs.IsUnauthorized(err):
6573
return status.Error(codes.Unauthenticated, err.Error())
66-
case IsPermissionDenied(err):
74+
case errdefs.IsPermissionDenied(err):
6775
return status.Error(codes.PermissionDenied, err.Error())
68-
case IsInternal(err):
76+
case errdefs.IsInternal(err):
6977
return status.Error(codes.Internal, err.Error())
70-
case IsDataLoss(err):
78+
case errdefs.IsDataLoss(err):
7179
return status.Error(codes.DataLoss, err.Error())
72-
case IsAborted(err):
80+
case errdefs.IsAborted(err):
7381
return status.Error(codes.Aborted, err.Error())
74-
case IsOutOfRange(err):
82+
case errdefs.IsOutOfRange(err):
7583
return status.Error(codes.OutOfRange, err.Error())
76-
case IsResourceExhausted(err):
84+
case errdefs.IsResourceExhausted(err):
7785
return status.Error(codes.ResourceExhausted, err.Error())
78-
case IsUnknown(err):
86+
case errdefs.IsUnknown(err):
7987
return status.Error(codes.Unknown, err.Error())
8088
}
8189

@@ -85,13 +93,13 @@ func ToGRPC(err error) error {
8593
// ToGRPCf maps the error to grpc error codes, assembling the formatting string
8694
// and combining it with the target error string.
8795
//
88-
// This is equivalent to errdefs.ToGRPC(fmt.Errorf("%s: %w", fmt.Sprintf(format, args...), err))
96+
// This is equivalent to grpc.ToGRPC(fmt.Errorf("%s: %w", fmt.Sprintf(format, args...), err))
8997
func ToGRPCf(err error, format string, args ...interface{}) error {
9098
return ToGRPC(fmt.Errorf("%s: %w", fmt.Sprintf(format, args...), err))
9199
}
92100

93-
// FromGRPC returns the underlying error from a grpc service based on the grpc error code
94-
func FromGRPC(err error) error {
101+
// ToNative returns the underlying error from a grpc service based on the grpc error code
102+
func ToNative(err error) error {
95103
if err == nil {
96104
return nil
97105
}
@@ -102,49 +110,49 @@ func FromGRPC(err error) error {
102110

103111
switch code(err) {
104112
case codes.InvalidArgument:
105-
cls = ErrInvalidArgument
113+
cls = errdefs.ErrInvalidArgument
106114
case codes.AlreadyExists:
107-
cls = ErrAlreadyExists
115+
cls = errdefs.ErrAlreadyExists
108116
case codes.NotFound:
109-
cls = ErrNotFound
117+
cls = errdefs.ErrNotFound
110118
case codes.Unavailable:
111-
cls = ErrUnavailable
119+
cls = errdefs.ErrUnavailable
112120
case codes.FailedPrecondition:
113-
if desc == ErrConflict.Error() || strings.HasSuffix(desc, ": "+ErrConflict.Error()) {
114-
cls = ErrConflict
115-
} else if desc == ErrNotModified.Error() || strings.HasSuffix(desc, ": "+ErrNotModified.Error()) {
116-
cls = ErrNotModified
121+
if desc == errdefs.ErrConflict.Error() || strings.HasSuffix(desc, ": "+errdefs.ErrConflict.Error()) {
122+
cls = errdefs.ErrConflict
123+
} else if desc == errdefs.ErrNotModified.Error() || strings.HasSuffix(desc, ": "+errdefs.ErrNotModified.Error()) {
124+
cls = errdefs.ErrNotModified
117125
} else {
118-
cls = ErrFailedPrecondition
126+
cls = errdefs.ErrFailedPrecondition
119127
}
120128
case codes.Unimplemented:
121-
cls = ErrNotImplemented
129+
cls = errdefs.ErrNotImplemented
122130
case codes.Canceled:
123131
cls = context.Canceled
124132
case codes.DeadlineExceeded:
125133
cls = context.DeadlineExceeded
126134
case codes.Aborted:
127-
cls = ErrAborted
135+
cls = errdefs.ErrAborted
128136
case codes.Unauthenticated:
129-
cls = ErrUnauthenticated
137+
cls = errdefs.ErrUnauthenticated
130138
case codes.PermissionDenied:
131-
cls = ErrPermissionDenied
139+
cls = errdefs.ErrPermissionDenied
132140
case codes.Internal:
133-
cls = ErrInternal
141+
cls = errdefs.ErrInternal
134142
case codes.DataLoss:
135-
cls = ErrDataLoss
143+
cls = errdefs.ErrDataLoss
136144
case codes.OutOfRange:
137-
cls = ErrOutOfRange
145+
cls = errdefs.ErrOutOfRange
138146
case codes.ResourceExhausted:
139-
cls = ErrResourceExhausted
147+
cls = errdefs.ErrResourceExhausted
140148
default:
141-
if idx := strings.LastIndex(desc, unexpectedStatusPrefix); idx > 0 {
142-
if status, err := strconv.Atoi(desc[idx+len(unexpectedStatusPrefix):]); err == nil && status >= 200 && status < 600 {
143-
cls = errUnexpectedStatus{status}
149+
if idx := strings.LastIndex(desc, cause.UnexpectedStatusPrefix); idx > 0 {
150+
if status, err := strconv.Atoi(desc[idx+len(cause.UnexpectedStatusPrefix):]); err == nil && status >= 200 && status < 600 {
151+
cls = cause.ErrUnexpectedStatus{Status: status}
144152
}
145153
}
146154
if cls == nil {
147-
cls = ErrUnknown
155+
cls = errdefs.ErrUnknown
148156
}
149157
}
150158

grpc_test.go renamed to errgrpc/grpc_test.go

Lines changed: 74 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
limitations under the License.
1515
*/
1616

17-
package errdefs
17+
package errgrpc
1818

1919
import (
2020
"context"
@@ -24,8 +24,21 @@ import (
2424

2525
"google.golang.org/grpc/codes"
2626
"google.golang.org/grpc/status"
27+
28+
"github.com/containerd/errdefs"
29+
"github.com/containerd/errdefs/errhttp"
30+
"github.com/containerd/errdefs/internal/cause"
2731
)
2832

33+
func TestGRPCNilInput(t *testing.T) {
34+
if err := ToGRPC(nil); err != nil {
35+
t.Fatalf("Expected nil error, got %v", err)
36+
}
37+
if err := ToNative(nil); err != nil {
38+
t.Fatalf("Expected nil error, got %v", err)
39+
}
40+
}
41+
2942
func TestGRPCRoundTrip(t *testing.T) {
3043
errShouldLeaveAlone := errors.New("unknown to package")
3144

@@ -35,28 +48,72 @@ func TestGRPCRoundTrip(t *testing.T) {
3548
str string
3649
}{
3750
{
38-
input: ErrAlreadyExists,
39-
cause: ErrAlreadyExists,
51+
input: errdefs.ErrInvalidArgument,
52+
cause: errdefs.ErrInvalidArgument,
53+
},
54+
{
55+
input: errdefs.ErrAlreadyExists,
56+
cause: errdefs.ErrAlreadyExists,
57+
},
58+
{
59+
input: errdefs.ErrNotFound,
60+
cause: errdefs.ErrNotFound,
61+
},
62+
{
63+
input: errdefs.ErrUnavailable,
64+
cause: errdefs.ErrUnavailable,
65+
},
66+
{
67+
input: errdefs.ErrNotImplemented,
68+
cause: errdefs.ErrNotImplemented,
69+
},
70+
{
71+
input: errdefs.ErrUnauthenticated,
72+
cause: errdefs.ErrUnauthenticated,
73+
},
74+
{
75+
input: errdefs.ErrPermissionDenied,
76+
cause: errdefs.ErrPermissionDenied,
77+
},
78+
{
79+
input: errdefs.ErrInternal,
80+
cause: errdefs.ErrInternal,
81+
},
82+
{
83+
input: errdefs.ErrDataLoss,
84+
cause: errdefs.ErrDataLoss,
4085
},
4186
{
42-
input: ErrNotFound,
43-
cause: ErrNotFound,
87+
input: errdefs.ErrAborted,
88+
cause: errdefs.ErrAborted,
89+
},
90+
{
91+
input: errdefs.ErrOutOfRange,
92+
cause: errdefs.ErrOutOfRange,
93+
},
94+
{
95+
input: errdefs.ErrResourceExhausted,
96+
cause: errdefs.ErrResourceExhausted,
97+
},
98+
{
99+
input: errdefs.ErrUnknown,
100+
cause: errdefs.ErrUnknown,
44101
},
45102
//nolint:dupword
46103
{
47-
input: fmt.Errorf("test test test: %w", ErrFailedPrecondition),
48-
cause: ErrFailedPrecondition,
104+
input: fmt.Errorf("test test test: %w", errdefs.ErrFailedPrecondition),
105+
cause: errdefs.ErrFailedPrecondition,
49106
str: "test test test: failed precondition",
50107
},
51108
{
52109
input: status.Errorf(codes.Unavailable, "should be not available"),
53-
cause: ErrUnavailable,
110+
cause: errdefs.ErrUnavailable,
54111
str: "should be not available: unavailable",
55112
},
56113
{
57114
input: errShouldLeaveAlone,
58-
cause: ErrUnknown,
59-
str: errShouldLeaveAlone.Error() + ": " + ErrUnknown.Error(),
115+
cause: errdefs.ErrUnknown,
116+
str: errShouldLeaveAlone.Error() + ": " + errdefs.ErrUnknown.Error(),
60117
},
61118
{
62119
input: context.Canceled,
@@ -79,26 +136,26 @@ func TestGRPCRoundTrip(t *testing.T) {
79136
str: "this is a test deadline exceeded: context deadline exceeded",
80137
},
81138
{
82-
input: fmt.Errorf("something conflicted: %w", ErrConflict),
83-
cause: ErrConflict,
139+
input: fmt.Errorf("something conflicted: %w", errdefs.ErrConflict),
140+
cause: errdefs.ErrConflict,
84141
str: "something conflicted: conflict",
85142
},
86143
{
87-
input: fmt.Errorf("everything is the same: %w", ErrNotModified),
88-
cause: ErrNotModified,
144+
input: fmt.Errorf("everything is the same: %w", errdefs.ErrNotModified),
145+
cause: errdefs.ErrNotModified,
89146
str: "everything is the same: not modified",
90147
},
91148
{
92-
input: fmt.Errorf("odd HTTP response: %w", FromHTTP(418)),
93-
cause: errUnexpectedStatus{418},
149+
input: fmt.Errorf("odd HTTP response: %w", errhttp.ToNative(418)),
150+
cause: cause.ErrUnexpectedStatus{Status: 418},
94151
str: "odd HTTP response: unexpected status 418",
95152
},
96153
} {
97154
t.Run(testcase.input.Error(), func(t *testing.T) {
98155
t.Logf("input: %v", testcase.input)
99156
gerr := ToGRPC(testcase.input)
100157
t.Logf("grpc: %v", gerr)
101-
ferr := FromGRPC(gerr)
158+
ferr := ToNative(gerr)
102159
t.Logf("recovered: %v", ferr)
103160

104161
if !errors.Is(ferr, testcase.cause) {
@@ -114,5 +171,4 @@ func TestGRPCRoundTrip(t *testing.T) {
114171
}
115172
})
116173
}
117-
118174
}

0 commit comments

Comments
 (0)