Merge pull request #71757 from mikedanese/fixcancel

implement request cancellation in token transport

Kubernetes-commit: bc1d8c6d61078269540f807248a455cba09e672c
This commit is contained in:
Kubernetes Publisher 2019-08-20 16:13:16 -07:00
commit f4e58ce609
7 changed files with 119 additions and 38 deletions

2
Godeps/Godeps.json generated
View File

@ -288,7 +288,7 @@
},
{
"ImportPath": "k8s.io/apimachinery",
"Rev": "21ddcbbef9e1"
"Rev": "ac02f8882ef6"
},
{
"ImportPath": "k8s.io/gengo",

4
go.mod
View File

@ -27,7 +27,7 @@ require (
golang.org/x/time v0.0.0-20161028155119-f51c12702a4d
google.golang.org/appengine v1.5.0 // indirect
k8s.io/api v0.0.0-20190820101039-d651a1528133
k8s.io/apimachinery v0.0.0-20190820100750-21ddcbbef9e1
k8s.io/apimachinery v0.0.0-20190820100751-ac02f8882ef6
k8s.io/klog v0.4.0
k8s.io/utils v0.0.0-20190801114015-581e00157fb1
sigs.k8s.io/yaml v1.1.0
@ -39,5 +39,5 @@ replace (
golang.org/x/sys => golang.org/x/sys v0.0.0-20190209173611-3b5209105503
golang.org/x/text => golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db
k8s.io/api => k8s.io/api v0.0.0-20190820101039-d651a1528133
k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20190820100750-21ddcbbef9e1
k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20190820100751-ac02f8882ef6
)

2
go.sum
View File

@ -145,7 +145,7 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
k8s.io/api v0.0.0-20190820101039-d651a1528133/go.mod h1:AlhL1I0Xqh5Tyz0HsxjEhy+iKci9l1Qy3UMDFW7iG3A=
k8s.io/apimachinery v0.0.0-20190820100750-21ddcbbef9e1/go.mod h1:EZoIMuAgG/4v58YL+bz0kqnivqupk28fKYxFCa5e6X8=
k8s.io/apimachinery v0.0.0-20190820100751-ac02f8882ef6/go.mod h1:EZoIMuAgG/4v58YL+bz0kqnivqupk28fKYxFCa5e6X8=
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=

View File

@ -80,10 +80,6 @@ func DebugWrappers(rt http.RoundTripper) http.RoundTripper {
return rt
}
type requestCanceler interface {
CancelRequest(*http.Request)
}
type authProxyRoundTripper struct {
username string
groups []string
@ -140,11 +136,7 @@ func SetAuthProxyHeaders(req *http.Request, username string, groups []string, ex
}
func (rt *authProxyRoundTripper) CancelRequest(req *http.Request) {
if canceler, ok := rt.rt.(requestCanceler); ok {
canceler.CancelRequest(req)
} else {
klog.Errorf("CancelRequest not implemented by %T", rt.rt)
}
tryCancelRequest(rt.WrappedRoundTripper(), req)
}
func (rt *authProxyRoundTripper) WrappedRoundTripper() http.RoundTripper { return rt.rt }
@ -168,11 +160,7 @@ func (rt *userAgentRoundTripper) RoundTrip(req *http.Request) (*http.Response, e
}
func (rt *userAgentRoundTripper) CancelRequest(req *http.Request) {
if canceler, ok := rt.rt.(requestCanceler); ok {
canceler.CancelRequest(req)
} else {
klog.Errorf("CancelRequest not implemented by %T", rt.rt)
}
tryCancelRequest(rt.WrappedRoundTripper(), req)
}
func (rt *userAgentRoundTripper) WrappedRoundTripper() http.RoundTripper { return rt.rt }
@ -199,11 +187,7 @@ func (rt *basicAuthRoundTripper) RoundTrip(req *http.Request) (*http.Response, e
}
func (rt *basicAuthRoundTripper) CancelRequest(req *http.Request) {
if canceler, ok := rt.rt.(requestCanceler); ok {
canceler.CancelRequest(req)
} else {
klog.Errorf("CancelRequest not implemented by %T", rt.rt)
}
tryCancelRequest(rt.WrappedRoundTripper(), req)
}
func (rt *basicAuthRoundTripper) WrappedRoundTripper() http.RoundTripper { return rt.rt }
@ -259,11 +243,7 @@ func (rt *impersonatingRoundTripper) RoundTrip(req *http.Request) (*http.Respons
}
func (rt *impersonatingRoundTripper) CancelRequest(req *http.Request) {
if canceler, ok := rt.delegate.(requestCanceler); ok {
canceler.CancelRequest(req)
} else {
klog.Errorf("CancelRequest not implemented by %T", rt.delegate)
}
tryCancelRequest(rt.WrappedRoundTripper(), req)
}
func (rt *impersonatingRoundTripper) WrappedRoundTripper() http.RoundTripper { return rt.delegate }
@ -318,11 +298,7 @@ func (rt *bearerAuthRoundTripper) RoundTrip(req *http.Request) (*http.Response,
}
func (rt *bearerAuthRoundTripper) CancelRequest(req *http.Request) {
if canceler, ok := rt.rt.(requestCanceler); ok {
canceler.CancelRequest(req)
} else {
klog.Errorf("CancelRequest not implemented by %T", rt.rt)
}
tryCancelRequest(rt.WrappedRoundTripper(), req)
}
func (rt *bearerAuthRoundTripper) WrappedRoundTripper() http.RoundTripper { return rt.rt }
@ -402,11 +378,7 @@ func newDebuggingRoundTripper(rt http.RoundTripper, levels ...debugLevel) *debug
}
func (rt *debuggingRoundTripper) CancelRequest(req *http.Request) {
if canceler, ok := rt.delegatedRoundTripper.(requestCanceler); ok {
canceler.CancelRequest(req)
} else {
klog.Errorf("CancelRequest not implemented by %T", rt.delegatedRoundTripper)
}
tryCancelRequest(rt.WrappedRoundTripper(), req)
}
var knownAuthTypes = map[string]bool{

View File

@ -25,6 +25,7 @@ import (
"time"
"golang.org/x/oauth2"
"k8s.io/klog"
)
@ -81,6 +82,14 @@ func (tst *tokenSourceTransport) RoundTrip(req *http.Request) (*http.Response, e
return tst.ort.RoundTrip(req)
}
func (tst *tokenSourceTransport) CancelRequest(req *http.Request) {
if req.Header.Get("Authorization") != "" {
tryCancelRequest(tst.base, req)
return
}
tryCancelRequest(tst.ort, req)
}
type fileTokenSource struct {
path string
period time.Duration

View File

@ -18,6 +18,7 @@ package transport
import (
"fmt"
"net/http"
"reflect"
"sync"
"testing"
@ -154,3 +155,85 @@ func TestCachingTokenSourceRace(t *testing.T) {
}
}
}
type uncancellableRT struct {
rt http.RoundTripper
}
func (urt *uncancellableRT) RoundTrip(req *http.Request) (*http.Response, error) {
return urt.rt.RoundTrip(req)
}
func TestCancellation(t *testing.T) {
tests := []struct {
name string
header http.Header
wrapTransport func(http.RoundTripper) http.RoundTripper
expectCancel bool
}{
{
name: "cancel req with bearer token skips oauth rt",
header: map[string][]string{"Authorization": {"Bearer TOKEN"}},
expectCancel: true,
},
{
name: "cancel req without bearer token hits both rts",
expectCancel: true,
},
{
name: "cancel req without bearer token hits both wrapped rts",
wrapTransport: func(rt http.RoundTripper) http.RoundTripper {
return NewUserAgentRoundTripper("testing testing", rt)
},
expectCancel: true,
},
{
name: "can't cancel request with rts that doesn't implent unwrap or cancel",
wrapTransport: func(rt http.RoundTripper) http.RoundTripper {
return &uncancellableRT{rt: rt}
},
expectCancel: false,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
baseRecorder := &recordCancelRoundTripper{}
var base http.RoundTripper = baseRecorder
if test.wrapTransport != nil {
base = test.wrapTransport(base)
}
rt := &tokenSourceTransport{
base: base,
ort: &oauth2.Transport{
Base: base,
},
}
rt.CancelRequest(&http.Request{
Header: test.header,
})
if baseRecorder.canceled != test.expectCancel {
t.Errorf("unexpected cancel: got=%v, want=%v", baseRecorder.canceled, test.expectCancel)
}
})
}
}
type recordCancelRoundTripper struct {
canceled bool
base http.RoundTripper
}
func (rt *recordCancelRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
return nil, nil
}
func (rt *recordCancelRoundTripper) CancelRequest(req *http.Request) {
rt.canceled = true
if rt.base != nil {
tryCancelRequest(rt.base, req)
}
}

View File

@ -23,6 +23,9 @@ import (
"fmt"
"io/ioutil"
"net/http"
utilnet "k8s.io/apimachinery/pkg/util/net"
"k8s.io/klog"
)
// New returns an http.RoundTripper that will provide the authentication
@ -225,3 +228,17 @@ func (b *contextCanceller) RoundTrip(req *http.Request) (*http.Response, error)
return b.rt.RoundTrip(req)
}
}
func tryCancelRequest(rt http.RoundTripper, req *http.Request) {
type canceler interface {
CancelRequest(*http.Request)
}
switch rt := rt.(type) {
case canceler:
rt.CancelRequest(req)
case utilnet.RoundTripperWrapper:
tryCancelRequest(rt.WrappedRoundTripper(), req)
default:
klog.Warningf("Unable to cancel request for %T", rt)
}
}