mirror of
https://github.com/kubernetes/client-go.git
synced 2025-09-02 07:35:21 +00:00
client-go: wrap error from previous attempt to provide more context
Kubernetes-commit: 868b5a31d382b325f49e1b831e6b094282d50cc7
This commit is contained in:
committed by
Kubernetes Publisher
parent
ed2838156a
commit
7c9347d386
@@ -20,9 +20,12 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -233,3 +236,166 @@ func TestIsNextRetry(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestWrapPreviousError(t *testing.T) {
|
||||
const (
|
||||
attempt = 2
|
||||
previousAttempt = 1
|
||||
containsFormatExpected = "- error from a previous attempt: %s"
|
||||
)
|
||||
var (
|
||||
wrappedCtxDeadlineExceededErr = &url.Error{
|
||||
Op: "GET",
|
||||
URL: "http://foo.bar",
|
||||
Err: context.DeadlineExceeded,
|
||||
}
|
||||
wrappedCtxCanceledErr = &url.Error{
|
||||
Op: "GET",
|
||||
URL: "http://foo.bar",
|
||||
Err: context.Canceled,
|
||||
}
|
||||
urlEOFErr = &url.Error{
|
||||
Op: "GET",
|
||||
URL: "http://foo.bar",
|
||||
Err: io.EOF,
|
||||
}
|
||||
)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
previousErr error
|
||||
currentErr error
|
||||
expectedErr error
|
||||
wrapped bool
|
||||
contains string
|
||||
}{
|
||||
{
|
||||
name: "current error is nil, previous error is nil",
|
||||
},
|
||||
{
|
||||
name: "current error is nil",
|
||||
previousErr: errors.New("error from a previous attempt"),
|
||||
},
|
||||
{
|
||||
name: "previous error is nil",
|
||||
currentErr: urlEOFErr,
|
||||
expectedErr: urlEOFErr,
|
||||
wrapped: false,
|
||||
},
|
||||
{
|
||||
name: "both current and previous errors represent the same error",
|
||||
currentErr: urlEOFErr,
|
||||
previousErr: &url.Error{Op: "GET", URL: "http://foo.bar", Err: io.EOF},
|
||||
expectedErr: urlEOFErr,
|
||||
},
|
||||
{
|
||||
name: "current and previous errors are not same",
|
||||
currentErr: urlEOFErr,
|
||||
previousErr: errors.New("unknown error"),
|
||||
expectedErr: urlEOFErr,
|
||||
wrapped: true,
|
||||
contains: fmt.Sprintf(containsFormatExpected, "unknown error"),
|
||||
},
|
||||
{
|
||||
name: "current error is context.Canceled",
|
||||
currentErr: context.Canceled,
|
||||
previousErr: io.EOF,
|
||||
expectedErr: context.Canceled,
|
||||
wrapped: true,
|
||||
contains: fmt.Sprintf(containsFormatExpected, io.EOF.Error()),
|
||||
},
|
||||
{
|
||||
name: "current error is context.DeadlineExceeded",
|
||||
currentErr: context.DeadlineExceeded,
|
||||
previousErr: io.EOF,
|
||||
expectedErr: context.DeadlineExceeded,
|
||||
wrapped: true,
|
||||
contains: fmt.Sprintf(containsFormatExpected, io.EOF.Error()),
|
||||
},
|
||||
{
|
||||
name: "current error is a wrapped context.DeadlineExceeded",
|
||||
currentErr: wrappedCtxDeadlineExceededErr,
|
||||
previousErr: io.EOF,
|
||||
expectedErr: wrappedCtxDeadlineExceededErr,
|
||||
wrapped: true,
|
||||
contains: fmt.Sprintf(containsFormatExpected, io.EOF.Error()),
|
||||
},
|
||||
{
|
||||
name: "current error is a wrapped context.Canceled",
|
||||
currentErr: wrappedCtxCanceledErr,
|
||||
previousErr: io.EOF,
|
||||
expectedErr: wrappedCtxCanceledErr,
|
||||
wrapped: true,
|
||||
contains: fmt.Sprintf(containsFormatExpected, io.EOF.Error()),
|
||||
},
|
||||
{
|
||||
name: "previous error should be unwrapped if it is url.Error",
|
||||
currentErr: urlEOFErr,
|
||||
previousErr: &url.Error{Err: io.ErrUnexpectedEOF},
|
||||
expectedErr: urlEOFErr,
|
||||
wrapped: true,
|
||||
contains: fmt.Sprintf(containsFormatExpected, io.ErrUnexpectedEOF.Error()),
|
||||
},
|
||||
{
|
||||
name: "previous error should not be unwrapped if it is not url.Error",
|
||||
currentErr: urlEOFErr,
|
||||
previousErr: fmt.Errorf("should be included in error message - %w", io.EOF),
|
||||
expectedErr: urlEOFErr,
|
||||
wrapped: true,
|
||||
contains: fmt.Sprintf(containsFormatExpected, "should be included in error message - EOF"),
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
retry := &withRetry{
|
||||
previousErr: test.previousErr,
|
||||
}
|
||||
|
||||
err := retry.WrapPreviousError(test.currentErr)
|
||||
switch {
|
||||
case test.expectedErr == nil:
|
||||
if err != nil {
|
||||
t.Errorf("Expected a nil error, but got: %v", err)
|
||||
return
|
||||
}
|
||||
case test.expectedErr != nil:
|
||||
// make sure the message from the returned error contains
|
||||
// message from the "previous" error from retries.
|
||||
if !strings.Contains(err.Error(), test.contains) {
|
||||
t.Errorf("Expected error message to contain %q, but got: %v", test.contains, err)
|
||||
}
|
||||
|
||||
currentErrGot := err
|
||||
if test.wrapped {
|
||||
currentErrGot = errors.Unwrap(err)
|
||||
}
|
||||
if test.expectedErr != currentErrGot {
|
||||
t.Errorf("Expected current error %v, but got: %v", test.expectedErr, currentErrGot)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("Before should track previous error", func(t *testing.T) {
|
||||
retry := &withRetry{
|
||||
currentErr: io.EOF,
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel()
|
||||
|
||||
// we pass zero Request object since we expect 'Before'
|
||||
// to check the context for error at the very beginning.
|
||||
err := retry.Before(ctx, &Request{})
|
||||
if err != context.Canceled {
|
||||
t.Errorf("Expected error: %v, but got: %v", context.Canceled, err)
|
||||
}
|
||||
if retry.currentErr != context.Canceled {
|
||||
t.Errorf("Expected current error: %v, but got: %v", context.Canceled, retry.currentErr)
|
||||
}
|
||||
if retry.previousErr != io.EOF {
|
||||
t.Errorf("Expected previous error: %v, but got: %v", io.EOF, retry.previousErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
Reference in New Issue
Block a user