Compare commits

...

15 Commits

Author SHA1 Message Date
Kubernetes Publisher
4cb5375022 Update dependencies to v0.25.8 tag 2023-03-17 20:53:04 +00:00
Kubernetes Publisher
d51156025a Merge pull request #115788 from liggitt/net-0.7.0-1.25
[1.25] Update golang.org/x/net to v0.7.0

Kubernetes-commit: 8972d2b73a497eb0d15615b24fa7ca82d5b38b86
2023-02-15 11:21:15 +00:00
Jordan Liggitt
c3942e6cb3 Update golang.org/x/net to v0.7.0
Kubernetes-commit: fc043497d870b68ba35701a42c06071340b68bb2
2023-02-14 23:21:55 -05:00
Kubernetes Publisher
705901c8a7 Merge pull request #115567 from enj/automated-cherry-pick-of-#115315-upstream-release-1.25
Automated cherry pick of #115315: kubelet/client: collapse transport wiring onto standard

Kubernetes-commit: dd286416074ab1796ce100e27a7eb008d4624936
2023-02-10 22:42:41 +00:00
Kubernetes Publisher
e42bae1174 Merge pull request #115641 from nckturner/pin-golang.org/x/net-to-v0.4.0-in-1.25
Pin golang.org/x/net to v0.4.0 in 1.25

Kubernetes-commit: 641272fbcf7a75a06fb55909b5514523271737ef
2023-02-10 15:27:41 +00:00
Nick Turner
27f722275a Pin golang.org/x/net to v0.4.0 in 1.25
Kubernetes-commit: cd5e3b8a1f65c464168093ef516726d3b603d011
2023-02-08 17:57:20 -08:00
Monis Khan
212fb452f3 kubelet/client: collapse transport wiring onto standard approach
Signed-off-by: Monis Khan <mok@microsoft.com>

Kubernetes-commit: 2fe98e9c00d6a084b482fa337acf5fe4f2fdedeb
2023-02-05 20:51:54 -05:00
Kubernetes Publisher
ab9a2e8199 Merge pull request #113990 from liggitt/automated-cherry-pick-of-#113933-upstream-release-1.25
Automated cherry pick of #113933: Limit request retrying to []byte request bodies

Kubernetes-commit: e6d249f777d83c0c33cbda56fc1e9df8142b29a7
2022-12-13 00:03:09 +00:00
Kubernetes Publisher
b2883ba878 Merge pull request #114320 from liggitt/net-1.25
Update golang.org/x/net 1e63c2f

Kubernetes-commit: 6711e16058e842dae009f0263b8b313677cebbfb
2022-12-07 02:20:47 +00:00
Jordan Liggitt
fcb591bc0a Update golang.org/x/net 1e63c2f
Includes fix for CVE-2022-41717

Kubernetes-commit: 73c3dcb6558eeeea9e77940dde525a81d7e0bc1d
2022-12-06 17:35:53 -05:00
Kubernetes Publisher
b63afdf260 Merge pull request #113425 from aimuz/automated-cherry-pick-of-#112693-upstream-release-1.25
Automated cherry pick of #112693: Fixed (CVE-2022-27664) Bump golang.org/x/net to

Kubernetes-commit: c8d0fbe009b48f96e36bba9c36e20dba79bfdd05
2022-11-16 01:57:44 +00:00
Jordan Liggitt
9c1395ec93 Limit request retrying to []byte request bodies
Kubernetes-commit: 1e5e5330df628ca5fb1306b8fcaf3a1231ae536c
2022-11-15 17:47:35 -05:00
aimuz
5aa9be71ec Fixed (CVE-2022-27664) Bump golang.org/x/net to v0.1.1-0.20221027164007-c63010009c80
Fixed https://pkg.go.dev/vuln/GO-2022-0969

Signed-off-by: aimuz <mr.imuz@gmail.com>

Kubernetes-commit: 9ab1a67d6769303b9c446c25f95d39a0a5218f32
2022-10-28 10:07:56 +08:00
Kubernetes Publisher
166ab05d11 Merge pull request #112808 from cheftako/automated-cherry-pick-of-#112689-upstream-release-1.25
Bump konnectivity-client to v0.0.33

