Merge pull request #80284 from danielqsj/exec

Fix a racing issue in client-go UpdateTransportConfig

Kubernetes-commit: 48ddf3be87789c92e6824c9ce536c76d008f5c19
This commit is contained in:
Kubernetes Publisher 2019-11-12 01:10:07 -08:00
commit dac3b9c76a
5 changed files with 65 additions and 8 deletions

2
Godeps/Godeps.json generated
View File

@ -348,7 +348,7 @@
},
{
"ImportPath": "k8s.io/api",
"Rev": "0171b7c15da1"
"Rev": "7f9008e52f64"
},
{
"ImportPath": "k8s.io/apimachinery",

4
go.mod
View File

@ -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
)

2
go.sum
View File

@ -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=

View File

@ -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
}

View File

@ -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) {