Skip to content

Commit 9f650bc

Browse files
authored
Merge pull request #1231 from k8s-infra-cherrypick-robot/cherry-pick-1230-to-release-1.10
[release-1.10] Return Canceled or DeadlineExceeded error code for context errors returned from polling
2 parents 25d8f4b + 070bf70 commit 9f650bc

File tree

2 files changed

+95
-3
lines changed

2 files changed

+95
-3
lines changed

pkg/gce-pd-csi-driver/utils.go

+34-3
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"errors"
2121
"fmt"
2222
"net/http"
23+
"strings"
2324

2425
"context"
2526

@@ -219,14 +220,21 @@ func containsZone(zones []string, zone string) bool {
219220
}
220221

221222
// CodeForError returns a pointer to the grpc error code that maps to the http
222-
// error code for the passed in user googleapi error. Returns codes.Internal if
223-
// the given error is not a googleapi error caused by the user. The following
224-
// http error codes are considered user errors:
223+
// error code for the passed in user googleapi error or context error. Returns
224+
// codes.Internal if the given error is not a googleapi error caused by the user.
225+
// The following http error codes are considered user errors:
225226
// (1) http 400 Bad Request, returns grpc InvalidArgument,
226227
// (2) http 403 Forbidden, returns grpc PermissionDenied,
227228
// (3) http 404 Not Found, returns grpc NotFound
228229
// (4) http 429 Too Many Requests, returns grpc ResourceExhausted
230+
// The following errors are considered context errors:
231+
// (1) "context deadline exceeded", returns grpc DeadlineExceeded,
232+
// (2) "context canceled", returns grpc Canceled
229233
func CodeForError(err error) *codes.Code {
234+
if code := isContextError(err); code != nil {
235+
return code
236+
}
237+
230238
internalErrorCode := codes.Internal
231239
// Upwrap the error
232240
var apiErr *googleapi.Error
@@ -243,9 +251,32 @@ func CodeForError(err error) *codes.Code {
243251
if code, ok := userErrors[apiErr.Code]; ok {
244252
return &code
245253
}
254+
246255
return &internalErrorCode
247256
}
248257

258+
// isContextError returns a pointer to the grpc error code DeadlineExceeded
259+
// if the passed in error contains the "context deadline exceeded" string and returns
260+
// the grpc error code Canceled if the error contains the "context canceled" string.
261+
func isContextError(err error) *codes.Code {
262+
if err == nil {
263+
return nil
264+
}
265+
266+
errStr := err.Error()
267+
if strings.Contains(errStr, context.DeadlineExceeded.Error()) {
268+
return errCodePtr(codes.DeadlineExceeded)
269+
}
270+
if strings.Contains(errStr, context.Canceled.Error()) {
271+
return errCodePtr(codes.Canceled)
272+
}
273+
return nil
274+
}
275+
276+
func errCodePtr(code codes.Code) *codes.Code {
277+
return &code
278+
}
279+
249280
func LoggedError(msg string, err error) error {
250281
klog.Errorf(msg+"%v", err.Error())
251282
return status.Errorf(*CodeForError(err), msg+"%v", err.Error())

pkg/gce-pd-csi-driver/utils_test.go

+61
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ limitations under the License.
1818
package gceGCEDriver
1919

2020
import (
21+
"context"
2122
"errors"
23+
"fmt"
2224
"net/http"
2325
"testing"
2426

@@ -319,6 +321,16 @@ func TestCodeForError(t *testing.T) {
319321
inputErr: &googleapi.Error{Code: http.StatusInternalServerError, Message: "Internal error"},
320322
expCode: &internalErrorCode,
321323
},
324+
{
325+
name: "context canceled error",
326+
inputErr: context.Canceled,
327+
expCode: errCodePtr(codes.Canceled),
328+
},
329+
{
330+
name: "context deadline exceeded error",
331+
inputErr: context.DeadlineExceeded,
332+
expCode: errCodePtr(codes.DeadlineExceeded),
333+
},
322334
}
323335

324336
for _, tc := range testCases {
@@ -329,3 +341,52 @@ func TestCodeForError(t *testing.T) {
329341
}
330342
}
331343
}
344+
345+
func TestIsContextError(t *testing.T) {
346+
cases := []struct {
347+
name string
348+
err error
349+
expectedErrCode *codes.Code
350+
}{
351+
{
352+
name: "deadline exceeded error",
353+
err: context.DeadlineExceeded,
354+
expectedErrCode: errCodePtr(codes.DeadlineExceeded),
355+
},
356+
{
357+
name: "contains 'context deadline exceeded'",
358+
err: fmt.Errorf("got error: %w", context.DeadlineExceeded),
359+
expectedErrCode: errCodePtr(codes.DeadlineExceeded),
360+
},
361+
{
362+
name: "context canceled error",
363+
err: context.Canceled,
364+
expectedErrCode: errCodePtr(codes.Canceled),
365+
},
366+
{
367+
name: "contains 'context canceled'",
368+
err: fmt.Errorf("got error: %w", context.Canceled),
369+
expectedErrCode: errCodePtr(codes.Canceled),
370+
},
371+
{
372+
name: "does not contain 'context canceled' or 'context deadline exceeded'",
373+
err: fmt.Errorf("unknown error"),
374+
expectedErrCode: nil,
375+
},
376+
{
377+
name: "nil error",
378+
err: nil,
379+
expectedErrCode: nil,
380+
},
381+
}
382+
383+
for _, test := range cases {
384+
errCode := isContextError(test.err)
385+
if (test.expectedErrCode == nil) != (errCode == nil) {
386+
t.Errorf("test %v failed: got %v, expected %v", test.name, errCode, test.expectedErrCode)
387+
}
388+
if test.expectedErrCode != nil && *errCode != *test.expectedErrCode {
389+
t.Errorf("test %v failed: got %v, expected %v", test.name, errCode, test.expectedErrCode)
390+
}
391+
}
392+
}

0 commit comments

Comments
 (0)