Kubernetes-commit: e1d0471ec1272f959253ffa2ebee1d9caacd7de5
2022-10-07 07:53:11 +00:00
Walter Fender
4b5a63801d Updated vendor to the new preferred versions.
Added missing files.

Kubernetes-commit: 1bc43a0dd51fa9b5450d5f66b594892874501479
2022-09-30 19:01:34 -07:00
7 changed files with 111 additions and 239 deletions

18
go.mod
View File

@@ -20,14 +20,14 @@ require (
github.com/imdario/mergo v0.3.6
github.com/peterbourgon/diskv v2.0.1+incompatible
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.7.0
golang.org/x/net v0.0.0-20220722155237-a158d28d115b
github.com/stretchr/testify v1.8.0
golang.org/x/net v0.7.0
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
golang.org/x/term v0.5.0
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8
google.golang.org/protobuf v1.28.0
k8s.io/api v0.0.0-20220909104543-fce3016c0f35
k8s.io/apimachinery v0.0.0-20220909103826-10b456c523c7
k8s.io/api v0.25.8
k8s.io/apimachinery v0.25.8
k8s.io/klog/v2 v2.70.1
k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1
k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed
@@ -60,8 +60,8 @@ require (
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd // indirect
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/sys v0.5.0 // indirect
golang.org/x/text v0.7.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
@@ -70,6 +70,6 @@ require (
)
replace (
k8s.io/api => k8s.io/api v0.0.0-20220909104543-fce3016c0f35
k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20220909103826-10b456c523c7
k8s.io/api => k8s.io/api v0.25.8
k8s.io/apimachinery => k8s.io/apimachinery v0.25.8
)

30
go.sum
View File

@@ -255,12 +255,14 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@@ -355,8 +357,8 @@ golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLd
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -431,11 +433,11 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -444,8 +446,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -665,10 +667,10 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
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=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
k8s.io/api v0.0.0-20220909104543-fce3016c0f35 h1:u8CuY5/HNkUPwechzT9y48BarB4yAtn/meKrJn5j+38=
k8s.io/api v0.0.0-20220909104543-fce3016c0f35/go.mod h1:FeqpcvGi6C+D7XYd+eThTUHnFpNx4enpIdClTz7Ww5Q=
k8s.io/apimachinery v0.0.0-20220909103826-10b456c523c7 h1:kRQIdYUKC7FP4M4xmU03qFA+BVcX8BVFZDiyIZ2wCuI=
k8s.io/apimachinery v0.0.0-20220909103826-10b456c523c7/go.mod h1:hqqA1X0bsgsxI6dXsJ4HnNTBOmJNxyPp8dw3u2fSHwA=
k8s.io/api v0.25.8 h1:pcbnWkCcmjNhp6OEKqR+ojO0CJydpOOw7WiWedjLJAU=
k8s.io/api v0.25.8/go.mod h1:FaJqAtI13XOERtpLOQTkW3SiSf0lqsUohYqaxCyHI18=
k8s.io/apimachinery v0.25.8 h1:c4kI9xm0U5nid8sBpBvM+2VHlv4Af8KnbhZIodZF/54=
k8s.io/apimachinery v0.25.8/go.mod h1:ZTl0drTQaFi5gMM3snYI5tWV1XJmRH1gfnDx2QCLsxk=
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
k8s.io/klog/v2 v2.70.1 h1:7aaoSdahviPmR+XkS7FyxlkkXs6tHISSG03RxleQAVQ=
k8s.io/klog/v2 v2.70.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=

View File

@@ -34,6 +34,7 @@ import (
"time"
"golang.org/x/net/http2"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
@@ -116,8 +117,11 @@ type Request struct {
subresource string
// output
err error
body io.Reader
err error
// only one of body / bodyBytes may be set. requests using body are not retriable.
body io.Reader
bodyBytes []byte
retryFn requestRetryFunc
}
@@ -443,12 +447,15 @@ func (r *Request) Body(obj interface{}) *Request {
return r
}
glogBody("Request Body", data)
r.body = bytes.NewReader(data)
r.body = nil
r.bodyBytes = data
case []byte:
glogBody("Request Body", t)
r.body = bytes.NewReader(t)
r.body = nil
r.bodyBytes = t
case io.Reader:
r.body = t
r.bodyBytes = nil
case runtime.Object:
// callers may pass typed interface pointers, therefore we must check nil with reflection
if reflect.ValueOf(t).IsNil() {
@@ -465,7 +472,8 @@ func (r *Request) Body(obj interface{}) *Request {
return r
}
glogBody("Request Body", data)
r.body = bytes.NewReader(data)
r.body = nil
r.bodyBytes = data
r.SetHeader("Content-Type", r.c.content.ContentType)
default:
r.err = fmt.Errorf("unknown type used for body: %+v", obj)
@@ -825,9 +833,7 @@ func (r *Request) Stream(ctx context.Context) (io.ReadCloser, error) {
if err != nil {
return nil, err
}
if r.body != nil {
req.Body = ioutil.NopCloser(r.body)
}
resp, err := client.Do(req)
updateURLMetrics(ctx, r, resp, err)
retry.After(ctx, r, resp, err)
@@ -889,8 +895,20 @@ func (r *Request) requestPreflightCheck() error {
}
func (r *Request) newHTTPRequest(ctx context.Context) (*http.Request, error) {
var body io.Reader
switch {
case r.body != nil && r.bodyBytes != nil:
return nil, fmt.Errorf("cannot set both body and bodyBytes")
case r.body != nil:
body = r.body
case r.bodyBytes != nil:
// Create a new reader specifically for this request.
// Giving each request a dedicated reader allows retries to avoid races resetting the request body.
body = bytes.NewReader(r.bodyBytes)
}
url := r.URL().String()
req, err := http.NewRequest(r.verb, url, r.body)
req, err := http.NewRequest(r.verb, url, body)
if err != nil {
return nil, err
}

View File

@@ -1123,42 +1123,6 @@ func TestRequestWatch(t *testing.T) {
},
Empty: true,
},
{
name: "max retries 1, server returns a retry-after response, request body seek error",
Request: &Request{
body: &readSeeker{err: io.EOF},
c: &RESTClient{
base: &url.URL{},
},
},
maxRetries: 1,
attemptsExpected: 1,
serverReturns: []responseErr{
{response: retryAfterResponse(), err: nil},
},
Err: true,
ErrFn: func(err error) bool {
return !apierrors.IsInternalError(err) && strings.Contains(err.Error(), "failed to reset the request body while retrying a request: EOF")
},
},
{
name: "max retries 1, server returns a retryable error, request body seek error",
Request: &Request{
body: &readSeeker{err: io.EOF},
c: &RESTClient{
base: &url.URL{},
},
},
maxRetries: 1,
attemptsExpected: 1,
serverReturns: []responseErr{
{response: nil, err: io.EOF},
},
Err: true,
ErrFn: func(err error) bool {
return !apierrors.IsInternalError(err)
},
},
{
name: "max retries 2, server always returns a response with Retry-After header",
Request: &Request{
@@ -1320,7 +1284,7 @@ func TestRequestStream(t *testing.T) {
},
},
{
name: "max retries 1, server returns a retry-after response, request body seek error",
name: "max retries 1, server returns a retry-after response, non-bytes request, no retry",
Request: &Request{
body: &readSeeker{err: io.EOF},
c: &RESTClient{
@@ -1333,9 +1297,6 @@ func TestRequestStream(t *testing.T) {
{response: retryAfterResponse(), err: nil},
},
Err: true,
ErrFn: func(err error) bool {
return !apierrors.IsInternalError(err) && strings.Contains(err.Error(), "failed to reset the request body while retrying a request: EOF")
},
},
{
name: "max retries 2, server always returns a response with Retry-After header",
@@ -2017,20 +1978,24 @@ func TestBody(t *testing.T) {
}
}
if r.body == nil {
req, err := r.newHTTPRequest(context.Background())
if err != nil {
t.Fatal(err)
}
if req.Body == nil {
if len(tt.expected) != 0 {
t.Errorf("%d: r.body = %q; want %q", i, r.body, tt.expected)
t.Errorf("%d: req.Body = %q; want %q", i, req.Body, tt.expected)
}
continue
}
buf := make([]byte, len(tt.expected))
if _, err := r.body.Read(buf); err != nil {
t.Errorf("%d: r.body.Read error: %v", i, err)
if _, err := req.Body.Read(buf); err != nil {
t.Errorf("%d: req.Body.Read error: %v", i, err)
continue
}
body := string(buf)
if body != tt.expected {
t.Errorf("%d: r.body = %q; want %q", i, body, tt.expected)
t.Errorf("%d: req.Body = %q; want %q", i, body, tt.expected)
}
}
}
@@ -2641,6 +2606,7 @@ func TestRequestWithRetry(t *testing.T) {
tests := []struct {
name string
body io.Reader
bodyBytes []byte
serverReturns responseErr
errExpected error
errContains string
@@ -2648,53 +2614,53 @@ func TestRequestWithRetry(t *testing.T) {
roundTripInvokedExpected int
}{
{
name: "server returns retry-after response, request body is not io.Seeker, retry goes ahead",
body: ioutil.NopCloser(bytes.NewReader([]byte{})),
name: "server returns retry-after response, no request body, retry goes ahead",
bodyBytes: nil,
serverReturns: responseErr{response: retryAfterResponse(), err: nil},
errExpected: nil,
transformFuncInvokedExpected: 1,
roundTripInvokedExpected: 2,
},
{
name: "server returns retry-after response, request body Seek returns error, retry aborted",
body: &readSeeker{err: io.EOF},
serverReturns: responseErr{response: retryAfterResponse(), err: nil},
errExpected: nil,
transformFuncInvokedExpected: 0,
roundTripInvokedExpected: 1,
},
{
name: "server returns retry-after response, request body Seek returns no error, retry goes ahead",
body: &readSeeker{err: nil},
name: "server returns retry-after response, bytes request body, retry goes ahead",
bodyBytes: []byte{},
serverReturns: responseErr{response: retryAfterResponse(), err: nil},
errExpected: nil,
transformFuncInvokedExpected: 1,
roundTripInvokedExpected: 2,
},
{
name: "server returns retryable err, request body is not io.Seek, retry goes ahead",
body: ioutil.NopCloser(bytes.NewReader([]byte{})),
serverReturns: responseErr{response: nil, err: io.ErrUnexpectedEOF},
errExpected: io.ErrUnexpectedEOF,
transformFuncInvokedExpected: 0,
roundTripInvokedExpected: 2,
},
{
name: "server returns retryable err, request body Seek returns error, retry aborted",
body: &readSeeker{err: io.EOF},
serverReturns: responseErr{response: nil, err: io.ErrUnexpectedEOF},
errContains: "failed to reset the request body while retrying a request: EOF",
transformFuncInvokedExpected: 0,
name: "server returns retry-after response, opaque request body, retry aborted",
body: &readSeeker{},
serverReturns: responseErr{response: retryAfterResponse(), err: nil},
errExpected: nil,
transformFuncInvokedExpected: 1,
roundTripInvokedExpected: 1,
},
{
name: "server returns retryable err, request body Seek returns no err, retry goes ahead",
body: &readSeeker{err: nil},
name: "server returns retryable err, no request body, retry goes ahead",
bodyBytes: nil,
serverReturns: responseErr{response: nil, err: io.ErrUnexpectedEOF},
errExpected: io.ErrUnexpectedEOF,
transformFuncInvokedExpected: 0,
roundTripInvokedExpected: 2,
},
{
name: "server returns retryable err, bytes request body, retry goes ahead",
bodyBytes: []byte{},
serverReturns: responseErr{response: nil, err: io.ErrUnexpectedEOF},
errExpected: io.ErrUnexpectedEOF,
transformFuncInvokedExpected: 0,
roundTripInvokedExpected: 2,
},
{
name: "server returns retryable err, opaque request body, retry aborted",
body: &readSeeker{},
serverReturns: responseErr{response: nil, err: io.ErrUnexpectedEOF},
errExpected: io.ErrUnexpectedEOF,
transformFuncInvokedExpected: 0,
roundTripInvokedExpected: 1,
},
}
for _, test := range tests {
@@ -2865,7 +2831,8 @@ func testRequestWithRetry(t *testing.T, key string, doFunc func(ctx context.Cont
tests := []struct {
name string
verb string
body func() io.Reader
body io.Reader
bodyBytes []byte
maxRetries int
serverReturns []responseErr
@@ -2875,7 +2842,7 @@ func testRequestWithRetry(t *testing.T, key string, doFunc func(ctx context.Cont
{
name: "server always returns retry-after response",
verb: "GET",
body: func() io.Reader { return bytes.NewReader([]byte{}) },
bodyBytes: []byte{},
maxRetries: 2,
serverReturns: []responseErr{
{response: retryAfterResponse(), err: nil},
@@ -2903,7 +2870,7 @@ func testRequestWithRetry(t *testing.T, key string, doFunc func(ctx context.Cont
{
name: "server always returns retryable error",
verb: "GET",
body: func() io.Reader { return bytes.NewReader([]byte{}) },
bodyBytes: []byte{},
maxRetries: 2,
serverReturns: []responseErr{
{response: nil, err: io.EOF},
@@ -2932,7 +2899,7 @@ func testRequestWithRetry(t *testing.T, key string, doFunc func(ctx context.Cont
{
name: "server returns success on the final retry",
verb: "GET",
body: func() io.Reader { return bytes.NewReader([]byte{}) },
bodyBytes: []byte{},
maxRetries: 2,
serverReturns: []responseErr{
{response: retryAfterResponse(), err: nil},
@@ -2979,13 +2946,10 @@ func testRequestWithRetry(t *testing.T, key string, doFunc func(ctx context.Cont
return resp, test.serverReturns[attempts].err
})
reqCountGot := newCount()
reqRecorder := newReadTracker(reqCountGot)
reqRecorder.delegated = test.body()
req := &Request{
verb: test.verb,
body: reqRecorder,
verb: test.verb,
body: test.body,
bodyBytes: test.bodyBytes,
c: &RESTClient{
content: defaultContentConfig(),
Client: client,
@@ -3005,9 +2969,6 @@ func testRequestWithRetry(t *testing.T, key string, doFunc func(ctx context.Cont
t.Errorf("Expected retries: %d, but got: %d", expected.attempts, attempts)
}
if !reflect.DeepEqual(expected.reqCount.seeks, reqCountGot.seeks) {
t.Errorf("Expected request body to have seek invocation: %v, but got: %v", expected.reqCount.seeks, reqCountGot.seeks)
}
if expected.respCount.closes != respCountGot.getCloseCount() {
t.Errorf("Expected response body Close to be invoked %d times, but got: %d", expected.respCount.closes, respCountGot.getCloseCount())
}
@@ -3264,8 +3225,8 @@ func testRetryWithRateLimiterBackoffAndMetrics(t *testing.T, key string, doFunc
t.Fatalf("Wrong test setup - did not find expected for: %s", key)
}
req := &Request{
verb: "GET",
body: bytes.NewReader([]byte{}),
verb: "GET",
bodyBytes: []byte{},
c: &RESTClient{
base: base,
content: defaultContentConfig(),
@@ -3400,8 +3361,8 @@ func testWithRetryInvokeOrder(t *testing.T, key string, doFunc func(ctx context.
t.Fatalf("Wrong test setup - did not find expected for: %s", key)
}
req := &Request{
verb: "GET",
body: bytes.NewReader([]byte{}),
verb: "GET",
bodyBytes: []byte{},
c: &RESTClient{
base: base,
content: defaultContentConfig(),
@@ -3575,8 +3536,8 @@ func testWithWrapPreviousError(t *testing.T, doFunc func(ctx context.Context, r
t.Fatalf("Failed to create new HTTP request - %v", err)
}
req := &Request{
verb: "GET",
body: bytes.NewReader([]byte{}),
verb: "GET",
bodyBytes: []byte{},
c: &RESTClient{
base: base,
content: defaultContentConfig(),
@@ -3811,104 +3772,3 @@ func TestTransportConcurrency(t *testing.T) {
})
}
}
// TODO: see if we can consolidate the other trackers into one.
type requestBodyTracker struct {
io.ReadSeeker
f func(string)
}
func (t *requestBodyTracker) Read(p []byte) (int, error) {
t.f("Request.Body.Read")
return t.ReadSeeker.Read(p)
}
func (t *requestBodyTracker) Seek(offset int64, whence int) (int64, error) {
t.f("Request.Body.Seek")
return t.ReadSeeker.Seek(offset, whence)
}
type responseBodyTracker struct {
io.ReadCloser
f func(string)
}
func (t *responseBodyTracker) Read(p []byte) (int, error) {
t.f("Response.Body.Read")
return t.ReadCloser.Read(p)
}
func (t *responseBodyTracker) Close() error {
t.f("Response.Body.Close")
return t.ReadCloser.Close()
}
type recorder struct {
order []string
}
func (r *recorder) record(call string) {
r.order = append(r.order, call)
}
func TestRequestBodyResetOrder(t *testing.T) {
recorder := &recorder{}
respBodyTracker := &responseBodyTracker{
ReadCloser: nil, // the server will fill it
f: recorder.record,
}
var attempts int
client := clientForFunc(func(req *http.Request) (*http.Response, error) {
defer func() {
attempts++
}()
// read the request body.
ioutil.ReadAll(req.Body)
// first attempt, we send a retry-after
if attempts == 0 {
resp := retryAfterResponse()
respBodyTracker.ReadCloser = ioutil.NopCloser(bytes.NewReader([]byte{}))
resp.Body = respBodyTracker
return resp, nil
}
return &http.Response{StatusCode: http.StatusOK}, nil
})
reqBodyTracker := &requestBodyTracker{
ReadSeeker: bytes.NewReader([]byte{}), // empty body ensures one Read operation at most.
f: recorder.record,
}
req := &Request{
verb: "POST",
body: reqBodyTracker,
c: &RESTClient{
content: defaultContentConfig(),
Client: client,
},
backoff: &noSleepBackOff{},
maxRetries: 1,
retryFn: defaultRequestRetryFn,
}
req.Do(context.Background())
expected := []string{
// 1st attempt: the server handler reads the request body
"Request.Body.Read",
// the server sends a retry-after, client reads the
// response body, and closes it
"Response.Body.Read",
"Response.Body.Close",
// client retry logic seeks to the beginning of the request body
"Request.Body.Seek",
// 2nd attempt: the server reads the request body
"Request.Body.Read",
}
if !reflect.DeepEqual(expected, recorder.order) {
t.Errorf("Expected invocation request and response body operations for retry do not match: %s", cmp.Diff(expected, recorder.order))
}
}

View File

@@ -154,6 +154,11 @@ func (r *withRetry) IsNextRetry(ctx context.Context, restReq *Request, httpReq *
return false
}
if restReq.body != nil {
// we have an opaque reader, we can't safely reset it
return false
}
r.attempts++
r.retryAfter = &RetryAfter{Attempt: r.attempts}
if r.attempts > r.maxRetries {
@@ -210,18 +215,6 @@ func (r *withRetry) Before(ctx context.Context, request *Request) error {
return nil
}
// At this point we've made atleast one attempt, post which the response
// body should have been fully read and closed in order for it to be safe
// to reset the request body before we reconnect, in order for us to reuse
// the same TCP connection.
if seeker, ok := request.body.(io.Seeker); ok && request.body != nil {
if _, err := seeker.Seek(0, io.SeekStart); err != nil {
err = fmt.Errorf("failed to reset the request body while retrying a request: %v", err)
r.trackPreviousError(err)
return err
}
}
// if we are here, we have made attempt(s) at least once before.
if request.backoff != nil {
delay := request.backoff.CalculateBackoff(url)

View File

@@ -17,7 +17,6 @@ limitations under the License.
package rest
import (
"bytes"
"context"
"errors"
"fmt"
@@ -212,7 +211,7 @@ func TestIsNextRetry(t *testing.T) {
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
restReq := &Request{
body: bytes.NewReader([]byte{}),
bodyBytes: []byte{},
c: &RESTClient{
base: &url.URL{},
},

View File

@@ -109,7 +109,7 @@ func (c *tlsTransportCache) get(config *Config) (http.RoundTripper, error) {
// If we use are reloading files, we need to handle certificate rotation properly
// TODO(jackkleeman): We can also add rotation here when config.HasCertCallback() is true
if config.TLS.ReloadTLSFiles {
if config.TLS.ReloadTLSFiles && tlsConfig != nil && tlsConfig.GetClientCertificate != nil {
dynamicCertDialer := certRotatingDialer(tlsConfig.GetClientCertificate, dial)
tlsConfig.GetClientCertificate = dynamicCertDialer.GetClientCertificate
dial = dynamicCertDialer.connDialer.DialContext