Fix Retry-After in clients

This commit is contained in:
Wojciech Tyczynski 2016-06-06 09:48:44 +02:00
parent 6460b34128
commit 528713bcc2
3 changed files with 32 additions and 4 deletions

View File

@ -57,6 +57,9 @@ type RESTClient struct {
// serializers contain all serializers for undelying content type. // serializers contain all serializers for undelying content type.
serializers Serializers serializers Serializers
// creates BackoffManager that is passed to requests.
createBackoffMgr func() BackoffManager
// TODO extract this into a wrapper interface via the RESTClient interface in kubectl. // TODO extract this into a wrapper interface via the RESTClient interface in kubectl.
Throttle flowcontrol.RateLimiter Throttle flowcontrol.RateLimiter
@ -105,6 +108,7 @@ func NewRESTClient(baseURL *url.URL, versionedAPIPath string, config ContentConf
versionedAPIPath: versionedAPIPath, versionedAPIPath: versionedAPIPath,
contentConfig: config, contentConfig: config,
serializers: *serializers, serializers: *serializers,
createBackoffMgr: readExpBackoffConfig,
Throttle: throttle, Throttle: throttle,
Client: client, Client: client,
}, nil }, nil
@ -181,7 +185,7 @@ func createSerializers(config ContentConfig) (*Serializers, error) {
// list, ok := resp.(*api.PodList) // list, ok := resp.(*api.PodList)
// //
func (c *RESTClient) Verb(verb string) *Request { func (c *RESTClient) Verb(verb string) *Request {
backoff := readExpBackoffConfig() backoff := c.createBackoffMgr()
if c.Client == nil { if c.Client == nil {
return NewRequest(nil, verb, c.base, c.versionedAPIPath, c.contentConfig, c.serializers, backoff, c.Throttle) return NewRequest(nil, verb, c.base, c.versionedAPIPath, c.contentConfig, c.serializers, backoff, c.Throttle)

View File

@ -838,6 +838,21 @@ func TestBackoffLifecycle(t *testing.T) {
} }
} }
type testBackoffManager struct {
sleeps []time.Duration
}
func (b *testBackoffManager) UpdateBackoff(actualUrl *url.URL, err error, responseCode int) {
}
func (b *testBackoffManager) CalculateBackoff(actualUrl *url.URL) time.Duration {
return time.Duration(0)
}
func (b *testBackoffManager) Sleep(d time.Duration) {
b.sleeps = append(b.sleeps, d)
}
func TestCheckRetryClosesBody(t *testing.T) { func TestCheckRetryClosesBody(t *testing.T) {
count := 0 count := 0
ch := make(chan struct{}) ch := make(chan struct{})
@ -849,12 +864,16 @@ func TestCheckRetryClosesBody(t *testing.T) {
close(ch) close(ch)
return return
} }
w.Header().Set("Retry-After", "0") w.Header().Set("Retry-After", "1")
w.WriteHeader(apierrors.StatusTooManyRequests) http.Error(w, "Too many requests, please try again later.", apierrors.StatusTooManyRequests)
})) }))
defer testServer.Close() defer testServer.Close()
backoffMgr := &testBackoffManager{}
expectedSleeps := []time.Duration{0, time.Second, 0, time.Second, 0, time.Second, 0, time.Second, 0}
c := testRESTClient(t, testServer) c := testRESTClient(t, testServer)
c.createBackoffMgr = func() BackoffManager { return backoffMgr }
_, err := c.Verb("POST"). _, err := c.Verb("POST").
Prefix("foo", "bar"). Prefix("foo", "bar").
Suffix("baz"). Suffix("baz").
@ -868,6 +887,9 @@ func TestCheckRetryClosesBody(t *testing.T) {
if count != 5 { if count != 5 {
t.Errorf("unexpected retries: %d", count) t.Errorf("unexpected retries: %d", count)
} }
if !reflect.DeepEqual(backoffMgr.sleeps, expectedSleeps) {
t.Errorf("unexpected sleeps, expected: %v, got: %v", expectedSleeps, backoffMgr.sleeps)
}
} }
func TestCheckRetryHandles429And5xx(t *testing.T) { func TestCheckRetryHandles429And5xx(t *testing.T) {

View File

@ -52,11 +52,13 @@ type NoBackoff struct {
func (n *NoBackoff) UpdateBackoff(actualUrl *url.URL, err error, responseCode int) { func (n *NoBackoff) UpdateBackoff(actualUrl *url.URL, err error, responseCode int) {
// do nothing. // do nothing.
} }
func (n *NoBackoff) CalculateBackoff(actualUrl *url.URL) time.Duration { func (n *NoBackoff) CalculateBackoff(actualUrl *url.URL) time.Duration {
return 0 * time.Second return 0 * time.Second
} }
func (n *NoBackoff) Sleep(d time.Duration) { func (n *NoBackoff) Sleep(d time.Duration) {
return time.Sleep(d)
} }
// Disable makes the backoff trivial, i.e., sets it to zero. This might be used // Disable makes the backoff trivial, i.e., sets it to zero. This might be used