diff --git a/pkg/kubectl/resource/builder.go b/pkg/kubectl/resource/builder.go index 0a40e7b21ba..8853be8a9f5 100644 --- a/pkg/kubectl/resource/builder.go +++ b/pkg/kubectl/resource/builder.go @@ -36,6 +36,8 @@ import ( var FileExtensions = []string{".json", ".yaml", ".yml"} var InputExtensions = append(FileExtensions, "stdin") +const defaultHttpGetAttempts int = 3 + // Builder provides convenience functions for taking arguments and parameters // from the command line and converting them to a list of resources to iterate // over using the Visitor interface. @@ -109,7 +111,7 @@ func (b *Builder) FilenameParam(enforceNamespace, recursive bool, paths ...strin b.errs = append(b.errs, fmt.Errorf("the URL passed to filename %q is not valid: %v", s, err)) continue } - b.URL(url) + b.URL(defaultHttpGetAttempts, url) default: b.Path(recursive, s) } @@ -123,11 +125,12 @@ func (b *Builder) FilenameParam(enforceNamespace, recursive bool, paths ...strin } // URL accepts a number of URLs directly. -func (b *Builder) URL(urls ...*url.URL) *Builder { +func (b *Builder) URL(httpAttemptCount int, urls ...*url.URL) *Builder { for _, u := range urls { b.paths = append(b.paths, &URLVisitor{ - URL: u, - StreamVisitor: NewStreamVisitor(nil, b.mapper, u.String(), b.schema), + URL: u, + StreamVisitor: NewStreamVisitor(nil, b.mapper, u.String(), b.schema), + HttpAttemptCount: httpAttemptCount, }) } return b diff --git a/pkg/kubectl/resource/visitor.go b/pkg/kubectl/resource/visitor.go index e29513dbd0d..288bfa85aca 100644 --- a/pkg/kubectl/resource/visitor.go +++ b/pkg/kubectl/resource/visitor.go @@ -222,10 +222,11 @@ func ValidateSchema(data []byte, schema validation.Schema) error { type URLVisitor struct { URL *url.URL *StreamVisitor + HttpAttemptCount int } func (v *URLVisitor) Visit(fn VisitorFunc) error { - body, err := readHttpWithRetries(httpgetImpl, time.Second, v.URL.String()) + body, err := readHttpWithRetries(httpgetImpl, time.Second, v.URL.String(), v.HttpAttemptCount) if err != nil { return err } @@ -234,11 +235,14 @@ func (v *URLVisitor) Visit(fn VisitorFunc) error { return v.StreamVisitor.Visit(fn) } -// readHttpWithRetries tries to http.Get the v.URL 3 times before giving up. -func readHttpWithRetries(get httpget, duration time.Duration, u string) (io.ReadCloser, error) { +// readHttpWithRetries tries to http.Get the v.URL retries times before giving up. +func readHttpWithRetries(get httpget, duration time.Duration, u string, attempts int) (io.ReadCloser, error) { var err error var body io.ReadCloser - for i := 0; i < 3; i++ { + if attempts <= 0 { + return nil, fmt.Errorf("http attempts must be greater than 0, was %d", attempts) + } + for i := 0; i < attempts; i++ { var statusCode int var status string if i > 0 { diff --git a/pkg/kubectl/resource/visitor_test.go b/pkg/kubectl/resource/visitor_test.go index c69231e9ff3..e781c90c249 100644 --- a/pkg/kubectl/resource/visitor_test.go +++ b/pkg/kubectl/resource/visitor_test.go @@ -37,7 +37,7 @@ func TestVisitorHttpGet(t *testing.T) { return 0, "", nil, expectedErr } return 0, "", nil, fmt.Errorf("Unexpected error") - }, 0, "hello") + }, 0, "hello", 3) assert.Equal(t, expectedErr, actualErr) assert.Nil(t, actualBytes) assert.Equal(t, 3, i) @@ -48,7 +48,7 @@ func TestVisitorHttpGet(t *testing.T) { assert.Equal(t, "hello", url) i++ return 501, "Status", nil, nil - }, 0, "hello") + }, 0, "hello", 3) assert.Error(t, actualErr) assert.Nil(t, actualBytes) assert.Equal(t, 3, i) @@ -59,14 +59,35 @@ func TestVisitorHttpGet(t *testing.T) { assert.Equal(t, "hello", url) i++ return 300, "Status", nil, nil - }, 0, "hello") + }, 0, "hello", 3) assert.Error(t, actualErr) assert.Nil(t, actualBytes) assert.Equal(t, 1, i) - // Test Success + // Test attempt count is respected + i = 0 + actualBytes, actualErr = readHttpWithRetries(func(url string) (int, string, io.ReadCloser, error) { + assert.Equal(t, "hello", url) + i++ + return 501, "Status", nil, nil + }, 0, "hello", 1) + assert.Error(t, actualErr) + assert.Nil(t, actualBytes) + assert.Equal(t, 1, i) + + // Test attempts less than 1 results in an error i = 0 b := bytes.Buffer{} + actualBytes, actualErr = readHttpWithRetries(func(url string) (int, string, io.ReadCloser, error) { + return 200, "Status", ioutil.NopCloser(&b), nil + }, 0, "hello", 0) + assert.Error(t, actualErr) + assert.Nil(t, actualBytes) + assert.Equal(t, 0, i) + + // Test Success + i = 0 + b = bytes.Buffer{} actualBytes, actualErr = readHttpWithRetries(func(url string) (int, string, io.ReadCloser, error) { assert.Equal(t, "hello", url) i++ @@ -74,7 +95,7 @@ func TestVisitorHttpGet(t *testing.T) { return 200, "Status", ioutil.NopCloser(&b), nil } return 501, "Status", nil, nil - }, 0, "hello") + }, 0, "hello", 3) assert.Nil(t, actualErr) assert.NotNil(t, actualBytes) assert.Equal(t, 2, i)