mirror of
https://github.com/kubernetes/client-go.git
synced 2026-05-16 04:12:13 +00:00
Compare commits
1 Commits
release-1.
...
v0.22.14
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
46058ccabe |
8
go.mod
8
go.mod
@@ -30,8 +30,8 @@ require (
|
||||
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d
|
||||
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac
|
||||
google.golang.org/protobuf v1.26.0
|
||||
k8s.io/api v0.0.0-20220222193716-39d32415443c
|
||||
k8s.io/apimachinery v0.0.0-20220919093433-dcdd25a23be9
|
||||
k8s.io/api v0.22.14
|
||||
k8s.io/apimachinery v0.22.14
|
||||
k8s.io/klog/v2 v2.9.0
|
||||
k8s.io/utils v0.0.0-20211116205334-6203023598ed
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.1
|
||||
@@ -39,6 +39,6 @@ require (
|
||||
)
|
||||
|
||||
replace (
|
||||
k8s.io/api => k8s.io/api v0.0.0-20220222193716-39d32415443c
|
||||
k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20220919093433-dcdd25a23be9
|
||||
k8s.io/api => k8s.io/api v0.22.14
|
||||
k8s.io/apimachinery => k8s.io/apimachinery v0.22.14
|
||||
)
|
||||
|
||||
8
go.sum
8
go.sum
@@ -444,10 +444,10 @@ honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWh
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
k8s.io/api v0.0.0-20220222193716-39d32415443c h1:7uKzkkSldmiACEmFGRb0+RdhsZShf8z4pC+523YGlRE=
|
||||
k8s.io/api v0.0.0-20220222193716-39d32415443c/go.mod h1:VZ3awwdNsOtdAUAYPvruiFtrH/OD4gT5qmZHaQmXgxY=
|
||||
k8s.io/apimachinery v0.0.0-20220919093433-dcdd25a23be9 h1:Ys+9Q+7lj+x4whF0Ckupf3oj0SA9pmK8K2vZsZKVo/4=
|
||||
k8s.io/apimachinery v0.0.0-20220919093433-dcdd25a23be9/go.mod h1:ZvVLP5iLhwVFg2Yx9Gh5W0um0DUauExbRhe+2Z8I1EU=
|
||||
k8s.io/api v0.22.14 h1:jHwGFMTXqg9MiZWOkzc6BMOzjaoxfzt9D5iw/qOGF7Q=
|
||||
k8s.io/api v0.22.14/go.mod h1:NzWR/OvQIjDxxqHK4RLDEZiud7ED8tin0npb7JfdYHY=
|
||||
k8s.io/apimachinery v0.22.14 h1:PvTeDzIxZu7fwbq+mem2pvVfZRhoiY6X3QZyS2z17a0=
|
||||
k8s.io/apimachinery v0.22.14/go.mod h1:ZvVLP5iLhwVFg2Yx9Gh5W0um0DUauExbRhe+2Z8I1EU=
|
||||
k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
|
||||
k8s.io/klog/v2 v2.9.0 h1:D7HV+n1V57XeZ0m6tdRkfknthUaM06VFbWldOFh8kzM=
|
||||
|
||||
@@ -82,12 +82,6 @@ func (r *RequestConstructionError) Error() string {
|
||||
|
||||
var noBackoff = &NoBackoff{}
|
||||
|
||||
type requestRetryFunc func(maxRetries int) WithRetry
|
||||
|
||||
func defaultRequestRetryFn(maxRetries int) WithRetry {
|
||||
return &withRetry{maxRetries: maxRetries}
|
||||
}
|
||||
|
||||
// Request allows for building up a request to a server in a chained fashion.
|
||||
// Any errors are stored until the end of your call, so you only have to
|
||||
// check once.
|
||||
@@ -99,7 +93,6 @@ type Request struct {
|
||||
rateLimiter flowcontrol.RateLimiter
|
||||
backoff BackoffManager
|
||||
timeout time.Duration
|
||||
maxRetries int
|
||||
|
||||
// generic components accessible via method setters
|
||||
verb string
|
||||
@@ -116,10 +109,9 @@ type Request struct {
|
||||
subresource string
|
||||
|
||||
// output
|
||||
err error
|
||||
body io.Reader
|
||||
|
||||
retryFn requestRetryFunc
|
||||
err error
|
||||
body io.Reader
|
||||
retry WithRetry
|
||||
}
|
||||
|
||||
// NewRequest creates a new request helper object for accessing runtime.Objects on a server.
|
||||
@@ -150,8 +142,7 @@ func NewRequest(c *RESTClient) *Request {
|
||||
backoff: backoff,
|
||||
timeout: timeout,
|
||||
pathPrefix: pathPrefix,
|
||||
maxRetries: 10,
|
||||
retryFn: defaultRequestRetryFn,
|
||||
retry: &withRetry{maxRetries: 10},
|
||||
warningHandler: c.warningHandler,
|
||||
}
|
||||
|
||||
@@ -417,10 +408,7 @@ func (r *Request) Timeout(d time.Duration) *Request {
|
||||
// function is specifically called with a different value.
|
||||
// A zero maxRetries prevent it from doing retires and return an error immediately.
|
||||
func (r *Request) MaxRetries(maxRetries int) *Request {
|
||||
if maxRetries < 0 {
|
||||
maxRetries = 0
|
||||
}
|
||||
r.maxRetries = maxRetries
|
||||
r.retry.SetMaxRetries(maxRetries)
|
||||
return r
|
||||
}
|
||||
|
||||
@@ -700,10 +688,8 @@ func (r *Request) Watch(ctx context.Context) (watch.Interface, error) {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var retryAfter *RetryAfter
|
||||
url := r.URL().String()
|
||||
withRetry := r.retryFn(r.maxRetries)
|
||||
for {
|
||||
req, err := r.newHTTPRequest(ctx)
|
||||
if err != nil {
|
||||
@@ -738,9 +724,9 @@ func (r *Request) Watch(ctx context.Context) (watch.Interface, error) {
|
||||
defer readAndCloseResponseBody(resp)
|
||||
|
||||
var retry bool
|
||||
retryAfter, retry = withRetry.NextRetry(req, resp, err, isErrRetryableFunc)
|
||||
retryAfter, retry = r.retry.NextRetry(req, resp, err, isErrRetryableFunc)
|
||||
if retry {
|
||||
err := withRetry.BeforeNextRetry(ctx, r.backoff, retryAfter, url, r.body)
|
||||
err := r.retry.BeforeNextRetry(ctx, r.backoff, retryAfter, url, r.body)
|
||||
if err == nil {
|
||||
return false, nil
|
||||
}
|
||||
@@ -831,7 +817,6 @@ func (r *Request) Stream(ctx context.Context) (io.ReadCloser, error) {
|
||||
}
|
||||
|
||||
var retryAfter *RetryAfter
|
||||
withRetry := r.retryFn(r.maxRetries)
|
||||
url := r.URL().String()
|
||||
for {
|
||||
req, err := r.newHTTPRequest(ctx)
|
||||
@@ -877,9 +862,9 @@ func (r *Request) Stream(ctx context.Context) (io.ReadCloser, error) {
|
||||
defer resp.Body.Close()
|
||||
|
||||
var retry bool
|
||||
retryAfter, retry = withRetry.NextRetry(req, resp, err, neverRetryError)
|
||||
retryAfter, retry = r.retry.NextRetry(req, resp, err, neverRetryError)
|
||||
if retry {
|
||||
err := withRetry.BeforeNextRetry(ctx, r.backoff, retryAfter, url, r.body)
|
||||
err := r.retry.BeforeNextRetry(ctx, r.backoff, retryAfter, url, r.body)
|
||||
if err == nil {
|
||||
return false, nil
|
||||
}
|
||||
@@ -976,7 +961,6 @@ func (r *Request) request(ctx context.Context, fn func(*http.Request, *http.Resp
|
||||
|
||||
// Right now we make about ten retry attempts if we get a Retry-After response.
|
||||
var retryAfter *RetryAfter
|
||||
withRetry := r.retryFn(r.maxRetries)
|
||||
for {
|
||||
req, err := r.newHTTPRequest(ctx)
|
||||
if err != nil {
|
||||
@@ -1013,7 +997,7 @@ func (r *Request) request(ctx context.Context, fn func(*http.Request, *http.Resp
|
||||
}
|
||||
|
||||
var retry bool
|
||||
retryAfter, retry = withRetry.NextRetry(req, resp, err, func(req *http.Request, err error) bool {
|
||||
retryAfter, retry = r.retry.NextRetry(req, resp, err, func(req *http.Request, err error) bool {
|
||||
// "Connection reset by peer" or "apiserver is shutting down" are usually a transient errors.
|
||||
// Thus in case of "GET" operations, we simply retry it.
|
||||
// We are not automatically retrying "write" operations, as they are not idempotent.
|
||||
@@ -1027,7 +1011,7 @@ func (r *Request) request(ctx context.Context, fn func(*http.Request, *http.Resp
|
||||
return false
|
||||
})
|
||||
if retry {
|
||||
err := withRetry.BeforeNextRetry(ctx, r.backoff, retryAfter, req.URL.String(), r.body)
|
||||
err := r.retry.BeforeNextRetry(ctx, r.backoff, retryAfter, req.URL.String(), r.body)
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -32,7 +32,6 @@ import (
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -1194,8 +1193,7 @@ func TestRequestWatch(t *testing.T) {
|
||||
c.Client = client
|
||||
}
|
||||
testCase.Request.backoff = &noSleepBackOff{}
|
||||
testCase.Request.maxRetries = testCase.maxRetries
|
||||
testCase.Request.retryFn = defaultRequestRetryFn
|
||||
testCase.Request.retry = &withRetry{maxRetries: testCase.maxRetries}
|
||||
|
||||
watch, err := testCase.Request.Watch(context.Background())
|
||||
|
||||
@@ -1408,8 +1406,7 @@ func TestRequestStream(t *testing.T) {
|
||||
c.Client = client
|
||||
}
|
||||
testCase.Request.backoff = &noSleepBackOff{}
|
||||
testCase.Request.maxRetries = testCase.maxRetries
|
||||
testCase.Request.retryFn = defaultRequestRetryFn
|
||||
testCase.Request.retry = &withRetry{maxRetries: testCase.maxRetries}
|
||||
|
||||
body, err := testCase.Request.Stream(context.Background())
|
||||
|
||||
@@ -1498,7 +1495,7 @@ func TestRequestDo(t *testing.T) {
|
||||
}
|
||||
for i, testCase := range testCases {
|
||||
testCase.Request.backoff = &NoBackoff{}
|
||||
testCase.Request.retryFn = defaultRequestRetryFn
|
||||
testCase.Request.retry = &withRetry{}
|
||||
body, err := testCase.Request.Do(context.Background()).Raw()
|
||||
hasErr := err != nil
|
||||
if hasErr != testCase.Err {
|
||||
@@ -1661,9 +1658,8 @@ func TestConnectionResetByPeerIsRetried(t *testing.T) {
|
||||
return nil, &net.OpError{Err: syscall.ECONNRESET}
|
||||
}),
|
||||
},
|
||||
backoff: backoff,
|
||||
maxRetries: 10,
|
||||
retryFn: defaultRequestRetryFn,
|
||||
backoff: backoff,
|
||||
retry: &withRetry{maxRetries: 10},
|
||||
}
|
||||
// We expect two retries of "connection reset by peer" and the success.
|
||||
_, err := req.Do(context.Background()).Raw()
|
||||
@@ -2734,9 +2730,8 @@ func TestRequestWithRetry(t *testing.T) {
|
||||
c: &RESTClient{
|
||||
Client: client,
|
||||
},
|
||||
backoff: &noSleepBackOff{},
|
||||
maxRetries: 1,
|
||||
retryFn: defaultRequestRetryFn,
|
||||
backoff: &noSleepBackOff{},
|
||||
retry: &withRetry{maxRetries: 1},
|
||||
}
|
||||
|
||||
var transformFuncInvoked int
|
||||
@@ -2926,9 +2921,8 @@ func testRequestWithRetry(t *testing.T, key string, doFunc func(ctx context.Cont
|
||||
content: defaultContentConfig(),
|
||||
Client: client,
|
||||
},
|
||||
backoff: &noSleepBackOff{},
|
||||
maxRetries: test.maxRetries,
|
||||
retryFn: defaultRequestRetryFn,
|
||||
backoff: &noSleepBackOff{},
|
||||
retry: &withRetry{maxRetries: test.maxRetries},
|
||||
}
|
||||
|
||||
doFunc(context.Background(), req)
|
||||
@@ -2950,50 +2944,3 @@ func testRequestWithRetry(t *testing.T, key string, doFunc func(ctx context.Cont
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRequestConcurrencyWithRetry(t *testing.T) {
|
||||
var attempts int32
|
||||
client := clientForFunc(func(req *http.Request) (*http.Response, error) {
|
||||
defer func() {
|
||||
atomic.AddInt32(&attempts, 1)
|
||||
}()
|
||||
|
||||
// always send a retry-after response
|
||||
return &http.Response{
|
||||
StatusCode: http.StatusInternalServerError,
|
||||
Header: http.Header{"Retry-After": []string{"1"}},
|
||||
}, nil
|
||||
})
|
||||
|
||||
req := &Request{
|
||||
verb: "POST",
|
||||
c: &RESTClient{
|
||||
content: defaultContentConfig(),
|
||||
Client: client,
|
||||
},
|
||||
backoff: &noSleepBackOff{},
|
||||
maxRetries: 9, // 10 attempts in total, including the first
|
||||
retryFn: defaultRequestRetryFn,
|
||||
}
|
||||
|
||||
concurrency := 20
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(concurrency)
|
||||
startCh := make(chan struct{})
|
||||
for i := 0; i < concurrency; i++ {
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
<-startCh
|
||||
req.Do(context.Background())
|
||||
}()
|
||||
}
|
||||
|
||||
close(startCh)
|
||||
wg.Wait()
|
||||
|
||||
// we expect (concurrency*req.maxRetries+1) attempts to be recorded
|
||||
expected := concurrency * (req.maxRetries + 1)
|
||||
if atomic.LoadInt32(&attempts) != int32(expected) {
|
||||
t.Errorf("Expected attempts: %d, but got: %d", expected, attempts)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user