diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 91b48b2a..fe9217b7 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -348,7 +348,7 @@ }, { "ImportPath": "k8s.io/api", - "Rev": "0171b7c15da1" + "Rev": "7f9008e52f64" }, { "ImportPath": "k8s.io/apimachinery", diff --git a/go.mod b/go.mod index d27cc68d..0cd0e908 100644 --- a/go.mod +++ b/go.mod @@ -28,7 +28,7 @@ require ( golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 google.golang.org/appengine v1.5.0 // indirect - k8s.io/api v0.0.0-20191109101513-0171b7c15da1 + k8s.io/api v0.0.0-20191112020540-7f9008e52f64 k8s.io/apimachinery v0.0.0-20191111054156-6eb29fdf75dc k8s.io/klog v1.0.0 k8s.io/utils v0.0.0-20191030222137-2b95a09bc58d @@ -38,6 +38,6 @@ require ( replace ( golang.org/x/sys => golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a golang.org/x/tools => golang.org/x/tools v0.0.0-20190821162956-65e3620a7ae7 - k8s.io/api => k8s.io/api v0.0.0-20191109101513-0171b7c15da1 + k8s.io/api => k8s.io/api v0.0.0-20191112020540-7f9008e52f64 k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20191111054156-6eb29fdf75dc ) diff --git a/go.sum b/go.sum index 321c4660..a1af542b 100644 --- a/go.sum +++ b/go.sum @@ -191,7 +191,7 @@ gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -k8s.io/api v0.0.0-20191109101513-0171b7c15da1/go.mod h1:VJq7+38rpM4TSUbRiZX4P5UVAKK2UQpNQLZClkFQkpE= +k8s.io/api v0.0.0-20191112020540-7f9008e52f64/go.mod h1:8svLRMiLwQReMTycutfjsaQ0ackWIf8HCT4UcixYLjI= k8s.io/apimachinery v0.0.0-20191111054156-6eb29fdf75dc/go.mod h1:+6CX7hP4aLfX2sb91JYDMIp0VqDSog2kZu0BHe+lP+s= 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= diff --git a/plugin/pkg/client/auth/exec/exec.go b/plugin/pkg/client/auth/exec/exec.go index b88902c1..741729bb 100644 --- a/plugin/pkg/client/auth/exec/exec.go +++ b/plugin/pkg/client/auth/exec/exec.go @@ -48,6 +48,7 @@ import ( ) const execInfoEnv = "KUBERNETES_EXEC_INFO" +const onRotateListWarningLength = 1000 var scheme = runtime.NewScheme() var codecs = serializer.NewCodecFactory(scheme) @@ -164,7 +165,7 @@ type Authenticator struct { cachedCreds *credentials exp time.Time - onRotate func() + onRotateList []func() } type credentials struct { @@ -191,7 +192,15 @@ func (a *Authenticator) UpdateTransportConfig(c *transport.Config) error { dial = (&net.Dialer{Timeout: 30 * time.Second, KeepAlive: 30 * time.Second}).DialContext } d := connrotation.NewDialer(dial) - a.onRotate = d.CloseAll + + a.mu.Lock() + defer a.mu.Unlock() + a.onRotateList = append(a.onRotateList, d.CloseAll) + onRotateListLength := len(a.onRotateList) + if onRotateListLength > onRotateListWarningLength { + klog.Warningf("constructing many client instances from the same exec auth config can cause performance problems during cert rotation and can exhaust available network connections; %d clients constructed calling %q", onRotateListLength, a.cmd) + } + c.Dial = d.DialContext return nil @@ -353,8 +362,10 @@ func (a *Authenticator) refreshCredsLocked(r *clientauthentication.Response) err a.cachedCreds = newCreds // Only close all connections when TLS cert rotates. Token rotation doesn't // need the extra noise. - if a.onRotate != nil && oldCreds != nil && !reflect.DeepEqual(oldCreds.cert, a.cachedCreds.cert) { - a.onRotate() + if len(a.onRotateList) > 0 && oldCreds != nil && !reflect.DeepEqual(oldCreds.cert, a.cachedCreds.cert) { + for _, onRotate := range a.onRotateList { + onRotate() + } } return nil } diff --git a/plugin/pkg/client/auth/exec/exec_test.go b/plugin/pkg/client/auth/exec/exec_test.go index e3398e82..d8f94cc0 100644 --- a/plugin/pkg/client/auth/exec/exec_test.go +++ b/plugin/pkg/client/auth/exec/exec_test.go @@ -713,6 +713,52 @@ func TestTLSCredentials(t *testing.T) { get(t, "valid TLS cert again", false) } +func TestConcurrentUpdateTransportConfig(t *testing.T) { + n := time.Now() + now := func() time.Time { return n } + + env := []string{""} + environ := func() []string { + s := make([]string, len(env)) + copy(s, env) + return s + } + + c := api.ExecConfig{ + Command: "./testdata/test-plugin.sh", + APIVersion: "client.authentication.k8s.io/v1alpha1", + } + a, err := newAuthenticator(newCache(), &c) + if err != nil { + t.Fatal(err) + } + a.environ = environ + a.now = now + a.stderr = ioutil.Discard + + stopCh := make(chan struct{}) + defer close(stopCh) + + numConcurrent := 2 + + for i := 0; i < numConcurrent; i++ { + go func() { + for { + tc := &transport.Config{} + a.UpdateTransportConfig(tc) + + select { + case <-stopCh: + return + default: + continue + } + } + }() + } + time.Sleep(2 * time.Second) +} + // genClientCert generates an x509 certificate for testing. Certificate and key // are returned in PEM encoding. func genClientCert(t *testing.T) ([]byte, []byte) {